Merge pull request #3500 from tonistiigi/update-containerd-v2.2.0-rc.1
vendor: update containerd to v2.2.0-rc.1
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
module github.com/docker/buildx
|
||||
|
||||
go 1.24.0
|
||||
go 1.24.3
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.4.0
|
||||
@@ -8,7 +8,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27
|
||||
github.com/compose-spec/compose-go/v2 v2.9.1
|
||||
github.com/containerd/console v1.0.5
|
||||
github.com/containerd/containerd/v2 v2.1.4
|
||||
github.com/containerd/containerd/v2 v2.2.0-rc.1
|
||||
github.com/containerd/continuity v0.4.5
|
||||
github.com/containerd/errdefs v1.0.0
|
||||
github.com/containerd/log v0.1.0
|
||||
@@ -44,30 +44,30 @@ require (
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/stretchr/testify v1.11.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0
|
||||
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250408171107-3dd17559e117
|
||||
github.com/zclconf/go-cty v1.17.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0
|
||||
go.opentelemetry.io/otel v1.35.0
|
||||
go.opentelemetry.io/otel v1.37.0
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0
|
||||
go.opentelemetry.io/otel/metric v1.35.0
|
||||
go.opentelemetry.io/otel/sdk v1.35.0
|
||||
go.opentelemetry.io/otel/trace v1.35.0
|
||||
go.opentelemetry.io/otel/metric v1.37.0
|
||||
go.opentelemetry.io/otel/sdk v1.37.0
|
||||
go.opentelemetry.io/otel/trace v1.37.0
|
||||
go.yaml.in/yaml/v3 v3.0.4
|
||||
golang.org/x/mod v0.28.0
|
||||
golang.org/x/mod v0.29.0
|
||||
golang.org/x/sync v0.17.0
|
||||
golang.org/x/sys v0.35.0
|
||||
golang.org/x/term v0.34.0
|
||||
golang.org/x/text v0.28.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463
|
||||
google.golang.org/grpc v1.73.0
|
||||
golang.org/x/sys v0.37.0
|
||||
golang.org/x/term v0.35.0
|
||||
golang.org/x/text v0.29.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b
|
||||
google.golang.org/grpc v1.76.0
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
|
||||
google.golang.org/protobuf v1.36.9
|
||||
k8s.io/api v0.32.3
|
||||
k8s.io/apimachinery v0.32.3
|
||||
k8s.io/client-go v0.32.3
|
||||
google.golang.org/protobuf v1.36.10
|
||||
k8s.io/api v0.34.1
|
||||
k8s.io/apimachinery v0.34.1
|
||||
k8s.io/client-go v0.34.1
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -88,7 +88,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
|
||||
github.com/aws/smithy-go v1.20.3 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/containerd/containerd/api v1.9.0 // indirect
|
||||
github.com/containerd/containerd/api v1.10.0-rc.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/ttrpc v1.2.7 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.2.3 // indirect
|
||||
@@ -96,11 +96,11 @@ require (
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.3 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fvbommel/sortorder v1.0.1 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
@@ -108,17 +108,16 @@ require (
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // 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/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/compress v1.18.1 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
@@ -133,7 +132,7 @@ require (
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
@@ -156,23 +155,25 @@ require (
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
golang.org/x/crypto v0.41.0 // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
golang.org/x/oauth2 v0.29.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/net v0.44.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
golang.org/x/tools v0.37.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
)
|
||||
|
||||
exclude (
|
||||
|
||||
@@ -7,8 +7,8 @@ github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA=
|
||||
github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok=
|
||||
github.com/Microsoft/hcsshim v0.14.0-rc.1 h1:qAPXKwGOkVn8LlqgBN8GS0bxZ83hOJpcjxzmlQKxKsQ=
|
||||
github.com/Microsoft/hcsshim v0.14.0-rc.1/go.mod h1:hTKFGbnDtQb1wHiOWv4v0eN+7boSWAHyK/tNAaYZL0c=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
|
||||
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
@@ -64,14 +64,14 @@ github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUo
|
||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
||||
github.com/compose-spec/compose-go/v2 v2.9.1 h1:8UwI+ujNU+9Ffkf/YgAm/qM9/eU7Jn8nHzWG721W4rs=
|
||||
github.com/compose-spec/compose-go/v2 v2.9.1/go.mod h1:Oky9AZGTRB4E+0VbTPZTUu4Kp+oEMMuwZXZtPPVT1iE=
|
||||
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
|
||||
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
|
||||
github.com/containerd/cgroups/v3 v3.1.0 h1:azxYVj+91ZgSnIBp2eI3k9y2iYQSR/ZQIgh9vKO+HSY=
|
||||
github.com/containerd/cgroups/v3 v3.1.0/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
|
||||
github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
|
||||
github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0=
|
||||
github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI=
|
||||
github.com/containerd/containerd/v2 v2.1.4 h1:/hXWjiSFd6ftrBOBGfAZ6T30LJcx1dBjdKEeI8xucKQ=
|
||||
github.com/containerd/containerd/v2 v2.1.4/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM=
|
||||
github.com/containerd/containerd/api v1.10.0-rc.0 h1:PEaPRT4atfXLlbr3HaZH/i7/2PZK/+sUnp210HkhXmY=
|
||||
github.com/containerd/containerd/api v1.10.0-rc.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI=
|
||||
github.com/containerd/containerd/v2 v2.2.0-rc.1 h1:806y9qsFiZkwl90DJhtAtedG43OcmGd57sJCFyvfxpo=
|
||||
github.com/containerd/containerd/v2 v2.2.0-rc.1/go.mod h1:X7H17UATRidzfNzb5vS/l30qEJk0ktoTEU1thMh0Nbk=
|
||||
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
|
||||
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
@@ -132,19 +132,19 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
||||
github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
|
||||
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/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
|
||||
github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE=
|
||||
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
@@ -168,23 +168,20 @@ github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
|
||||
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/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||
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.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
|
||||
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-dap v0.12.0 h1:rVcjv3SyMIrpaOoTAdFDyHs99CwVOItIJGKLQFQhNeM=
|
||||
github.com/google/go-dap v0.12.0/go.mod h1:tNjCASCm5cqePi/RVXXWEVqtnNLV1KTWtYOqu6rZNzc=
|
||||
github.com/google/gofuzz v1.0.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/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
@@ -193,8 +190,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U=
|
||||
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
@@ -225,8 +222,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
||||
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||
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.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
|
||||
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
|
||||
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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
@@ -284,8 +281,9 @@ github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFL
|
||||
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 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
@@ -325,17 +323,17 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1
|
||||
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/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
@@ -371,6 +369,8 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
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=
|
||||
@@ -379,8 +379,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8=
|
||||
github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
|
||||
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4=
|
||||
@@ -417,8 +417,8 @@ go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0 h1:0NIXxOCFx+SKbhCVxwl3ETG8ClLPAa0KuKV6p3yhxP8=
|
||||
@@ -431,18 +431,20 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qH
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 h1:UGZ1QwZWY67Z6BmckTU+9Rxn04m2bD3gD6Mk0OIOCPk=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0/go.mod h1:fcwWuDuaObkkChiDlhEpSq9+X1C0omv+s5mBtToAQ64=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@@ -451,21 +453,21 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
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.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
|
||||
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/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=
|
||||
@@ -482,38 +484,40 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
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=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1:hE3bRWtU6uceqlh4fhrSnUyjKHMKB9KrTLLG+bc0ddM=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b h1:ULiyYQ0FdsJhwwZUwbaXpZF5yUE3h+RA+gxvBu37ucc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:oDOGiMSXHL4sDTJvFvIB9nRQCGdLP1o/iVaqQK8zB+M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b h1:zPKJod4w6F1+nRGDI9ubnXYhU9NSWoFAijkHkUXeTK8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
|
||||
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -536,21 +540,23 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
|
||||
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
|
||||
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
|
||||
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
|
||||
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
|
||||
k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM=
|
||||
k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk=
|
||||
k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
|
||||
k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
|
||||
k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY=
|
||||
k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
|
||||
+5
@@ -170,6 +170,11 @@ type Syncer interface {
|
||||
Sync() error
|
||||
}
|
||||
|
||||
// ReferrersProvider handles looking up additional referrer objects for a given descriptor.
|
||||
type ReferrersProvider interface {
|
||||
Referrers(context.Context, ocispec.Descriptor) ([]ocispec.Descriptor, error)
|
||||
}
|
||||
|
||||
// Opt is used to alter the mutable properties of content
|
||||
type Opt func(*Info) error
|
||||
|
||||
|
||||
+6
@@ -20,4 +20,10 @@ const (
|
||||
// AnnotationImageName is an annotation on a Descriptor in an index.json
|
||||
// containing the `Name` value as used by an `Image` struct
|
||||
AnnotationImageName = "io.containerd.image.name"
|
||||
|
||||
// AnnotationManifestSubject is an annotation on a Descriptor that means
|
||||
// that current descriptor is a referrer to the subject manifest.
|
||||
// If descriptor in image.json has this annotation, it will not create
|
||||
// a new image.
|
||||
AnnotationManifestSubject = "io.containerd.manifest.subject"
|
||||
)
|
||||
|
||||
+77
-6
@@ -23,6 +23,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@@ -43,6 +44,7 @@ type exportOptions struct {
|
||||
allPlatforms bool
|
||||
skipDockerManifest bool
|
||||
blobRecordOptions blobRecordOptions
|
||||
referrers content.ReferrersProvider
|
||||
}
|
||||
|
||||
// ExportOpt defines options for configuring exported descriptors
|
||||
@@ -143,6 +145,15 @@ func WithSkipNonDistributableBlobs() ExportOpt {
|
||||
return WithBlobFilter(f)
|
||||
}
|
||||
|
||||
// WithReferrersProvider sets a referrers provider to resolve referrer objects
|
||||
// that are included in the exported archive.
|
||||
func WithReferrersProvider(p content.ReferrersProvider) ExportOpt {
|
||||
return func(ctx context.Context, o *exportOptions) error {
|
||||
o.referrers = p
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSkipMissing excludes blobs referenced by manifests if not all blobs
|
||||
// would be included in the archive.
|
||||
// The manifest itself is excluded only if it's not present locally.
|
||||
@@ -242,7 +253,7 @@ func Export(ctx context.Context, store content.InfoReaderProvider, writer io.Wri
|
||||
mt, ok := dManifests[desc.Digest]
|
||||
if !ok {
|
||||
// TODO(containerd): Skip if already added
|
||||
r, err := getRecords(ctx, store, desc, algorithms, &eo.blobRecordOptions)
|
||||
r, err := getRecords(ctx, store, desc, algorithms, &eo.blobRecordOptions, eo.referrers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -286,7 +297,7 @@ func Export(ctx context.Context, store content.InfoReaderProvider, writer io.Wri
|
||||
}
|
||||
}
|
||||
|
||||
r, err := getRecords(ctx, store, m, algorithms, &eo.blobRecordOptions)
|
||||
r, err := getRecords(ctx, store, m, algorithms, &eo.blobRecordOptions, eo.referrers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -327,7 +338,7 @@ func Export(ctx context.Context, store content.InfoReaderProvider, writer io.Wri
|
||||
}
|
||||
}
|
||||
|
||||
records = append(records, ociIndexRecord(manifests))
|
||||
records = append(records, ociIndexRecord(slices.Concat(manifests, filterReferrers(records))))
|
||||
|
||||
if !eo.skipDockerManifest && len(dManifests) > 0 {
|
||||
tr, err := manifestsRecord(ctx, store, dManifests)
|
||||
@@ -350,7 +361,7 @@ func Export(ctx context.Context, store content.InfoReaderProvider, writer io.Wri
|
||||
return writeTar(ctx, tw, records)
|
||||
}
|
||||
|
||||
func getRecords(ctx context.Context, store content.Provider, desc ocispec.Descriptor, algorithms map[string]struct{}, brOpts *blobRecordOptions) ([]tarRecord, error) {
|
||||
func getRecords(ctx context.Context, store content.Provider, desc ocispec.Descriptor, algorithms map[string]struct{}, brOpts *blobRecordOptions, referrers content.ReferrersProvider) ([]tarRecord, error) {
|
||||
var records []tarRecord
|
||||
exportHandler := func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
|
||||
if err := desc.Digest.Validate(); err != nil {
|
||||
@@ -366,6 +377,36 @@ func getRecords(ctx context.Context, store content.Provider, desc ocispec.Descri
|
||||
childrenHandler = images.ChildrenHandler(store)
|
||||
}
|
||||
|
||||
if referrers != nil {
|
||||
ch := childrenHandler
|
||||
childrenHandler = func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
|
||||
children, err := ch(ctx, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !images.IsManifestType(desc.MediaType) && !images.IsIndexType(desc.MediaType) {
|
||||
return children, nil
|
||||
}
|
||||
|
||||
refs, err := referrers.Referrers(ctx, desc)
|
||||
if err != nil {
|
||||
if errdefs.IsNotFound(err) {
|
||||
return children, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
for _, r := range refs {
|
||||
if r.Annotations == nil {
|
||||
r.Annotations = map[string]string{}
|
||||
}
|
||||
r.Annotations[images.AnnotationManifestSubject] = desc.Digest.String()
|
||||
children = append(children, r)
|
||||
}
|
||||
|
||||
return children, nil
|
||||
}
|
||||
}
|
||||
|
||||
handlers := images.Handlers(
|
||||
childrenHandler,
|
||||
images.HandlerFunc(exportHandler),
|
||||
@@ -380,9 +421,38 @@ func getRecords(ctx context.Context, store content.Provider, desc ocispec.Descri
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// filterReferrers filters records with referrer annotations that are not
|
||||
// exported in another way already
|
||||
func filterReferrers(records []tarRecord) []ocispec.Descriptor {
|
||||
inTree := map[digest.Digest]struct{}{}
|
||||
referrersMap := map[digest.Digest]ocispec.Descriptor{}
|
||||
for _, r := range records {
|
||||
if r.Descriptor != nil {
|
||||
if _, ok := r.Descriptor.Annotations[images.AnnotationManifestSubject]; ok {
|
||||
referrersMap[r.Descriptor.Digest] = *r.Descriptor
|
||||
} else {
|
||||
inTree[r.Descriptor.Digest] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var referrers []ocispec.Descriptor
|
||||
for dgst, desc := range referrersMap {
|
||||
if _, ok := inTree[dgst]; ok {
|
||||
continue
|
||||
}
|
||||
referrers = append(referrers, desc)
|
||||
}
|
||||
slices.SortFunc(referrers, func(a, b ocispec.Descriptor) int {
|
||||
return strings.Compare(a.Digest.String(), b.Digest.String())
|
||||
})
|
||||
return referrers
|
||||
}
|
||||
|
||||
type tarRecord struct {
|
||||
Header *tar.Header
|
||||
CopyTo func(context.Context, io.Writer) (int64, error)
|
||||
Header *tar.Header
|
||||
CopyTo func(context.Context, io.Writer) (int64, error)
|
||||
Descriptor *ocispec.Descriptor
|
||||
}
|
||||
|
||||
type blobRecordOptions struct {
|
||||
@@ -395,6 +465,7 @@ func blobRecord(cs content.Provider, desc ocispec.Descriptor, opts *blobRecordOp
|
||||
return tarRecord{}
|
||||
}
|
||||
return tarRecord{
|
||||
Descriptor: &desc,
|
||||
Header: &tar.Header{
|
||||
Name: path.Join(ocispec.ImageBlobsDir, desc.Digest.Algorithm().String(), desc.Digest.Encoded()),
|
||||
Mode: 0444,
|
||||
|
||||
+4
-3
@@ -104,15 +104,16 @@ func ImportIndex(ctx context.Context, store content.Store, reader io.Reader, opt
|
||||
}
|
||||
|
||||
hdrName := path.Clean(hdr.Name)
|
||||
if hdrName == ocispec.ImageLayoutFile {
|
||||
switch hdrName {
|
||||
case ocispec.ImageLayoutFile:
|
||||
if err = onUntarJSON(tr, &ociLayout); err != nil {
|
||||
return ocispec.Descriptor{}, fmt.Errorf("untar oci layout %q: %w", hdr.Name, err)
|
||||
}
|
||||
} else if hdrName == "manifest.json" {
|
||||
case "manifest.json":
|
||||
if err = onUntarJSON(tr, &mfsts); err != nil {
|
||||
return ocispec.Descriptor{}, fmt.Errorf("untar manifest %q: %w", hdr.Name, err)
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
dgst, err := onUntarBlob(ctx, tr, store, hdr.Size, "tar-"+hdrName)
|
||||
if err != nil {
|
||||
return ocispec.Descriptor{}, fmt.Errorf("failed to ingest %q: %w", hdr.Name, err)
|
||||
|
||||
+34
-1
@@ -20,7 +20,9 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/content"
|
||||
"github.com/containerd/errdefs"
|
||||
@@ -198,6 +200,35 @@ func ChildrenHandler(provider content.Provider) HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// SetReferrers is a handler wrapper which adds referrer descriptors
|
||||
// from the provided ReferrersProvider and adds them to the children
|
||||
// returned by the handler. The referrers will have a container-specific
|
||||
// annotation added to indicate the subject descriptor.
|
||||
func SetReferrers(refProvider content.ReferrersProvider, f HandlerFunc) HandlerFunc {
|
||||
return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
|
||||
children, err := f(ctx, desc)
|
||||
if err != nil {
|
||||
return children, err
|
||||
}
|
||||
if !IsManifestType(desc.MediaType) && !IsIndexType(desc.MediaType) {
|
||||
return children, nil
|
||||
}
|
||||
refs, err := refProvider.Referrers(ctx, desc)
|
||||
if err != nil {
|
||||
return children, err
|
||||
}
|
||||
children = slices.Grow(children, len(refs))
|
||||
for _, ref := range refs {
|
||||
if ref.Annotations == nil {
|
||||
ref.Annotations = map[string]string{}
|
||||
}
|
||||
ref.Annotations[AnnotationManifestSubject] = desc.Digest.String()
|
||||
children = append(children, ref)
|
||||
}
|
||||
return children, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SetChildrenLabels is a handler wrapper which sets labels for the content on
|
||||
// the children returned by the handler and passes through the children.
|
||||
// Must follow a handler that returns the children to be labeled.
|
||||
@@ -235,7 +266,9 @@ func SetChildrenMappedLabels(manager content.Manager, f HandlerFunc, labelMap fu
|
||||
for _, key := range labelKeys {
|
||||
idx := keys[key]
|
||||
keys[key] = idx + 1
|
||||
if idx > 0 || key[len(key)-1] == '.' {
|
||||
if strings.HasSuffix(key, ".sha256.") {
|
||||
key = fmt.Sprintf("%s%s", key, ch.Digest.Hex()[:12])
|
||||
} else if idx > 0 || key[len(key)-1] == '.' {
|
||||
key = fmt.Sprintf("%s%d", key, idx)
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -208,6 +208,9 @@ func IsAttestationType(mt string) bool {
|
||||
|
||||
// ChildGCLabels returns the label for a given descriptor to reference it
|
||||
func ChildGCLabels(desc ocispec.Descriptor) []string {
|
||||
if _, ok := desc.Annotations[AnnotationManifestSubject]; ok {
|
||||
return []string{"containerd.io/gc.ref.content.referrer.sha256."}
|
||||
}
|
||||
mt := desc.MediaType
|
||||
if IsKnownConfig(mt) {
|
||||
return []string{"containerd.io/gc.ref.content.config"}
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ func (ec ErrorCode) ErrorCode() ErrorCode {
|
||||
// Error returns the ID/Value
|
||||
func (ec ErrorCode) Error() string {
|
||||
// NOTE(stevvooe): Cannot use message here since it may have unpopulated args.
|
||||
return strings.ToLower(strings.Replace(ec.String(), "_", " ", -1))
|
||||
return strings.ToLower(strings.ReplaceAll(ec.String(), "_", " "))
|
||||
}
|
||||
|
||||
// Descriptor returns the descriptor for the error code.
|
||||
|
||||
+17
-15
@@ -26,7 +26,6 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -260,7 +259,7 @@ func (r dockerFetcher) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.R
|
||||
req.path = req.path + "?" + u.RawQuery
|
||||
}
|
||||
|
||||
rc, err := r.open(ctx, req, desc.MediaType, offset, false)
|
||||
rc, _, err := r.open(ctx, req, desc.MediaType, offset, false)
|
||||
if err != nil {
|
||||
if errdefs.IsNotFound(err) {
|
||||
continue // try one of the other urls.
|
||||
@@ -282,7 +281,7 @@ func (r dockerFetcher) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.R
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rc, err := r.open(ctx, req, desc.MediaType, offset, i == len(r.hosts)-1)
|
||||
rc, _, err := r.open(ctx, req, desc.MediaType, offset, i == len(r.hosts)-1)
|
||||
if err != nil {
|
||||
// Store the error for referencing later
|
||||
if firstErr == nil {
|
||||
@@ -305,7 +304,7 @@ func (r dockerFetcher) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.R
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rc, err := r.open(ctx, req, desc.MediaType, offset, i == len(r.hosts)-1)
|
||||
rc, _, err := r.open(ctx, req, desc.MediaType, offset, i == len(r.hosts)-1)
|
||||
if err != nil {
|
||||
// Store the error for referencing later
|
||||
if firstErr == nil {
|
||||
@@ -419,8 +418,9 @@ func (r dockerFetcher) FetchByDigest(ctx context.Context, dgst digest.Digest, op
|
||||
return nil, desc, firstErr
|
||||
}
|
||||
|
||||
seeker, err := newHTTPReadSeeker(sz, func(offset int64) (io.ReadCloser, error) {
|
||||
return r.open(ctx, getReq, config.Mediatype, offset, true)
|
||||
seeker, err := newHTTPReadSeeker(sz, func(offset int64) (rc io.ReadCloser, err error) {
|
||||
rc, _, err = r.open(ctx, getReq, config.Mediatype, offset, true)
|
||||
return
|
||||
})
|
||||
if err != nil {
|
||||
return nil, desc, err
|
||||
@@ -437,7 +437,7 @@ func (r dockerFetcher) FetchByDigest(ctx context.Context, dgst digest.Digest, op
|
||||
return seeker, desc, nil
|
||||
}
|
||||
|
||||
func (r dockerFetcher) open(ctx context.Context, req *request, mediatype string, offset int64, lastHost bool) (_ io.ReadCloser, retErr error) {
|
||||
func (r dockerFetcher) open(ctx context.Context, req *request, mediatype string, offset int64, lastHost bool) (_ io.ReadCloser, _ int64, retErr error) {
|
||||
const minChunkSize = 512
|
||||
|
||||
chunkSize := int64(r.performances.ConcurrentLayerFetchBuffer)
|
||||
@@ -457,21 +457,24 @@ func (r dockerFetcher) open(ctx context.Context, req *request, mediatype string,
|
||||
}
|
||||
|
||||
if err := r.Acquire(ctx, 1); err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
resp, err := req.doWithRetries(ctx, lastHost, withErrorCheck, withOffsetCheck(offset))
|
||||
var remaining int64
|
||||
resp, err := req.doWithRetries(ctx, lastHost, withErrorCheck, withOffsetCheck(offset, parallelism))
|
||||
switch err {
|
||||
case nil:
|
||||
// all good
|
||||
remaining = resp.ContentLength
|
||||
case errContentRangeIgnored:
|
||||
if parallelism != 1 {
|
||||
log.G(ctx).WithError(err).Info("remote host ignored content range, forcing parallelism to 1")
|
||||
parallelism = 1
|
||||
}
|
||||
remaining = resp.ContentLength - offset
|
||||
default:
|
||||
log.G(ctx).WithError(err).Debug("fetch failed")
|
||||
r.Release(1)
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
body := &fnOnClose{
|
||||
@@ -490,7 +493,6 @@ func (r dockerFetcher) open(ctx context.Context, req *request, mediatype string,
|
||||
return r == ' ' || r == '\t' || r == ','
|
||||
})
|
||||
|
||||
remaining, _ := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 0)
|
||||
if remaining <= chunkSize {
|
||||
parallelism = 1
|
||||
}
|
||||
@@ -589,13 +591,13 @@ func (r dockerFetcher) open(ctx context.Context, req *request, mediatype string,
|
||||
zstd.WithDecoderLowmem(false),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
body.ReadCloser = r.IOReadCloser()
|
||||
case "gzip":
|
||||
r, err := gzip.NewReader(body.ReadCloser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
body.ReadCloser = r
|
||||
case "deflate":
|
||||
@@ -603,11 +605,11 @@ func (r dockerFetcher) open(ctx context.Context, req *request, mediatype string,
|
||||
case "identity", "":
|
||||
// no content-encoding applied, use raw body
|
||||
default:
|
||||
return nil, errors.New("unsupported Content-Encoding algorithm: " + algorithm)
|
||||
return nil, 0, errors.New("unsupported Content-Encoding algorithm: " + algorithm)
|
||||
}
|
||||
}
|
||||
|
||||
return body, nil
|
||||
return body, remaining, nil
|
||||
}
|
||||
|
||||
type fnOnClose struct {
|
||||
|
||||
+33
-2
@@ -59,7 +59,8 @@ func (hrs *httpReadSeeker) Read(p []byte) (n int, err error) {
|
||||
if n > 0 || err == nil {
|
||||
hrs.errsWithNoProgress = 0
|
||||
}
|
||||
if err == io.ErrUnexpectedEOF {
|
||||
switch err {
|
||||
case io.ErrUnexpectedEOF:
|
||||
// connection closed unexpectedly. try reconnecting.
|
||||
if n == 0 {
|
||||
hrs.errsWithNoProgress++
|
||||
@@ -76,7 +77,7 @@ func (hrs *httpReadSeeker) Read(p []byte) (n int, err error) {
|
||||
if _, err2 := hrs.reader(); err2 == nil {
|
||||
return n, nil
|
||||
}
|
||||
} else if err == io.EOF {
|
||||
case io.EOF:
|
||||
// The CRI's imagePullProgressTimeout relies on responseBody.Close to
|
||||
// update the process monitor's status. If the err is io.EOF, close
|
||||
// the connection since there is no more available data.
|
||||
@@ -102,6 +103,36 @@ func (hrs *httpReadSeeker) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hrs *httpReadSeeker) ReadAt(p []byte, offset int64) (n int, err error) {
|
||||
if hrs.closed {
|
||||
return 0, fmt.Errorf("httpReadSeeker.ReadAt: closed: %w", errdefs.ErrUnavailable)
|
||||
}
|
||||
|
||||
if offset < 0 {
|
||||
return 0, fmt.Errorf("httpReadSeeker.ReadAt: negative offset: %w", errdefs.ErrInvalidArgument)
|
||||
}
|
||||
|
||||
if hrs.size != -1 && offset >= hrs.size {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if hrs.open == nil {
|
||||
return 0, fmt.Errorf("httpReadSeeker.ReadAt: cannot open: %w", errdefs.ErrNotImplemented)
|
||||
}
|
||||
|
||||
rc, err := hrs.open(offset)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("httpReadSeeker.ReadAt: failed to open at offset %d: %w", offset, err)
|
||||
}
|
||||
defer func() {
|
||||
if closeErr := rc.Close(); closeErr != nil {
|
||||
log.L.WithError(closeErr).Error("httpReadSeeker.ReadAt: failed to close ReadCloser")
|
||||
}
|
||||
}()
|
||||
|
||||
return io.ReadFull(rc, p)
|
||||
}
|
||||
|
||||
func (hrs *httpReadSeeker) Seek(offset int64, whence int) (int64, error) {
|
||||
if hrs.closed {
|
||||
return 0, fmt.Errorf("Fetcher.Seek: closed: %w", errdefs.ErrUnavailable)
|
||||
|
||||
+12
-6
@@ -526,15 +526,21 @@ func (pw *pushWriter) Commit(ctx context.Context, size int64, expected digest.Di
|
||||
|
||||
if expected == "" {
|
||||
expected = status.Expected
|
||||
} else if expected != status.Expected {
|
||||
return fmt.Errorf("unexpected digest received: got %q, expected %q", status.Expected, expected)
|
||||
}
|
||||
|
||||
actual, err := digest.Parse(resp.Header.Get("Docker-Content-Digest"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid content digest in response: %w", err)
|
||||
}
|
||||
if dgstHdr := resp.Header.Get("Docker-Content-Digest"); dgstHdr != "" {
|
||||
actual, err := digest.Parse(dgstHdr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid content digest in response: %w", err)
|
||||
}
|
||||
|
||||
if actual != expected {
|
||||
return fmt.Errorf("got digest %s, expected %s", actual, expected)
|
||||
if actual != expected {
|
||||
return fmt.Errorf("got digest %s, expected %s", actual, expected)
|
||||
}
|
||||
} else {
|
||||
log.G(ctx).Info("registry did not send a Docker-Content-Digest header")
|
||||
}
|
||||
|
||||
status.Committed = true
|
||||
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
Copyright The containerd 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 docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/remotes"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/containerd/log"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
func (r dockerFetcher) FetchReferrers(ctx context.Context, dgst digest.Digest, opts ...remotes.FetchReferrersOpt) ([]ocispec.Descriptor, error) {
|
||||
var config remotes.FetchReferrersConfig
|
||||
for _, opt := range opts {
|
||||
opt(ctx, &config)
|
||||
}
|
||||
rc, size, err := r.openReferrers(ctx, dgst, config)
|
||||
if err != nil {
|
||||
if errdefs.IsNotFound(err) {
|
||||
return []ocispec.Descriptor{}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer rc.Close()
|
||||
if size < 0 {
|
||||
size = MaxManifestSize
|
||||
} else if size > MaxManifestSize {
|
||||
return nil, fmt.Errorf("referrers index size %d exceeds maximum allowed %d: %w", size, MaxManifestSize, errdefs.ErrNotFound)
|
||||
}
|
||||
|
||||
var index ocispec.Index
|
||||
dec := json.NewDecoder(io.LimitReader(rc, size))
|
||||
if err := dec.Decode(&index); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode referrers index: %w", err)
|
||||
}
|
||||
if _, err := dec.Token(); !errors.Is(err, io.EOF) {
|
||||
return nil, fmt.Errorf("unexpected data after JSON object")
|
||||
}
|
||||
|
||||
if len(config.ArtifactTypes) == 0 {
|
||||
return index.Manifests, nil
|
||||
}
|
||||
|
||||
var referrers []ocispec.Descriptor
|
||||
tFilter := map[string]struct{}{}
|
||||
for _, t := range config.ArtifactTypes {
|
||||
tFilter[t] = struct{}{}
|
||||
}
|
||||
for _, desc := range index.Manifests {
|
||||
if _, ok := tFilter[desc.ArtifactType]; ok {
|
||||
referrers = append(referrers, desc)
|
||||
}
|
||||
}
|
||||
return referrers, nil
|
||||
}
|
||||
|
||||
func (r dockerFetcher) openReferrers(ctx context.Context, dgst digest.Digest, config remotes.FetchReferrersConfig) (io.ReadCloser, int64, error) {
|
||||
mediaType := ocispec.MediaTypeImageIndex
|
||||
ctx = log.WithLogger(ctx, log.G(ctx).WithField("digest", dgst))
|
||||
|
||||
hosts := r.filterHosts(HostCapabilityReferrers)
|
||||
var fallbackHosts []RegistryHost
|
||||
if len(hosts) == 0 {
|
||||
fallbackHosts = r.filterHosts(HostCapabilityResolve)
|
||||
if len(fallbackHosts) == 0 {
|
||||
return nil, 0, fmt.Errorf("no referrers hosts: %w", errdefs.ErrNotFound)
|
||||
}
|
||||
} else {
|
||||
// If referrers are defined, use same hosts for fallback
|
||||
fallbackHosts = hosts
|
||||
}
|
||||
|
||||
ctx, err := ContextWithRepositoryScope(ctx, r.refspec, false)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var firstErr error
|
||||
for i, host := range hosts {
|
||||
req := r.request(host, http.MethodGet, "referrers", dgst.String())
|
||||
for _, artifactType := range config.ArtifactTypes {
|
||||
if err := req.addQuery("artifactType", artifactType); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
for k, vs := range config.QueryFilters {
|
||||
for _, v := range vs {
|
||||
if err := req.addQuery(k, v); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := req.addNamespace(r.refspec.Hostname()); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
rc, cl, err := r.open(ctx, req, mediaType, 0, i == len(hosts)-1)
|
||||
if err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
log.G(ctx).WithError(err).WithField("host", host.Host).Debug("error fetching referrers")
|
||||
if firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return rc, cl, nil
|
||||
}
|
||||
}
|
||||
|
||||
for i, host := range fallbackHosts {
|
||||
req := r.request(host, http.MethodGet, "manifests", strings.Replace(dgst.String(), ":", "-", 1))
|
||||
if err := req.addNamespace(r.refspec.Hostname()); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
rc, cl, err := r.open(ctx, req, mediaType, 0, i == len(fallbackHosts)-1)
|
||||
if err != nil {
|
||||
if errdefs.IsNotFound(err) {
|
||||
// Equivalent to empty referrers list
|
||||
firstErr = err
|
||||
break
|
||||
}
|
||||
log.G(ctx).WithError(err).WithField("host", host.Host).Debug("error fetching referrers via fallback")
|
||||
if firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
} else {
|
||||
return rc, cl, nil
|
||||
}
|
||||
}
|
||||
if firstErr == nil {
|
||||
firstErr = fmt.Errorf("could not be found at any host: %w", errdefs.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil, 0, firstErr
|
||||
}
|
||||
+8
-4
@@ -57,6 +57,10 @@ const (
|
||||
// manifests
|
||||
HostCapabilityPush
|
||||
|
||||
// HostCapabilityReferrers represents the capability to generate a
|
||||
// list of referrers using the OCI Distribution referrers endpoint.
|
||||
HostCapabilityReferrers
|
||||
|
||||
// Reserved for future capabilities (i.e. search, catalog, remove)
|
||||
)
|
||||
|
||||
@@ -168,7 +172,7 @@ func ConfigureDefaultRegistries(ropts ...RegistryOpt) RegistryHosts {
|
||||
Host: host,
|
||||
Scheme: "https",
|
||||
Path: "/v2",
|
||||
Capabilities: HostCapabilityPull | HostCapabilityResolve | HostCapabilityPush,
|
||||
Capabilities: HostCapabilityPull | HostCapabilityResolve | HostCapabilityPush | HostCapabilityReferrers,
|
||||
}
|
||||
|
||||
if config.Client == nil {
|
||||
@@ -212,10 +216,10 @@ func MatchAllHosts(string) (bool, error) {
|
||||
// Note: this does not handle matching of ip addresses in octal,
|
||||
// decimal or hex form.
|
||||
func MatchLocalhost(host string) (bool, error) {
|
||||
switch {
|
||||
case host == "::1":
|
||||
switch host {
|
||||
case "::1":
|
||||
return true, nil
|
||||
case host == "[::1]":
|
||||
case "[::1]":
|
||||
return true, nil
|
||||
}
|
||||
h, p, err := net.SplitHostPort(host)
|
||||
|
||||
+14
-10
@@ -551,29 +551,33 @@ func (r *request) authorize(ctx context.Context, req *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *request) addNamespace(ns string) (err error) {
|
||||
if !r.host.isProxy(ns) {
|
||||
return nil
|
||||
}
|
||||
func (r *request) addQuery(key, value string) (err error) {
|
||||
var q url.Values
|
||||
// Parse query
|
||||
if i := strings.IndexByte(r.path, '?'); i > 0 {
|
||||
r.path = r.path[:i+1]
|
||||
q, err = url.ParseQuery(r.path[i+1:])
|
||||
if p, query, ok := strings.Cut(r.path, "?"); ok {
|
||||
q, err = url.ParseQuery(query)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r.path = p + "?"
|
||||
} else {
|
||||
r.path = r.path + "?"
|
||||
q = url.Values{}
|
||||
}
|
||||
q.Add("ns", ns)
|
||||
q.Add(key, value)
|
||||
|
||||
r.path = r.path + q.Encode()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *request) addNamespace(ns string) error {
|
||||
if !r.host.isProxy(ns) {
|
||||
return nil
|
||||
}
|
||||
return r.addQuery("ns", ns)
|
||||
}
|
||||
|
||||
type request struct {
|
||||
method string
|
||||
path string
|
||||
@@ -659,9 +663,9 @@ func withErrorCheck(r *request, resp *http.Response) error {
|
||||
|
||||
var errContentRangeIgnored = errors.New("content range requests ignored")
|
||||
|
||||
func withOffsetCheck(offset int64) doChecks {
|
||||
func withOffsetCheck(offset, parallelism int64) doChecks {
|
||||
return func(r *request, resp *http.Response) error {
|
||||
if offset == 0 {
|
||||
if parallelism <= 1 && offset == 0 {
|
||||
return nil
|
||||
}
|
||||
if resp.StatusCode == http.StatusPartialContent {
|
||||
|
||||
+34
@@ -19,6 +19,7 @@ package remotes
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/url"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/content"
|
||||
"github.com/containerd/containerd/v2/core/transfer"
|
||||
@@ -75,6 +76,10 @@ type FetcherByDigest interface {
|
||||
FetchByDigest(ctx context.Context, dgst digest.Digest, opts ...FetchByDigestOpts) (io.ReadCloser, ocispec.Descriptor, error)
|
||||
}
|
||||
|
||||
type ReferrersFetcher interface {
|
||||
FetchReferrers(ctx context.Context, dgst digest.Digest, opts ...FetchReferrersOpt) ([]ocispec.Descriptor, error)
|
||||
}
|
||||
|
||||
// Pusher pushes content
|
||||
type Pusher interface {
|
||||
// Push returns a content writer for the given resource identified
|
||||
@@ -116,3 +121,32 @@ func WithMediaType(mediatype string) FetchByDigestOpts {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type FetchReferrersConfig struct {
|
||||
// ArtifactTypes specifies the artifact types to filter referrers, this can be
|
||||
// applied to registry queries or filtering the results after fetching.
|
||||
ArtifactTypes []string
|
||||
// QueryFilters specifies additional filters which may get sent as query parameters
|
||||
QueryFilters url.Values
|
||||
}
|
||||
|
||||
type FetchReferrersOpt func(context.Context, *FetchReferrersConfig) error
|
||||
|
||||
// WithReferrerArtifactTypes sets the artifact types to filter referrers
|
||||
func WithReferrerArtifactTypes(artifactTypes ...string) FetchReferrersOpt {
|
||||
return func(ctx context.Context, cfg *FetchReferrersConfig) error {
|
||||
cfg.ArtifactTypes = artifactTypes
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithReferrerQueryFilter sets additional query filters for referrer fetching
|
||||
func WithReferrerQueryFilter(param, value string) FetchReferrersOpt {
|
||||
return func(ctx context.Context, cfg *FetchReferrersConfig) error {
|
||||
if cfg.QueryFilters == nil {
|
||||
cfg.QueryFilters = url.Values{}
|
||||
}
|
||||
cfg.QueryFilters.Add(param, value)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -29,4 +29,9 @@ const (
|
||||
// DefaultSnapshotterNSLabel defines the namespace label to check for the
|
||||
// default snapshotter
|
||||
DefaultSnapshotterNSLabel = "containerd.io/defaults/snapshotter"
|
||||
// DefaultSandboxerNSLabel defines the namespace label to check for the
|
||||
// default sandboxcr
|
||||
DefaultSandboxerNSLabel = "containerd.io/defaults/sandboxer"
|
||||
// DefaultSandboxer defines the default sandboxer to use for creating sandboxes.
|
||||
DefaultSandboxer = "shim"
|
||||
)
|
||||
|
||||
+3
@@ -24,4 +24,7 @@ const (
|
||||
// DefaultRootDir is the default location used by containerd to store
|
||||
// persistent data
|
||||
DefaultRootDir = "/var/lib/containerd"
|
||||
|
||||
// DefaultConfigIncludePattern is the default location for drop-in configuration files.
|
||||
DefaultConfigIncludePattern = "/etc/containerd/conf.d/*.toml"
|
||||
)
|
||||
|
||||
+3
@@ -31,6 +31,9 @@ var (
|
||||
|
||||
// DefaultConfigDir is the default location for config files.
|
||||
DefaultConfigDir = filepath.Join(os.Getenv("programfiles"), "containerd")
|
||||
|
||||
// DefaultConfigIncludePattern is the default location for drop-in configuration files.
|
||||
DefaultConfigIncludePattern = filepath.Join(os.Getenv("programfiles"), "containerd\\conf.d\\*.toml")
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+3
@@ -139,6 +139,9 @@ func (w *writer) Commit(ctx context.Context, size int64, expected digest.Digest,
|
||||
return err
|
||||
}
|
||||
|
||||
if err := syncDir(filepath.Dir(target)); err != nil {
|
||||
return err
|
||||
}
|
||||
// Enable content blob integrity verification if supported
|
||||
|
||||
if w.s.integritySupported {
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
//go:build !windows
|
||||
|
||||
/*
|
||||
Copyright The containerd 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 local
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func syncDir(dir string) error {
|
||||
dirF, err := os.Open(dir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open dir %s: %w", dir, err)
|
||||
}
|
||||
err = dirF.Sync()
|
||||
dirF.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to sync dir %s: %w", dir, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Generated
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
Copyright The containerd 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 local
|
||||
|
||||
// sync dir doesn't support in windows
|
||||
func syncDir(dir string) error {
|
||||
return nil
|
||||
}
|
||||
+1
-1
@@ -24,7 +24,7 @@ var (
|
||||
Package = "github.com/containerd/containerd/v2"
|
||||
|
||||
// Version holds the complete version number. Filled in at linking time.
|
||||
Version = "2.1.4+unknown"
|
||||
Version = "2.2.0-rc+unknown"
|
||||
|
||||
// Revision is filled with the VCS (e.g. git) revision being used to build
|
||||
// the program at linking time.
|
||||
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.x
|
||||
|
||||
before_install:
|
||||
- go test -v
|
||||
|
||||
script:
|
||||
- go test -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
+25
@@ -1,5 +1,30 @@
|
||||
# Change history of go-restful
|
||||
|
||||
## [v3.13.0] - 2025-08-14
|
||||
|
||||
- optimize performance of path matching in CurlyRouter ( thanks @wenhuang, Wen Huang)
|
||||
|
||||
## [v3.12.2] - 2025-02-21
|
||||
|
||||
- allow empty payloads in post,put,patch, issue #580 ( thanks @liggitt, Jordan Liggitt)
|
||||
|
||||
## [v3.12.1] - 2024-05-28
|
||||
|
||||
- fix misroute when dealing multiple webservice with regex (#549) (thanks Haitao Chen)
|
||||
|
||||
## [v3.12.0] - 2024-03-11
|
||||
|
||||
- add Flush method #529 (#538)
|
||||
- fix: Improper handling of empty POST requests (#543)
|
||||
|
||||
## [v3.11.3] - 2024-01-09
|
||||
|
||||
- better not have 2 tags on one commit
|
||||
|
||||
## [v3.11.1, v3.11.2] - 2024-01-09
|
||||
|
||||
- fix by restoring custom JSON handler functions (Mike Beaumont #540)
|
||||
|
||||
## [v3.11.0] - 2023-08-19
|
||||
|
||||
- restored behavior as <= v3.9.0 with option to change path strategy using TrimRightSlashEnabled.
|
||||
|
||||
+3
-4
@@ -2,9 +2,8 @@ go-restful
|
||||
==========
|
||||
package for building REST-style Web Services using Google Go
|
||||
|
||||
[](https://travis-ci.org/emicklei/go-restful)
|
||||
[](https://goreportcard.com/report/github.com/emicklei/go-restful)
|
||||
[](https://pkg.go.dev/github.com/emicklei/go-restful)
|
||||
[](https://pkg.go.dev/github.com/emicklei/go-restful/v3)
|
||||
[](https://codecov.io/gh/emicklei/go-restful)
|
||||
|
||||
- [Code examples use v3](https://github.com/emicklei/go-restful/tree/v3/examples)
|
||||
@@ -85,6 +84,7 @@ func (u UserResource) findUser(request *restful.Request, response *restful.Respo
|
||||
- Configurable (trace) logging
|
||||
- Customizable gzip/deflate readers and writers using CompressorProvider registration
|
||||
- Inject your own http.Handler using the `HttpMiddlewareHandlerToFilter` function
|
||||
- Added `SetPathTokenCacheEnabled` and `SetCustomVerbCacheEnabled` to disable regexp caching (default=true)
|
||||
|
||||
## How to customize
|
||||
There are several hooks to customize the behavior of the go-restful package.
|
||||
@@ -95,8 +95,7 @@ There are several hooks to customize the behavior of the go-restful package.
|
||||
- Trace logging
|
||||
- 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 `/`
|
||||
- Use the package variable `TrimRightSlashEnabled` (default true) to control the behavior of matching routes that end with a slash `/`
|
||||
|
||||
## Resources
|
||||
|
||||
|
||||
+10
@@ -49,6 +49,16 @@ func (c *CompressingResponseWriter) CloseNotify() <-chan bool {
|
||||
return c.writer.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
// Flush is part of http.Flusher interface. Noop if the underlying writer doesn't support it.
|
||||
func (c *CompressingResponseWriter) Flush() {
|
||||
flusher, ok := c.writer.(http.Flusher)
|
||||
if !ok {
|
||||
// writer doesn't support http.Flusher interface
|
||||
return
|
||||
}
|
||||
flusher.Flush()
|
||||
}
|
||||
|
||||
// Close the underlying compressor
|
||||
func (c *CompressingResponseWriter) Close() error {
|
||||
if c.isCompressorClosed() {
|
||||
|
||||
+74
-22
@@ -9,11 +9,35 @@ import (
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// CurlyRouter expects Routes with paths that contain zero or more parameters in curly brackets.
|
||||
type CurlyRouter struct{}
|
||||
|
||||
var (
|
||||
regexCache sync.Map // Cache for compiled regex patterns
|
||||
pathTokenCacheEnabled = true // Enable/disable path token regex caching
|
||||
)
|
||||
|
||||
// SetPathTokenCacheEnabled enables or disables path token regex caching for CurlyRouter.
|
||||
// When disabled, regex patterns will be compiled on every request.
|
||||
// When enabled (default), compiled regex patterns are cached for better performance.
|
||||
func SetPathTokenCacheEnabled(enabled bool) {
|
||||
pathTokenCacheEnabled = enabled
|
||||
}
|
||||
|
||||
// getCachedRegexp retrieves a compiled regex from the cache if found and valid.
|
||||
// Returns the regex and true if found and valid, nil and false otherwise.
|
||||
func getCachedRegexp(cache *sync.Map, pattern string) (*regexp.Regexp, bool) {
|
||||
if cached, found := cache.Load(pattern); found {
|
||||
if regex, ok := cached.(*regexp.Regexp); ok {
|
||||
return regex, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// SelectRoute is part of the Router interface and returns the best match
|
||||
// for the WebService and its Route for the given Request.
|
||||
func (c CurlyRouter) SelectRoute(
|
||||
@@ -46,10 +70,10 @@ func (c CurlyRouter) SelectRoute(
|
||||
// selectRoutes return a collection of Route from a WebService that matches the path tokens from the request.
|
||||
func (c CurlyRouter) selectRoutes(ws *WebService, requestTokens []string) sortableCurlyRoutes {
|
||||
candidates := make(sortableCurlyRoutes, 0, 8)
|
||||
for _, each := range ws.routes {
|
||||
matches, paramCount, staticCount := c.matchesRouteByPathTokens(each.pathParts, requestTokens, each.hasCustomVerb)
|
||||
for _, eachRoute := range ws.routes {
|
||||
matches, paramCount, staticCount := c.matchesRouteByPathTokens(eachRoute.pathParts, requestTokens, eachRoute.hasCustomVerb)
|
||||
if matches {
|
||||
candidates.add(curlyRoute{each, paramCount, staticCount}) // TODO make sure Routes() return pointers?
|
||||
candidates.add(curlyRoute{eachRoute, paramCount, staticCount}) // TODO make sure Routes() return pointers?
|
||||
}
|
||||
}
|
||||
sort.Sort(candidates)
|
||||
@@ -72,7 +96,7 @@ func (c CurlyRouter) matchesRouteByPathTokens(routeTokens, requestTokens []strin
|
||||
return false, 0, 0
|
||||
}
|
||||
requestToken := requestTokens[i]
|
||||
if routeHasCustomVerb && hasCustomVerb(routeToken){
|
||||
if routeHasCustomVerb && hasCustomVerb(routeToken) {
|
||||
if !isMatchCustomVerb(routeToken, requestToken) {
|
||||
return false, 0, 0
|
||||
}
|
||||
@@ -113,8 +137,28 @@ func (c CurlyRouter) regularMatchesPathToken(routeToken string, colon int, reque
|
||||
}
|
||||
return true, true
|
||||
}
|
||||
matched, err := regexp.MatchString(regPart, requestToken)
|
||||
return (matched && err == nil), false
|
||||
|
||||
// Check cache first (if enabled)
|
||||
if pathTokenCacheEnabled {
|
||||
if regex, found := getCachedRegexp(®exCache, regPart); found {
|
||||
matched := regex.MatchString(requestToken)
|
||||
return matched, false
|
||||
}
|
||||
}
|
||||
|
||||
// Compile the regex
|
||||
regex, err := regexp.Compile(regPart)
|
||||
if err != nil {
|
||||
return false, false
|
||||
}
|
||||
|
||||
// Cache the regex (if enabled)
|
||||
if pathTokenCacheEnabled {
|
||||
regexCache.Store(regPart, regex)
|
||||
}
|
||||
|
||||
matched := regex.MatchString(requestToken)
|
||||
return matched, false
|
||||
}
|
||||
|
||||
var jsr311Router = RouterJSR311{}
|
||||
@@ -129,44 +173,52 @@ func (c CurlyRouter) detectRoute(candidateRoutes sortableCurlyRoutes, httpReques
|
||||
// detectWebService returns the best matching webService given the list of path tokens.
|
||||
// see also computeWebserviceScore
|
||||
func (c CurlyRouter) detectWebService(requestTokens []string, webServices []*WebService) *WebService {
|
||||
var best *WebService
|
||||
var bestWs *WebService
|
||||
score := -1
|
||||
for _, each := range webServices {
|
||||
matches, eachScore := c.computeWebserviceScore(requestTokens, each.pathExpr.tokens)
|
||||
for _, eachWS := range webServices {
|
||||
matches, eachScore := c.computeWebserviceScore(requestTokens, eachWS.pathExpr.tokens)
|
||||
if matches && (eachScore > score) {
|
||||
best = each
|
||||
bestWs = eachWS
|
||||
score = eachScore
|
||||
}
|
||||
}
|
||||
return best
|
||||
return bestWs
|
||||
}
|
||||
|
||||
// computeWebserviceScore returns whether tokens match and
|
||||
// the weighted score of the longest matching consecutive tokens from the beginning.
|
||||
func (c CurlyRouter) computeWebserviceScore(requestTokens []string, tokens []string) (bool, int) {
|
||||
if len(tokens) > len(requestTokens) {
|
||||
func (c CurlyRouter) computeWebserviceScore(requestTokens []string, routeTokens []string) (bool, int) {
|
||||
if len(routeTokens) > len(requestTokens) {
|
||||
return false, 0
|
||||
}
|
||||
score := 0
|
||||
for i := 0; i < len(tokens); i++ {
|
||||
each := requestTokens[i]
|
||||
other := tokens[i]
|
||||
if len(each) == 0 && len(other) == 0 {
|
||||
for i := 0; i < len(routeTokens); i++ {
|
||||
eachRequestToken := requestTokens[i]
|
||||
eachRouteToken := routeTokens[i]
|
||||
if len(eachRequestToken) == 0 && len(eachRouteToken) == 0 {
|
||||
score++
|
||||
continue
|
||||
}
|
||||
if len(other) > 0 && strings.HasPrefix(other, "{") {
|
||||
if len(eachRouteToken) > 0 && strings.HasPrefix(eachRouteToken, "{") {
|
||||
// no empty match
|
||||
if len(each) == 0 {
|
||||
if len(eachRequestToken) == 0 {
|
||||
return false, score
|
||||
}
|
||||
score += 1
|
||||
score++
|
||||
|
||||
if colon := strings.Index(eachRouteToken, ":"); colon != -1 {
|
||||
// match by regex
|
||||
matchesToken, _ := c.regularMatchesPathToken(eachRouteToken, colon, eachRequestToken)
|
||||
if matchesToken {
|
||||
score++ // extra score for regex match
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// not a parameter
|
||||
if each != other {
|
||||
if eachRequestToken != eachRouteToken {
|
||||
return false, score
|
||||
}
|
||||
score += (len(tokens) - i) * 10 //fuzzy
|
||||
score += (len(routeTokens) - i) * 10 //fuzzy
|
||||
}
|
||||
}
|
||||
return true, score
|
||||
|
||||
+32
-2
@@ -1,14 +1,28 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2025 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
customVerbReg = regexp.MustCompile(":([A-Za-z]+)$")
|
||||
customVerbReg = regexp.MustCompile(":([A-Za-z]+)$")
|
||||
customVerbCache sync.Map // Cache for compiled custom verb regexes
|
||||
customVerbCacheEnabled = true // Enable/disable custom verb regex caching
|
||||
)
|
||||
|
||||
// SetCustomVerbCacheEnabled enables or disables custom verb regex caching.
|
||||
// When disabled, custom verb regex patterns will be compiled on every request.
|
||||
// When enabled (default), compiled custom verb regex patterns are cached for better performance.
|
||||
func SetCustomVerbCacheEnabled(enabled bool) {
|
||||
customVerbCacheEnabled = enabled
|
||||
}
|
||||
|
||||
func hasCustomVerb(routeToken string) bool {
|
||||
return customVerbReg.MatchString(routeToken)
|
||||
}
|
||||
@@ -20,7 +34,23 @@ func isMatchCustomVerb(routeToken string, pathToken string) bool {
|
||||
}
|
||||
|
||||
customVerb := rs[1]
|
||||
specificVerbReg := regexp.MustCompile(fmt.Sprintf(":%s$", customVerb))
|
||||
regexPattern := fmt.Sprintf(":%s$", customVerb)
|
||||
|
||||
// Check cache first (if enabled)
|
||||
if customVerbCacheEnabled {
|
||||
if specificVerbReg, found := getCachedRegexp(&customVerbCache, regexPattern); found {
|
||||
return specificVerbReg.MatchString(pathToken)
|
||||
}
|
||||
}
|
||||
|
||||
// Compile the regex
|
||||
specificVerbReg := regexp.MustCompile(regexPattern)
|
||||
|
||||
// Cache the regex (if enabled)
|
||||
if customVerbCacheEnabled {
|
||||
customVerbCache.Store(regexPattern, specificVerbReg)
|
||||
}
|
||||
|
||||
return specificVerbReg.MatchString(pathToken)
|
||||
}
|
||||
|
||||
|
||||
+19
-23
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Package restful , a lean package for creating REST-style WebServices without magic.
|
||||
|
||||
WebServices and Routes
|
||||
### WebServices and Routes
|
||||
|
||||
A WebService has a collection of Route objects that dispatch incoming Http Requests to a function calls.
|
||||
Typically, a WebService has a root path (e.g. /users) and defines common MIME types for its routes.
|
||||
@@ -30,14 +30,14 @@ The (*Request, *Response) arguments provide functions for reading information fr
|
||||
|
||||
See the example https://github.com/emicklei/go-restful/blob/v3/examples/user-resource/restful-user-resource.go with a full implementation.
|
||||
|
||||
Regular expression matching Routes
|
||||
### Regular expression matching Routes
|
||||
|
||||
A Route parameter can be specified using the format "uri/{var[:regexp]}" or the special version "uri/{var:*}" for matching the tail of the path.
|
||||
For example, /persons/{name:[A-Z][A-Z]} can be used to restrict values for the parameter "name" to only contain capital alphabetic characters.
|
||||
Regular expressions must use the standard Go syntax as described in the regexp package. (https://code.google.com/p/re2/wiki/Syntax)
|
||||
This feature requires the use of a CurlyRouter.
|
||||
|
||||
Containers
|
||||
### Containers
|
||||
|
||||
A Container holds a collection of WebServices, Filters and a http.ServeMux for multiplexing http requests.
|
||||
Using the statements "restful.Add(...) and restful.Filter(...)" will register WebServices and Filters to the Default Container.
|
||||
@@ -47,7 +47,7 @@ You can create your own Container and create a new http.Server for that particul
|
||||
container := restful.NewContainer()
|
||||
server := &http.Server{Addr: ":8081", Handler: container}
|
||||
|
||||
Filters
|
||||
### Filters
|
||||
|
||||
A filter dynamically intercepts requests and responses to transform or use the information contained in the requests or responses.
|
||||
You can use filters to perform generic logging, measurement, authentication, redirect, set response headers etc.
|
||||
@@ -60,22 +60,21 @@ Use the following statement to pass the request,response pair to the next filter
|
||||
|
||||
chain.ProcessFilter(req, resp)
|
||||
|
||||
Container Filters
|
||||
### Container Filters
|
||||
|
||||
These are processed before any registered WebService.
|
||||
|
||||
// install a (global) filter for the default container (processed before any webservice)
|
||||
restful.Filter(globalLogging)
|
||||
|
||||
WebService Filters
|
||||
### WebService Filters
|
||||
|
||||
These are processed before any Route of a WebService.
|
||||
|
||||
// install a webservice filter (processed before any route)
|
||||
ws.Filter(webserviceLogging).Filter(measureTime)
|
||||
|
||||
|
||||
Route Filters
|
||||
### Route Filters
|
||||
|
||||
These are processed before calling the function associated with the Route.
|
||||
|
||||
@@ -84,7 +83,7 @@ These are processed before calling the function associated with the Route.
|
||||
|
||||
See the example https://github.com/emicklei/go-restful/blob/v3/examples/filters/restful-filters.go with full implementations.
|
||||
|
||||
Response Encoding
|
||||
### Response Encoding
|
||||
|
||||
Two encodings are supported: gzip and deflate. To enable this for all responses:
|
||||
|
||||
@@ -95,20 +94,20 @@ Alternatively, you can create a Filter that performs the encoding and install it
|
||||
|
||||
See the example https://github.com/emicklei/go-restful/blob/v3/examples/encoding/restful-encoding-filter.go
|
||||
|
||||
OPTIONS support
|
||||
### OPTIONS support
|
||||
|
||||
By installing a pre-defined container filter, your Webservice(s) can respond to the OPTIONS Http request.
|
||||
|
||||
Filter(OPTIONSFilter())
|
||||
|
||||
CORS
|
||||
### CORS
|
||||
|
||||
By installing the filter of a CrossOriginResourceSharing (CORS), your WebService(s) can handle CORS requests.
|
||||
|
||||
cors := CrossOriginResourceSharing{ExposeHeaders: []string{"X-My-Header"}, CookiesAllowed: false, Container: DefaultContainer}
|
||||
Filter(cors.Filter)
|
||||
|
||||
Error Handling
|
||||
### Error Handling
|
||||
|
||||
Unexpected things happen. If a request cannot be processed because of a failure, your service needs to tell via the response what happened and why.
|
||||
For this reason HTTP status codes exist and it is important to use the correct code in every exceptional situation.
|
||||
@@ -137,11 +136,11 @@ The request does not have or has an unknown Accept Header set for this operation
|
||||
|
||||
The request does not have or has an unknown Content-Type Header set for this operation.
|
||||
|
||||
ServiceError
|
||||
### ServiceError
|
||||
|
||||
In addition to setting the correct (error) Http status code, you can choose to write a ServiceError message on the response.
|
||||
|
||||
Performance options
|
||||
### Performance options
|
||||
|
||||
This package has several options that affect the performance of your service. It is important to understand them and how you can change it.
|
||||
|
||||
@@ -156,30 +155,27 @@ Default value is true
|
||||
If content encoding is enabled then the default strategy for getting new gzip/zlib writers and readers is to use a sync.Pool.
|
||||
Because writers are expensive structures, performance is even more improved when using a preloaded cache. You can also inject your own implementation.
|
||||
|
||||
Trouble shooting
|
||||
### Trouble shooting
|
||||
|
||||
This package has the means to produce detail logging of the complete Http request matching process and filter invocation.
|
||||
Enabling this feature requires you to set an implementation of restful.StdLogger (e.g. log.Logger) instance such as:
|
||||
|
||||
restful.TraceLogger(log.New(os.Stdout, "[restful] ", log.LstdFlags|log.Lshortfile))
|
||||
|
||||
Logging
|
||||
### Logging
|
||||
|
||||
The restful.SetLogger() method allows you to override the logger used by the package. By default restful
|
||||
uses the standard library `log` package and logs to stdout. Different logging packages are supported as
|
||||
long as they conform to `StdLogger` interface defined in the `log` sub-package, writing an adapter for your
|
||||
preferred package is simple.
|
||||
|
||||
Resources
|
||||
### Resources
|
||||
|
||||
(c) 2012-2025, http://ernestmicklei.com. MIT License
|
||||
|
||||
[project]: https://github.com/emicklei/go-restful
|
||||
|
||||
[examples]: https://github.com/emicklei/go-restful/blob/master/examples
|
||||
|
||||
[design]: http://ernestmicklei.com/2012/11/11/go-restful-api-design/
|
||||
|
||||
[design]: http://ernestmicklei.com/2012/11/11/go-restful-api-design/
|
||||
[showcases]: https://github.com/emicklei/mora, https://github.com/emicklei/landskape
|
||||
|
||||
(c) 2012-2015, http://ernestmicklei.com. MIT License
|
||||
*/
|
||||
package restful
|
||||
|
||||
+7
@@ -5,11 +5,18 @@ package restful
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
MarshalIndent = json.MarshalIndent
|
||||
NewDecoder = json.NewDecoder
|
||||
NewEncoder = json.NewEncoder
|
||||
)
|
||||
|
||||
// EntityReaderWriter can read and write values using an encoding such as JSON,XML.
|
||||
type EntityReaderWriter interface {
|
||||
// Read a serialized version of the value from the request.
|
||||
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
// +build !jsoniter
|
||||
|
||||
package restful
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
var (
|
||||
MarshalIndent = json.MarshalIndent
|
||||
NewDecoder = json.NewDecoder
|
||||
NewEncoder = json.NewEncoder
|
||||
)
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
// +build jsoniter
|
||||
|
||||
package restful
|
||||
|
||||
import "github.com/json-iterator/go"
|
||||
|
||||
var (
|
||||
json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
MarshalIndent = json.MarshalIndent
|
||||
NewDecoder = json.NewDecoder
|
||||
NewEncoder = json.NewEncoder
|
||||
)
|
||||
+3
-16
@@ -65,7 +65,7 @@ func (RouterJSR311) extractParams(pathExpr *pathExpression, matches []string) ma
|
||||
return params
|
||||
}
|
||||
|
||||
// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2
|
||||
// https://download.oracle.com/otndocs/jcp/jaxrs-1.1-mrel-eval-oth-JSpec/
|
||||
func (r RouterJSR311) detectRoute(routes []Route, httpRequest *http.Request) (*Route, error) {
|
||||
candidates := make([]*Route, 0, 8)
|
||||
for i, each := range routes {
|
||||
@@ -126,9 +126,7 @@ func (r RouterJSR311) detectRoute(routes []Route, httpRequest *http.Request) (*R
|
||||
if trace {
|
||||
traceLogger.Printf("no Route found (from %d) that matches HTTP Content-Type: %s\n", len(previous), contentType)
|
||||
}
|
||||
if httpRequest.ContentLength > 0 {
|
||||
return nil, NewError(http.StatusUnsupportedMediaType, "415: Unsupported Media Type")
|
||||
}
|
||||
return nil, NewError(http.StatusUnsupportedMediaType, "415: Unsupported Media Type")
|
||||
}
|
||||
|
||||
// accept
|
||||
@@ -151,20 +149,9 @@ func (r RouterJSR311) detectRoute(routes []Route, httpRequest *http.Request) (*R
|
||||
for _, candidate := range previous {
|
||||
available = append(available, candidate.Produces...)
|
||||
}
|
||||
// if POST,PUT,PATCH without body
|
||||
method, length := httpRequest.Method, httpRequest.Header.Get("Content-Length")
|
||||
if (method == http.MethodPost ||
|
||||
method == http.MethodPut ||
|
||||
method == http.MethodPatch) && length == "" {
|
||||
return nil, NewError(
|
||||
http.StatusUnsupportedMediaType,
|
||||
fmt.Sprintf("415: Unsupported Media Type\n\nAvailable representations: %s", strings.Join(available, ", ")),
|
||||
)
|
||||
}
|
||||
return nil, NewError(
|
||||
http.StatusNotAcceptable,
|
||||
fmt.Sprintf("406: Not Acceptable\n\nAvailable representations: %s", strings.Join(available, ", ")),
|
||||
)
|
||||
fmt.Sprintf("406: Not Acceptable\n\nAvailable representations: %s", strings.Join(available, ", ")))
|
||||
}
|
||||
// return r.bestMatchByMedia(outputMediaOk, contentType, accept), nil
|
||||
return candidates[0], nil
|
||||
|
||||
+2
@@ -111,6 +111,8 @@ func (r Route) matchesAccept(mimeTypesWithQuality string) bool {
|
||||
}
|
||||
|
||||
// Return whether this Route can consume content with a type specified by mimeTypes (can be empty).
|
||||
// If the route does not specify Consumes then return true (*/*).
|
||||
// If no content type is set then return true for GET,HEAD,OPTIONS,DELETE and TRACE.
|
||||
func (r Route) matchesContentType(mimeTypes string) bool {
|
||||
|
||||
if len(r.Consumes) == 0 {
|
||||
|
||||
+417
-174
@@ -1,30 +1,31 @@
|
||||
# CBOR Codec in Go
|
||||
|
||||
<!-- [](#cbor-library-in-go) -->
|
||||
<h1>CBOR Codec <a href="https://pkg.go.dev/github.com/fxamacker/cbor/v2"><img src="https://raw.githubusercontent.com/fxamacker/images/refs/heads/master/cbor/go-logo-blue.svg" alt="Go logo" style="height: 1em;" align="right"></a></h1>
|
||||
|
||||
[fxamacker/cbor](https://github.com/fxamacker/cbor) is a library for encoding and decoding [CBOR](https://www.rfc-editor.org/info/std94) and [CBOR Sequences](https://www.rfc-editor.org/rfc/rfc8742.html).
|
||||
|
||||
CBOR is a [trusted alternative](https://www.rfc-editor.org/rfc/rfc8949.html#name-comparison-of-other-binary-) to JSON, MessagePack, Protocol Buffers, etc. CBOR is an Internet Standard defined by [IETF STD 94 (RFC 8949)](https://www.rfc-editor.org/info/std94) and is designed to be relevant for decades.
|
||||
|
||||
`fxamacker/cbor` is used in projects by Arm Ltd., Cisco, EdgeX Foundry, Flow Foundation, Fraunhofer‑AISEC, Kubernetes, Let's Encrypt (ISRG), Linux Foundation, Microsoft, Mozilla, Oasis Protocol, Tailscale, Teleport, [etc](https://github.com/fxamacker/cbor#who-uses-fxamackercbor).
|
||||
`fxamacker/cbor` is used in projects by Arm Ltd., EdgeX Foundry, Flow Foundation, Fraunhofer‑AISEC, IBM, Kubernetes[*](https://github.com/search?q=org%3Akubernetes%20fxamacker%2Fcbor&type=code), Let's Encrypt, Linux Foundation, Microsoft, Oasis Protocol, Red Hat[*](https://github.com/search?q=org%3Aopenshift+fxamacker%2Fcbor&type=code), Tailscale[*](https://github.com/search?q=org%3Atailscale+fxamacker%2Fcbor&type=code), Veraison[*](https://github.com/search?q=org%3Averaison+fxamacker%2Fcbor&type=code), [etc](https://github.com/fxamacker/cbor#who-uses-fxamackercbor).
|
||||
|
||||
See [Quick Start](#quick-start) and [Releases](https://github.com/fxamacker/cbor/releases/). 🆕 `UnmarshalFirst` and `DiagnoseFirst` can decode CBOR Sequences. `cbor.MarshalToBuffer()` and `UserBufferEncMode` accepts user-specified buffer.
|
||||
See [Quick Start](#quick-start) and [Releases](https://github.com/fxamacker/cbor/releases/). 🆕 `UnmarshalFirst` and `DiagnoseFirst` can decode CBOR Sequences. `MarshalToBuffer` and `UserBufferEncMode` accepts user-specified buffer.
|
||||
|
||||
## fxamacker/cbor
|
||||
|
||||
[](https://github.com/fxamacker/cbor/actions?query=workflow%3Aci)
|
||||
[](https://github.com/fxamacker/cbor/actions?query=workflow%3A%22cover+%E2%89%A596%25%22)
|
||||
[](https://github.com/fxamacker/cbor/actions?query=workflow%3A%22cover+%E2%89%A597%25%22)
|
||||
[](https://github.com/fxamacker/cbor/actions/workflows/codeql-analysis.yml)
|
||||
[](#fuzzing-and-code-coverage)
|
||||
[](https://goreportcard.com/report/github.com/fxamacker/cbor)
|
||||
[](https://github.com/fxamacker/cbor#fuzzing-and-code-coverage)
|
||||
|
||||
`fxamacker/cbor` is a CBOR codec in full conformance with [IETF STD 94 (RFC 8949)](https://www.rfc-editor.org/info/std94). It also supports CBOR Sequences ([RFC 8742](https://www.rfc-editor.org/rfc/rfc8742.html)) and Extended Diagnostic Notation ([Appendix G of RFC 8610](https://www.rfc-editor.org/rfc/rfc8610.html#appendix-G)).
|
||||
|
||||
Features include full support for CBOR tags, [Core Deterministic Encoding](https://www.rfc-editor.org/rfc/rfc8949.html#name-core-deterministic-encoding), duplicate map key detection, etc.
|
||||
|
||||
API is mostly same as `encoding/json`, plus interfaces that simplify concurrency and CBOR options.
|
||||
|
||||
Design balances trade-offs between security, speed, concurrency, encoded data size, usability, etc.
|
||||
|
||||
<details><summary>Highlights</summary><p/>
|
||||
<details><summary> 🔎 Highlights</summary><p/>
|
||||
|
||||
__🚀 Speed__
|
||||
|
||||
@@ -38,7 +39,7 @@ Codec passed multiple confidential security assessments in 2022. No vulnerabili
|
||||
|
||||
__🗜️ Data Size__
|
||||
|
||||
Struct tags (`toarray`, `keyasint`, `omitempty`) automatically reduce size of encoded structs. Encoding optionally shrinks float64→32→16 when values fit.
|
||||
Struct tag options (`toarray`, `keyasint`, `omitempty`, `omitzero`) and field tag "-" automatically reduce size of encoded structs. Encoding optionally shrinks float64→32→16 when values fit.
|
||||
|
||||
__:jigsaw: Usability__
|
||||
|
||||
@@ -58,164 +59,205 @@ Features include CBOR [extension points](https://www.rfc-editor.org/rfc/rfc8949.
|
||||
|
||||
`fxamacker/cbor` has configurable limits, etc. that defend against malicious CBOR data.
|
||||
|
||||
By contrast, `encoding/gob` is [not designed to be hardened against adversarial inputs](https://pkg.go.dev/encoding/gob#hdr-Security).
|
||||
Notably, `fxamacker/cbor` is fast at rejecting malformed CBOR data.
|
||||
|
||||
<details><summary>Example decoding with encoding/gob 💥 fatal error (out of memory)</summary><p/>
|
||||
> [!NOTE]
|
||||
> Benchmarks rejecting 10 bytes of malicious CBOR data decoding to `[]byte`:
|
||||
>
|
||||
> | Codec | Speed (ns/op) | Memory | Allocs |
|
||||
> | :---- | ------------: | -----: | -----: |
|
||||
> | fxamacker/cbor 2.7.0 | 47 ± 7% | 32 B/op | 2 allocs/op |
|
||||
> | ugorji/go 1.2.12 | 5878187 ± 3% | 67111556 B/op | 13 allocs/op |
|
||||
>
|
||||
> Faster hardware (overclocked DDR4 or DDR5) can reduce speed difference.
|
||||
>
|
||||
> <details><summary> 🔎 Benchmark details </summary><p/>
|
||||
>
|
||||
> Latest comparison for decoding CBOR data to Go `[]byte`:
|
||||
> - Input: `[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}`
|
||||
> - go1.22.7, linux/amd64, i5-13600K (DDR4-2933, disabled e-cores)
|
||||
> - go test -bench=. -benchmem -count=20
|
||||
>
|
||||
> #### Prior comparisons
|
||||
>
|
||||
> | Codec | Speed (ns/op) | Memory | Allocs |
|
||||
> | :---- | ------------: | -----: | -----: |
|
||||
> | fxamacker/cbor 2.5.0-beta2 | 44.33 ± 2% | 32 B/op | 2 allocs/op |
|
||||
> | fxamacker/cbor 0.1.0 - 2.4.0 | ~44.68 ± 6% | 32 B/op | 2 allocs/op |
|
||||
> | ugorji/go 1.2.10 | 5524792.50 ± 3% | 67110491 B/op | 12 allocs/op |
|
||||
> | ugorji/go 1.1.0 - 1.2.6 | 💥 runtime: | out of memory: | cannot allocate |
|
||||
>
|
||||
> - Input: `[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}`
|
||||
> - go1.19.6, linux/amd64, i5-13600K (DDR4)
|
||||
> - go test -bench=. -benchmem -count=20
|
||||
>
|
||||
> </details>
|
||||
|
||||
```Go
|
||||
// Example of encoding/gob having "fatal error: runtime: out of memory"
|
||||
// while decoding 181 bytes.
|
||||
package main
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
In contrast, some codecs can crash or use excessive resources while decoding bad data.
|
||||
|
||||
// Example data is from https://github.com/golang/go/issues/24446
|
||||
// (shortened to 181 bytes).
|
||||
const data = "4dffb503010102303001ff30000109010130010800010130010800010130" +
|
||||
"01ffb80001014a01ffb60001014b01ff860001013001ff860001013001ff" +
|
||||
"860001013001ff860001013001ffb80000001eff850401010e3030303030" +
|
||||
"30303030303030303001ff3000010c0104000016ffb70201010830303030" +
|
||||
"3030303001ff3000010c000030ffb6040405fcff00303030303030303030" +
|
||||
"303030303030303030303030303030303030303030303030303030303030" +
|
||||
"30"
|
||||
> [!WARNING]
|
||||
> Go's `encoding/gob` is [not designed to be hardened against adversarial inputs](https://pkg.go.dev/encoding/gob#hdr-Security).
|
||||
>
|
||||
> <details><summary> 🔎 gob fatal error (out of memory) 💥 decoding 181 bytes</summary><p/>
|
||||
>
|
||||
> ```Go
|
||||
> // Example of encoding/gob having "fatal error: runtime: out of memory"
|
||||
> // while decoding 181 bytes (all Go versions as of Dec. 8, 2024).
|
||||
> package main
|
||||
> import (
|
||||
> "bytes"
|
||||
> "encoding/gob"
|
||||
> "encoding/hex"
|
||||
> "fmt"
|
||||
> )
|
||||
>
|
||||
> // Example data is from https://github.com/golang/go/issues/24446
|
||||
> // (shortened to 181 bytes).
|
||||
> const data = "4dffb503010102303001ff30000109010130010800010130010800010130" +
|
||||
> "01ffb80001014a01ffb60001014b01ff860001013001ff860001013001ff" +
|
||||
> "860001013001ff860001013001ffb80000001eff850401010e3030303030" +
|
||||
> "30303030303030303001ff3000010c0104000016ffb70201010830303030" +
|
||||
> "3030303001ff3000010c000030ffb6040405fcff00303030303030303030" +
|
||||
> "303030303030303030303030303030303030303030303030303030303030" +
|
||||
> "30"
|
||||
>
|
||||
> type X struct {
|
||||
> J *X
|
||||
> K map[string]int
|
||||
> }
|
||||
>
|
||||
> func main() {
|
||||
> raw, _ := hex.DecodeString(data)
|
||||
> decoder := gob.NewDecoder(bytes.NewReader(raw))
|
||||
>
|
||||
> var x X
|
||||
> decoder.Decode(&x) // fatal error: runtime: out of memory
|
||||
> fmt.Println("Decoding finished.")
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
>
|
||||
> </details>
|
||||
|
||||
type X struct {
|
||||
J *X
|
||||
K map[string]int
|
||||
}
|
||||
### Smaller Encodings with Struct Tag Options
|
||||
|
||||
func main() {
|
||||
raw, _ := hex.DecodeString(data)
|
||||
decoder := gob.NewDecoder(bytes.NewReader(raw))
|
||||
Struct tags automatically reduce encoded size of structs and improve speed.
|
||||
|
||||
var x X
|
||||
decoder.Decode(&x) // fatal error: runtime: out of memory
|
||||
fmt.Println("Decoding finished.")
|
||||
}
|
||||
```
|
||||
We can write less code by using struct tag options:
|
||||
- `toarray`: encode without field names (decode back to original struct)
|
||||
- `keyasint`: encode field names as integers (decode back to original struct)
|
||||
- `omitempty`: omit empty field when encoding
|
||||
- `omitzero`: omit zero-value field when encoding
|
||||
|
||||
<hr/>
|
||||
As a special case, struct field tag "-" omits the field.
|
||||
|
||||
</details>
|
||||
|
||||
`fxamacker/cbor` is fast at rejecting malformed CBOR data. E.g. attempts to
|
||||
decode 10 bytes of malicious CBOR data to `[]byte` (with default settings):
|
||||
|
||||
| Codec | Speed (ns/op) | Memory | Allocs |
|
||||
| :---- | ------------: | -----: | -----: |
|
||||
| fxamacker/cbor 2.5.0 | 44 ± 5% | 32 B/op | 2 allocs/op |
|
||||
| ugorji/go 1.2.11 | 5353261 ± 4% | 67111321 B/op | 13 allocs/op |
|
||||
|
||||
<details><summary>Benchmark details</summary><p/>
|
||||
|
||||
Latest comparison used:
|
||||
- Input: `[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}`
|
||||
- go1.19.10, linux/amd64, i5-13600K (disabled all e-cores, DDR4 @2933)
|
||||
- go test -bench=. -benchmem -count=20
|
||||
|
||||
#### Prior comparisons
|
||||
|
||||
| Codec | Speed (ns/op) | Memory | Allocs |
|
||||
| :---- | ------------: | -----: | -----: |
|
||||
| fxamacker/cbor 2.5.0-beta2 | 44.33 ± 2% | 32 B/op | 2 allocs/op |
|
||||
| fxamacker/cbor 0.1.0 - 2.4.0 | ~44.68 ± 6% | 32 B/op | 2 allocs/op |
|
||||
| ugorji/go 1.2.10 | 5524792.50 ± 3% | 67110491 B/op | 12 allocs/op |
|
||||
| ugorji/go 1.1.0 - 1.2.6 | 💥 runtime: | out of memory: | cannot allocate |
|
||||
|
||||
- Input: `[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}`
|
||||
- go1.19.6, linux/amd64, i5-13600K (DDR4)
|
||||
- go test -bench=. -benchmem -count=20
|
||||
|
||||
<hr/>
|
||||
|
||||
</details>
|
||||
|
||||
### Smaller Encodings with Struct Tags
|
||||
|
||||
Struct tags (`toarray`, `keyasint`, `omitempty`) reduce encoded size of structs.
|
||||
|
||||
<details><summary>Example encoding 3-level nested Go struct to 1 byte CBOR</summary><p/>
|
||||
|
||||
https://go.dev/play/p/YxwvfPdFQG2
|
||||
|
||||
```Go
|
||||
// Example encoding nested struct (with omitempty tag)
|
||||
// - encoding/json: 18 byte JSON
|
||||
// - fxamacker/cbor: 1 byte CBOR
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
)
|
||||
|
||||
type GrandChild struct {
|
||||
Quux int `json:",omitempty"`
|
||||
}
|
||||
|
||||
type Child struct {
|
||||
Baz int `json:",omitempty"`
|
||||
Qux GrandChild `json:",omitempty"`
|
||||
}
|
||||
|
||||
type Parent struct {
|
||||
Foo Child `json:",omitempty"`
|
||||
Bar int `json:",omitempty"`
|
||||
}
|
||||
|
||||
func cb() {
|
||||
results, _ := cbor.Marshal(Parent{})
|
||||
fmt.Println("hex(CBOR): " + hex.EncodeToString(results))
|
||||
|
||||
text, _ := cbor.Diagnose(results) // Diagnostic Notation
|
||||
fmt.Println("DN: " + text)
|
||||
}
|
||||
|
||||
func js() {
|
||||
results, _ := json.Marshal(Parent{})
|
||||
fmt.Println("hex(JSON): " + hex.EncodeToString(results))
|
||||
|
||||
text := string(results) // JSON
|
||||
fmt.Println("JSON: " + text)
|
||||
}
|
||||
|
||||
func main() {
|
||||
cb()
|
||||
fmt.Println("-------------")
|
||||
js()
|
||||
}
|
||||
```
|
||||
|
||||
Output (DN is Diagnostic Notation):
|
||||
```
|
||||
hex(CBOR): a0
|
||||
DN: {}
|
||||
-------------
|
||||
hex(JSON): 7b22466f6f223a7b22517578223a7b7d7d7d
|
||||
JSON: {"Foo":{"Qux":{}}}
|
||||
```
|
||||
|
||||
<hr/>
|
||||
|
||||
</details>
|
||||
|
||||
Example using different struct tags together:
|
||||
NOTE: When a struct uses `toarray`, the encoder will ignore `omitempty` and `omitzero` to prevent position of encoded array elements from changing. This allows decoder to match encoded elements to their Go struct field.
|
||||
|
||||

|
||||
|
||||
API is mostly same as `encoding/json`, plus interfaces that simplify concurrency for CBOR options.
|
||||
> [!NOTE]
|
||||
> `fxamacker/cbor` can encode a 3-level nested Go struct to 1 byte!
|
||||
> - `encoding/json`: 18 bytes of JSON
|
||||
> - `fxamacker/cbor`: 1 byte of CBOR
|
||||
>
|
||||
> <details><summary> 🔎 Encoding 3-level nested Go struct with omitempty</summary><p/>
|
||||
>
|
||||
> https://go.dev/play/p/YxwvfPdFQG2
|
||||
>
|
||||
> ```Go
|
||||
> // Example encoding nested struct (with omitempty tag)
|
||||
> // - encoding/json: 18 byte JSON
|
||||
> // - fxamacker/cbor: 1 byte CBOR
|
||||
>
|
||||
> package main
|
||||
>
|
||||
> import (
|
||||
> "encoding/hex"
|
||||
> "encoding/json"
|
||||
> "fmt"
|
||||
>
|
||||
> "github.com/fxamacker/cbor/v2"
|
||||
> )
|
||||
>
|
||||
> type GrandChild struct {
|
||||
> Quux int `json:",omitempty"`
|
||||
> }
|
||||
>
|
||||
> type Child struct {
|
||||
> Baz int `json:",omitempty"`
|
||||
> Qux GrandChild `json:",omitempty"`
|
||||
> }
|
||||
>
|
||||
> type Parent struct {
|
||||
> Foo Child `json:",omitempty"`
|
||||
> Bar int `json:",omitempty"`
|
||||
> }
|
||||
>
|
||||
> func cb() {
|
||||
> results, _ := cbor.Marshal(Parent{})
|
||||
> fmt.Println("hex(CBOR): " + hex.EncodeToString(results))
|
||||
>
|
||||
> text, _ := cbor.Diagnose(results) // Diagnostic Notation
|
||||
> fmt.Println("DN: " + text)
|
||||
> }
|
||||
>
|
||||
> func js() {
|
||||
> results, _ := json.Marshal(Parent{})
|
||||
> fmt.Println("hex(JSON): " + hex.EncodeToString(results))
|
||||
>
|
||||
> text := string(results) // JSON
|
||||
> fmt.Println("JSON: " + text)
|
||||
> }
|
||||
>
|
||||
> func main() {
|
||||
> cb()
|
||||
> fmt.Println("-------------")
|
||||
> js()
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
> Output (DN is Diagnostic Notation):
|
||||
> ```
|
||||
> hex(CBOR): a0
|
||||
> DN: {}
|
||||
> -------------
|
||||
> hex(JSON): 7b22466f6f223a7b22517578223a7b7d7d7d
|
||||
> JSON: {"Foo":{"Qux":{}}}
|
||||
> ```
|
||||
>
|
||||
> </details>
|
||||
|
||||
|
||||
## Quick Start
|
||||
|
||||
__Install__: `go get github.com/fxamacker/cbor/v2` and `import "github.com/fxamacker/cbor/v2"`.
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Tinygo users can try beta/experimental branch [feature/cbor-tinygo-beta](https://github.com/fxamacker/cbor/tree/feature/cbor-tinygo-beta).
|
||||
>
|
||||
> <details><summary> 🔎 More about tinygo feature branch</summary>
|
||||
>
|
||||
> ### Tinygo
|
||||
>
|
||||
> Branch [feature/cbor-tinygo-beta](https://github.com/fxamacker/cbor/tree/feature/cbor-tinygo-beta) is based on fxamacker/cbor v2.7.0 and it can be compiled using tinygo v0.33 (also compiles with golang/go).
|
||||
>
|
||||
> It passes unit tests (with both go1.22 and tinygo v0.33) and is considered beta/experimental for tinygo.
|
||||
>
|
||||
> :warning: The `feature/cbor-tinygo-beta` branch does not get fuzz tested yet.
|
||||
>
|
||||
> Changes in this feature branch only affect tinygo compiled software. Summary of changes:
|
||||
> - default `DecOptions.MaxNestedLevels` is reduced to 16 (was 32). User can specify higher limit but 24+ crashes tests when compiled with tinygo v0.33.
|
||||
> - disabled decoding CBOR tag data to Go interface because tinygo v0.33 is missing needed feature.
|
||||
> - encoding error message can be different when encoding function type.
|
||||
>
|
||||
> Related tinygo issues:
|
||||
> - https://github.com/tinygo-org/tinygo/issues/4277
|
||||
> - https://github.com/tinygo-org/tinygo/issues/4458
|
||||
>
|
||||
> </details>
|
||||
|
||||
|
||||
### Key Points
|
||||
|
||||
This library can encode and decode CBOR (RFC 8949) and CBOR Sequences (RFC 8742).
|
||||
@@ -252,16 +294,17 @@ rest, err = cbor.UnmarshalFirst(b, &v) // decode []byte b to v
|
||||
// DiagnoseFirst translates first CBOR data item to text and returns remaining bytes.
|
||||
text, rest, err = cbor.DiagnoseFirst(b) // decode []byte b to Diagnostic Notation text
|
||||
|
||||
// NOTE: Unmarshal returns ExtraneousDataError if there are remaining bytes,
|
||||
// but new funcs UnmarshalFirst and DiagnoseFirst do not.
|
||||
// NOTE: Unmarshal() returns ExtraneousDataError if there are remaining bytes, but
|
||||
// UnmarshalFirst() and DiagnoseFirst() allow trailing bytes.
|
||||
```
|
||||
|
||||
__IMPORTANT__: 👉 CBOR settings allow trade-offs between speed, security, encoding size, etc.
|
||||
|
||||
- Different CBOR libraries may use different default settings.
|
||||
- CBOR-based formats or protocols usually require specific settings.
|
||||
|
||||
For example, WebAuthn uses "CTAP2 Canonical CBOR" which is available as a preset.
|
||||
> [!IMPORTANT]
|
||||
> CBOR settings allow trade-offs between speed, security, encoding size, etc.
|
||||
>
|
||||
> - Different CBOR libraries may use different default settings.
|
||||
> - CBOR-based formats or protocols usually require specific settings.
|
||||
>
|
||||
> For example, WebAuthn uses "CTAP2 Canonical CBOR" which is available as a preset.
|
||||
|
||||
### Presets
|
||||
|
||||
@@ -312,9 +355,63 @@ err = em.MarshalToBuffer(v, &buf) // encode v to provided buf
|
||||
|
||||
### Struct Tags
|
||||
|
||||
Struct tags (`toarray`, `keyasint`, `omitempty`) reduce encoded size of structs.
|
||||
Struct tag options (`toarray`, `keyasint`, `omitempty`, `omitzero`) reduce encoded size of structs.
|
||||
|
||||
<details><summary>Example encoding 3-level nested Go struct to 1 byte CBOR</summary><p/>
|
||||
As a special case, struct field tag "-" omits the field.
|
||||
|
||||
<details><summary> 🔎 Example encoding with struct field tag "-"</summary><p/>
|
||||
|
||||
https://go.dev/play/p/aWEIFxd7InX
|
||||
|
||||
```Go
|
||||
// https://github.com/fxamacker/cbor/issues/652
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
)
|
||||
|
||||
// The `cbor:"-"` tag omits the Type field when encoding to CBOR.
|
||||
type Entity struct {
|
||||
_ struct{} `cbor:",toarray"`
|
||||
ID uint64 `json:"id"`
|
||||
Type string `cbor:"-" json:"typeOf"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
entity := Entity{
|
||||
ID: 1,
|
||||
Type: "int64",
|
||||
Name: "Identifier",
|
||||
}
|
||||
|
||||
c, _ := cbor.Marshal(entity)
|
||||
diag, _ := cbor.Diagnose(c)
|
||||
fmt.Printf("CBOR in hex: %x\n", c)
|
||||
fmt.Printf("CBOR in edn: %s\n", diag)
|
||||
|
||||
j, _ := json.Marshal(entity)
|
||||
fmt.Printf("JSON: %s\n", string(j))
|
||||
|
||||
fmt.Printf("JSON encoding is %d bytes\n", len(j))
|
||||
fmt.Printf("CBOR encoding is %d bytes\n", len(c))
|
||||
|
||||
// Output:
|
||||
// CBOR in hex: 82016a4964656e746966696572
|
||||
// CBOR in edn: [1, "Identifier"]
|
||||
// JSON: {"id":1,"typeOf":"int64","name":"Identifier"}
|
||||
// JSON encoding is 45 bytes
|
||||
// CBOR encoding is 13 bytes
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary> 🔎 Example encoding 3-level nested Go struct to 1 byte CBOR</summary><p/>
|
||||
|
||||
https://go.dev/play/p/YxwvfPdFQG2
|
||||
|
||||
@@ -382,13 +479,13 @@ JSON: {"Foo":{"Qux":{}}}
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>Example using several struct tags</summary><p/>
|
||||
<details><summary> 🔎 Example using struct tag options</summary><p/>
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
Struct tags simplify use of CBOR-based protocols that require CBOR arrays or maps with integer keys.
|
||||
Struct tag options simplify use of CBOR-based protocols that require CBOR arrays or maps with integer keys.
|
||||
|
||||
### CBOR Tags
|
||||
|
||||
@@ -404,7 +501,7 @@ em, err := opts.EncModeWithSharedTags(ts) // mutable shared CBOR tags
|
||||
|
||||
`TagSet` and modes using it are safe for concurrent use. Equivalent API is available for `DecMode`.
|
||||
|
||||
<details><summary>Example using TagSet and TagOptions</summary><p/>
|
||||
<details><summary> 🔎 Example using TagSet and TagOptions</summary><p/>
|
||||
|
||||
```go
|
||||
// Use signedCWT struct defined in "Decoding CWT" example.
|
||||
@@ -430,16 +527,149 @@ if err := dm.Unmarshal(data, &v); err != nil {
|
||||
em, _ := cbor.EncOptions{}.EncModeWithTags(tags)
|
||||
|
||||
// Marshal signedCWT with tag number.
|
||||
if data, err := cbor.Marshal(v); err != nil {
|
||||
if data, err := em.Marshal(v); err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
👉 `fxamacker/cbor` allows user apps to use almost any current or future CBOR tag number by implementing `cbor.Marshaler` and `cbor.Unmarshaler` interfaces.
|
||||
|
||||
Basically, `MarshalCBOR` and `UnmarshalCBOR` functions can be implemented by user apps and those functions will automatically be called by this CBOR codec's `Marshal`, `Unmarshal`, etc.
|
||||
|
||||
The following [example](https://github.com/fxamacker/cbor/blob/master/example_embedded_json_tag_for_cbor_test.go) shows how to encode and decode a tagged CBOR data item with tag number 262. The tag content is a JSON object "embedded" as a CBOR byte string (major type 2).
|
||||
|
||||
<details><summary> 🔎 Example using Embedded JSON Tag for CBOR (tag 262)</summary>
|
||||
|
||||
```go
|
||||
// https://github.com/fxamacker/cbor/issues/657
|
||||
|
||||
package cbor_test
|
||||
|
||||
// NOTE: RFC 8949 does not mention tag number 262. IANA assigned
|
||||
// CBOR tag number 262 as "Embedded JSON Object" specified by the
|
||||
// document Embedded JSON Tag for CBOR:
|
||||
//
|
||||
// "Tag 262 can be applied to a byte string (major type 2) to indicate
|
||||
// that the byte string is a JSON Object. The length of the byte string
|
||||
// indicates the content."
|
||||
//
|
||||
// For more info, see Embedded JSON Tag for CBOR at:
|
||||
// https://github.com/toravir/CBOR-Tag-Specs/blob/master/embeddedJSON.md
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
)
|
||||
|
||||
// cborTagNumForEmbeddedJSON is the CBOR tag number 262.
|
||||
const cborTagNumForEmbeddedJSON = 262
|
||||
|
||||
// EmbeddedJSON represents a Go value to be encoded as a tagged CBOR data item
|
||||
// with tag number 262 and the tag content is a JSON object "embedded" as a
|
||||
// CBOR byte string (major type 2).
|
||||
type EmbeddedJSON struct {
|
||||
any
|
||||
}
|
||||
|
||||
func NewEmbeddedJSON(val any) EmbeddedJSON {
|
||||
return EmbeddedJSON{val}
|
||||
}
|
||||
|
||||
// MarshalCBOR encodes EmbeddedJSON to a tagged CBOR data item with the
|
||||
// tag number 262 and the tag content is a JSON object that is
|
||||
// "embedded" as a CBOR byte string.
|
||||
func (v EmbeddedJSON) MarshalCBOR() ([]byte, error) {
|
||||
// Encode v to JSON object.
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create cbor.Tag representing a tagged CBOR data item.
|
||||
tag := cbor.Tag{
|
||||
Number: cborTagNumForEmbeddedJSON,
|
||||
Content: data,
|
||||
}
|
||||
|
||||
// Marshal to a tagged CBOR data item.
|
||||
return cbor.Marshal(tag)
|
||||
}
|
||||
|
||||
// UnmarshalCBOR decodes a tagged CBOR data item to EmbeddedJSON.
|
||||
// The byte slice provided to this function must contain a single
|
||||
// tagged CBOR data item with the tag number 262 and tag content
|
||||
// must be a JSON object "embedded" as a CBOR byte string.
|
||||
func (v *EmbeddedJSON) UnmarshalCBOR(b []byte) error {
|
||||
// Unmarshal tagged CBOR data item.
|
||||
var tag cbor.Tag
|
||||
if err := cbor.Unmarshal(b, &tag); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check tag number.
|
||||
if tag.Number != cborTagNumForEmbeddedJSON {
|
||||
return fmt.Errorf("got tag number %d, expect tag number %d", tag.Number, cborTagNumForEmbeddedJSON)
|
||||
}
|
||||
|
||||
// Check tag content.
|
||||
jsonData, isByteString := tag.Content.([]byte)
|
||||
if !isByteString {
|
||||
return fmt.Errorf("got tag content type %T, expect tag content []byte", tag.Content)
|
||||
}
|
||||
|
||||
// Unmarshal JSON object.
|
||||
return json.Unmarshal(jsonData, v)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes EmbeddedJSON to a JSON object.
|
||||
func (v EmbeddedJSON) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(v.any)
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes a JSON object.
|
||||
func (v *EmbeddedJSON) UnmarshalJSON(b []byte) error {
|
||||
dec := json.NewDecoder(bytes.NewReader(b))
|
||||
dec.UseNumber()
|
||||
return dec.Decode(&v.any)
|
||||
}
|
||||
|
||||
func Example_embeddedJSONTagForCBOR() {
|
||||
value := NewEmbeddedJSON(map[string]any{
|
||||
"name": "gopher",
|
||||
"id": json.Number("42"),
|
||||
})
|
||||
|
||||
data, err := cbor.Marshal(value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("cbor: %x\n", data)
|
||||
|
||||
var v EmbeddedJSON
|
||||
err = cbor.Unmarshal(data, &v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%+v\n", v.any)
|
||||
for k, v := range v.any.(map[string]any) {
|
||||
fmt.Printf(" %s: %v (%T)\n", k, v, v)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
### Functions and Interfaces
|
||||
|
||||
<details><summary>Functions and interfaces at a glance</summary><p/>
|
||||
<details><summary> 🔎 Functions and interfaces at a glance</summary><p/>
|
||||
|
||||
Common functions with same API as `encoding/json`:
|
||||
- `Marshal`, `Unmarshal`
|
||||
@@ -453,7 +683,7 @@ because RFC 8949 treats CBOR data item with remaining bytes as malformed.
|
||||
Other useful functions:
|
||||
- `Diagnose`, `DiagnoseFirst` produce human-readable [Extended Diagnostic Notation](https://www.rfc-editor.org/rfc/rfc8610.html#appendix-G) from CBOR data.
|
||||
- `UnmarshalFirst` decodes first CBOR data item and return any remaining bytes.
|
||||
- `Wellformed` returns true if the the CBOR data item is well-formed.
|
||||
- `Wellformed` returns true if the CBOR data item is well-formed.
|
||||
|
||||
Interfaces identical or comparable to Go `encoding` packages include:
|
||||
`Marshaler`, `Unmarshaler`, `BinaryMarshaler`, and `BinaryUnmarshaler`.
|
||||
@@ -472,15 +702,28 @@ Default limits may need to be increased for systems handling very large data (e.
|
||||
|
||||
## Status
|
||||
|
||||
v2.7.0 (June 23, 2024) adds features and improvements that help large projects (e.g. Kubernetes) use CBOR as an alternative to JSON and Protocol Buffers. Other improvements include speedups, improved memory use, bug fixes, new serialization options, etc. It passed fuzz tests (5+ billion executions) and is production quality.
|
||||
[v2.9.0](https://github.com/fxamacker/cbor/releases/tag/v2.9.0) (Jul 13, 2025) improved interoperability/transcoding between CBOR & JSON, refactored tests, and improved docs.
|
||||
- Add opt-in support for `encoding.TextMarshaler` and `encoding.TextUnmarshaler` to encode and decode from CBOR text string.
|
||||
- Add opt-in support for `json.Marshaler` and `json.Unmarshaler` via user-provided transcoding function.
|
||||
- Update docs for TimeMode, Tag, RawTag, and add example for Embedded JSON Tag for CBOR.
|
||||
|
||||
v2.9.0 passed fuzz tests and is production quality.
|
||||
|
||||
The minimum version of Go required to build:
|
||||
- v2.8.0 and newer releases require go 1.20+.
|
||||
- v2.7.1 and older releases require go 1.17+.
|
||||
|
||||
For more details, see [release notes](https://github.com/fxamacker/cbor/releases).
|
||||
|
||||
### Prior Release
|
||||
### Prior Releases
|
||||
|
||||
[v2.8.0](https://github.com/fxamacker/cbor/releases/tag/v2.8.0) (March 30, 2025) is a small release primarily to add `omitzero` option to struct field tags and fix bugs. It passed fuzz tests (billions of executions) and is production quality.
|
||||
|
||||
[v2.7.0](https://github.com/fxamacker/cbor/releases/tag/v2.7.0) (June 23, 2024) adds features and improvements that help large projects (e.g. Kubernetes) use CBOR as an alternative to JSON and Protocol Buffers. Other improvements include speedups, improved memory use, bug fixes, new serialization options, etc. It passed fuzz tests (5+ billion executions) and is production quality.
|
||||
|
||||
[v2.6.0](https://github.com/fxamacker/cbor/releases/tag/v2.6.0) (February 2024) adds important new features, optimizations, and bug fixes. It is especially useful to systems that need to convert data between CBOR and JSON. New options and optimizations improve handling of bignum, integers, maps, and strings.
|
||||
|
||||
v2.5.0 was released on Sunday, August 13, 2023 with new features and important bug fixes. It is fuzz tested and production quality after extended beta [v2.5.0-beta](https://github.com/fxamacker/cbor/releases/tag/v2.5.0-beta) (Dec 2022) -> [v2.5.0](https://github.com/fxamacker/cbor/releases/tag/v2.5.0) (Aug 2023).
|
||||
[v2.5.0](https://github.com/fxamacker/cbor/releases/tag/v2.5.0) was released on Sunday, August 13, 2023 with new features and important bug fixes. It is fuzz tested and production quality after extended beta [v2.5.0-beta](https://github.com/fxamacker/cbor/releases/tag/v2.5.0-beta) (Dec 2022) -> [v2.5.0](https://github.com/fxamacker/cbor/releases/tag/v2.5.0) (Aug 2023).
|
||||
|
||||
__IMPORTANT__: 👉 Before upgrading from v2.4 or older release, please read the notable changes highlighted in the release notes. v2.5.0 is a large release with bug fixes to error handling for extraneous data in `Unmarshal`, etc. that should be reviewed before upgrading.
|
||||
|
||||
@@ -489,7 +732,7 @@ See [v2.5.0 release notes](https://github.com/fxamacker/cbor/releases/tag/v2.5.0
|
||||
See ["Version and API Changes"](https://github.com/fxamacker/cbor#versions-and-api-changes) section for more info about version numbering, etc.
|
||||
|
||||
<!--
|
||||
<details><summary>👉 Benchmark Comparison: v2.4.0 vs v2.5.0</summary><p/>
|
||||
<details><summary> 🔎 Benchmark Comparison: v2.4.0 vs v2.5.0</summary><p/>
|
||||
|
||||
TODO: Update to v2.4.0 vs 2.5.0 (not beta2).
|
||||
|
||||
@@ -549,9 +792,9 @@ geomean 2.782
|
||||
|
||||
## Who uses fxamacker/cbor
|
||||
|
||||
`fxamacker/cbor` is used in projects by Arm Ltd., Berlin Institute of Health at Charité, Chainlink, Cisco, Confidential Computing Consortium, ConsenSys, Dapper Labs, EdgeX Foundry, F5, FIDO Alliance, Fraunhofer‑AISEC, Kubernetes, Let's Encrypt (ISRG), Linux Foundation, Matrix.org, Microsoft, Mozilla, National Cybersecurity Agency of France (govt), Netherlands (govt), Oasis Protocol, Smallstep, Tailscale, Taurus SA, Teleport, TIBCO, and others.
|
||||
`fxamacker/cbor` is used in projects by Arm Ltd., Berlin Institute of Health at Charité, Chainlink, Confidential Computing Consortium, ConsenSys, EdgeX Foundry, F5, Flow Foundation, Fraunhofer‑AISEC, IBM, Kubernetes, Let's Encrypt (ISRG), Linaro, Linux Foundation, Matrix.org, Microsoft, National Cybersecurity Agency of France (govt), Netherlands (govt), Oasis Protocol, Red Hat OpenShift, Smallstep, Tailscale, Taurus SA, TIBCO, Veraison, and others.
|
||||
|
||||
`fxamacker/cbor` passed multiple confidential security assessments. A [nonconfidential security assessment](https://github.com/veraison/go-cose/blob/v1.0.0-rc.1/reports/NCC_Microsoft-go-cose-Report_2022-05-26_v1.0.pdf) (prepared by NCC Group for Microsoft Corporation) includes a subset of fxamacker/cbor v2.4.0 in its scope.
|
||||
`fxamacker/cbor` passed multiple confidential security assessments in 2022. A [nonconfidential security assessment](https://github.com/veraison/go-cose/blob/v1.0.0-rc.1/reports/NCC_Microsoft-go-cose-Report_2022-05-26_v1.0.pdf) (prepared by NCC Group for Microsoft Corporation) assessed a subset of fxamacker/cbor v2.4.
|
||||
|
||||
## Standards
|
||||
|
||||
@@ -588,7 +831,7 @@ By default, decoder treats time values of floating-point NaN and Infinity as if
|
||||
__Click to expand topic:__
|
||||
|
||||
<details>
|
||||
<summary>Duplicate Map Keys</summary><p>
|
||||
<summary> 🔎 Duplicate Map Keys</summary><p>
|
||||
|
||||
This library provides options for fast detection and rejection of duplicate map keys based on applying a Go-specific data model to CBOR's extended generic data model in order to determine duplicate vs distinct map keys. Detection relies on whether the CBOR map key would be a duplicate "key" when decoded and applied to the user-provided Go map or struct.
|
||||
|
||||
@@ -601,7 +844,7 @@ APF suffix means "Allow Partial Fill" so the destination map or struct can conta
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Tag Validity</summary><p>
|
||||
<summary> 🔎 Tag Validity</summary><p>
|
||||
|
||||
This library checks tag validity for built-in tags (currently tag numbers 0, 1, 2, 3, and 55799):
|
||||
|
||||
|
||||
+27
@@ -38,11 +38,38 @@ func (bs ByteString) MarshalCBOR() ([]byte, error) {
|
||||
|
||||
// UnmarshalCBOR decodes CBOR byte string (major type 2) to ByteString.
|
||||
// Decoding CBOR null and CBOR undefined sets ByteString to be empty.
|
||||
//
|
||||
// Deprecated: No longer used by this codec; kept for compatibility
|
||||
// with user apps that directly call this function.
|
||||
func (bs *ByteString) UnmarshalCBOR(data []byte) error {
|
||||
if bs == nil {
|
||||
return errors.New("cbor.ByteString: UnmarshalCBOR on nil pointer")
|
||||
}
|
||||
|
||||
d := decoder{data: data, dm: defaultDecMode}
|
||||
|
||||
// Check well-formedness of CBOR data item.
|
||||
// ByteString.UnmarshalCBOR() is exported, so
|
||||
// the codec needs to support same behavior for:
|
||||
// - Unmarshal(data, *ByteString)
|
||||
// - ByteString.UnmarshalCBOR(data)
|
||||
err := d.wellformed(false, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bs.unmarshalCBOR(data)
|
||||
}
|
||||
|
||||
// unmarshalCBOR decodes CBOR byte string (major type 2) to ByteString.
|
||||
// Decoding CBOR null and CBOR undefined sets ByteString to be empty.
|
||||
// This function assumes data is well-formed, and does not perform bounds checking.
|
||||
// This function is called by Unmarshal().
|
||||
func (bs *ByteString) unmarshalCBOR(data []byte) error {
|
||||
if bs == nil {
|
||||
return errors.New("cbor.ByteString: UnmarshalCBOR on nil pointer")
|
||||
}
|
||||
|
||||
// Decoding CBOR null and CBOR undefined to ByteString resets data.
|
||||
// This behavior is similar to decoding CBOR null and CBOR undefined to []byte.
|
||||
if len(data) == 1 && (data[0] == 0xf6 || data[0] == 0xf7) {
|
||||
|
||||
+16
-9
@@ -17,6 +17,7 @@ import (
|
||||
type encodeFuncs struct {
|
||||
ef encodeFunc
|
||||
ief isEmptyFunc
|
||||
izf isZeroFunc
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -31,10 +32,12 @@ type specialType int
|
||||
const (
|
||||
specialTypeNone specialType = iota
|
||||
specialTypeUnmarshalerIface
|
||||
specialTypeUnexportedUnmarshalerIface
|
||||
specialTypeEmptyIface
|
||||
specialTypeIface
|
||||
specialTypeTag
|
||||
specialTypeTime
|
||||
specialTypeJSONUnmarshalerIface
|
||||
)
|
||||
|
||||
type typeInfo struct {
|
||||
@@ -50,7 +53,7 @@ type typeInfo struct {
|
||||
func newTypeInfo(t reflect.Type) *typeInfo {
|
||||
tInfo := typeInfo{typ: t, kind: t.Kind()}
|
||||
|
||||
for t.Kind() == reflect.Ptr {
|
||||
for t.Kind() == reflect.Pointer {
|
||||
t = t.Elem()
|
||||
}
|
||||
|
||||
@@ -69,8 +72,12 @@ func newTypeInfo(t reflect.Type) *typeInfo {
|
||||
tInfo.spclType = specialTypeTag
|
||||
} else if t == typeTime {
|
||||
tInfo.spclType = specialTypeTime
|
||||
} else if reflect.PtrTo(t).Implements(typeUnmarshaler) {
|
||||
} else if reflect.PointerTo(t).Implements(typeUnexportedUnmarshaler) {
|
||||
tInfo.spclType = specialTypeUnexportedUnmarshalerIface
|
||||
} else if reflect.PointerTo(t).Implements(typeUnmarshaler) {
|
||||
tInfo.spclType = specialTypeUnmarshalerIface
|
||||
} else if reflect.PointerTo(t).Implements(typeJSONUnmarshaler) {
|
||||
tInfo.spclType = specialTypeJSONUnmarshalerIface
|
||||
}
|
||||
|
||||
switch k {
|
||||
@@ -237,7 +244,7 @@ func getEncodingStructType(t reflect.Type) (*encodingStructType, error) {
|
||||
e := getEncodeBuffer()
|
||||
for i := 0; i < len(flds); i++ {
|
||||
// Get field's encodeFunc
|
||||
flds[i].ef, flds[i].ief = getEncodeFunc(flds[i].typ)
|
||||
flds[i].ef, flds[i].ief, flds[i].izf = getEncodeFunc(flds[i].typ)
|
||||
if flds[i].ef == nil {
|
||||
err = &UnsupportedTypeError{t}
|
||||
break
|
||||
@@ -321,7 +328,7 @@ func getEncodingStructType(t reflect.Type) (*encodingStructType, error) {
|
||||
func getEncodingStructToArrayType(t reflect.Type, flds fields) (*encodingStructType, error) {
|
||||
for i := 0; i < len(flds); i++ {
|
||||
// Get field's encodeFunc
|
||||
flds[i].ef, flds[i].ief = getEncodeFunc(flds[i].typ)
|
||||
flds[i].ef, flds[i].ief, flds[i].izf = getEncodeFunc(flds[i].typ)
|
||||
if flds[i].ef == nil {
|
||||
structType := &encodingStructType{err: &UnsupportedTypeError{t}}
|
||||
encodingStructTypeCache.Store(t, structType)
|
||||
@@ -337,14 +344,14 @@ func getEncodingStructToArrayType(t reflect.Type, flds fields) (*encodingStructT
|
||||
return structType, structType.err
|
||||
}
|
||||
|
||||
func getEncodeFunc(t reflect.Type) (encodeFunc, isEmptyFunc) {
|
||||
func getEncodeFunc(t reflect.Type) (encodeFunc, isEmptyFunc, isZeroFunc) {
|
||||
if v, _ := encodeFuncCache.Load(t); v != nil {
|
||||
fs := v.(encodeFuncs)
|
||||
return fs.ef, fs.ief
|
||||
return fs.ef, fs.ief, fs.izf
|
||||
}
|
||||
ef, ief := getEncodeFuncInternal(t)
|
||||
encodeFuncCache.Store(t, encodeFuncs{ef, ief})
|
||||
return ef, ief
|
||||
ef, ief, izf := getEncodeFuncInternal(t)
|
||||
encodeFuncCache.Store(t, encodeFuncs{ef, ief, izf})
|
||||
return ef, ief, izf
|
||||
}
|
||||
|
||||
func getTypeInfo(t reflect.Type) *typeInfo {
|
||||
|
||||
+9
@@ -5,6 +5,7 @@ package cbor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
@@ -180,3 +181,11 @@ func validBuiltinTag(tagNum uint64, contentHead byte) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transcoder is a scheme for transcoding a single CBOR encoded data item to or from a different
|
||||
// data format.
|
||||
type Transcoder interface {
|
||||
// Transcode reads the data item in its source format from a Reader and writes a
|
||||
// corresponding representation in its destination format to a Writer.
|
||||
Transcode(dst io.Writer, src io.Reader) error
|
||||
}
|
||||
|
||||
+278
-147
@@ -4,6 +4,7 @@
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
@@ -94,7 +95,7 @@ import (
|
||||
//
|
||||
// To unmarshal CBOR null (0xf6) and undefined (0xf7) values into a
|
||||
// slice/map/pointer, Unmarshal sets Go value to nil. Because null is often
|
||||
// used to mean "not present", unmarshalling CBOR null and undefined value
|
||||
// used to mean "not present", unmarshaling CBOR null and undefined value
|
||||
// into any other Go type has no effect and returns no error.
|
||||
//
|
||||
// Unmarshal supports CBOR tag 55799 (self-describe CBOR), tag 0 and 1 (time),
|
||||
@@ -104,7 +105,7 @@ import (
|
||||
// if there are any remaining bytes following the first valid CBOR data item.
|
||||
// See UnmarshalFirst, if you want to unmarshal only the first
|
||||
// CBOR data item without ExtraneousDataError caused by remaining bytes.
|
||||
func Unmarshal(data []byte, v interface{}) error {
|
||||
func Unmarshal(data []byte, v any) error {
|
||||
return defaultDecMode.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
@@ -114,7 +115,7 @@ func Unmarshal(data []byte, v interface{}) error {
|
||||
// If v is nil, not a pointer, or a nil pointer, UnmarshalFirst returns an error.
|
||||
//
|
||||
// See the documentation for Unmarshal for details.
|
||||
func UnmarshalFirst(data []byte, v interface{}) (rest []byte, err error) {
|
||||
func UnmarshalFirst(data []byte, v any) (rest []byte, err error) {
|
||||
return defaultDecMode.UnmarshalFirst(data, v)
|
||||
}
|
||||
|
||||
@@ -151,6 +152,10 @@ type Unmarshaler interface {
|
||||
UnmarshalCBOR([]byte) error
|
||||
}
|
||||
|
||||
type unmarshaler interface {
|
||||
unmarshalCBOR([]byte) error
|
||||
}
|
||||
|
||||
// InvalidUnmarshalError describes an invalid argument passed to Unmarshal.
|
||||
type InvalidUnmarshalError struct {
|
||||
s string
|
||||
@@ -193,12 +198,12 @@ func (e *InvalidMapKeyTypeError) Error() string {
|
||||
|
||||
// DupMapKeyError describes detected duplicate map key in CBOR map.
|
||||
type DupMapKeyError struct {
|
||||
Key interface{}
|
||||
Key any
|
||||
Index int
|
||||
}
|
||||
|
||||
func (e *DupMapKeyError) Error() string {
|
||||
return fmt.Sprintf("cbor: found duplicate map key \"%v\" at map element index %d", e.Key, e.Index)
|
||||
return fmt.Sprintf("cbor: found duplicate map key %#v at map element index %d", e.Key, e.Index)
|
||||
}
|
||||
|
||||
// UnknownFieldError describes detected unknown field in CBOR map when decoding to Go struct.
|
||||
@@ -383,7 +388,7 @@ const (
|
||||
// - return UnmarshalTypeError if value doesn't fit into int64
|
||||
IntDecConvertSignedOrFail
|
||||
|
||||
// IntDecConvertSigned affects how CBOR integers (major type 0 and 1) decode to Go interface{}.
|
||||
// IntDecConvertSignedOrBigInt affects how CBOR integers (major type 0 and 1) decode to Go interface{}.
|
||||
// It makes CBOR integers (major type 0 and 1) decode to:
|
||||
// - int64 if value fits
|
||||
// - big.Int or *big.Int (see BigIntDecMode) if value doesn't fit into int64
|
||||
@@ -489,11 +494,11 @@ type BigIntDecMode int
|
||||
|
||||
const (
|
||||
// BigIntDecodeValue makes CBOR bignum decode to big.Int (instead of *big.Int)
|
||||
// when unmarshalling into a Go interface{}.
|
||||
// when unmarshaling into a Go interface{}.
|
||||
BigIntDecodeValue BigIntDecMode = iota
|
||||
|
||||
// BigIntDecodePointer makes CBOR bignum decode to *big.Int when
|
||||
// unmarshalling into a Go interface{}.
|
||||
// unmarshaling into a Go interface{}.
|
||||
BigIntDecodePointer
|
||||
|
||||
maxBigIntDecMode
|
||||
@@ -745,6 +750,25 @@ func (bum BinaryUnmarshalerMode) valid() bool {
|
||||
return bum >= 0 && bum < maxBinaryUnmarshalerMode
|
||||
}
|
||||
|
||||
// TextUnmarshalerMode specifies how to decode into types that implement
|
||||
// encoding.TextUnmarshaler.
|
||||
type TextUnmarshalerMode int
|
||||
|
||||
const (
|
||||
// TextUnmarshalerNone does not recognize TextUnmarshaler implementations during decode.
|
||||
TextUnmarshalerNone TextUnmarshalerMode = iota
|
||||
|
||||
// TextUnmarshalerTextString will invoke UnmarshalText on the contents of a CBOR text
|
||||
// string when decoding into a value that implements TextUnmarshaler.
|
||||
TextUnmarshalerTextString
|
||||
|
||||
maxTextUnmarshalerMode
|
||||
)
|
||||
|
||||
func (tum TextUnmarshalerMode) valid() bool {
|
||||
return tum >= 0 && tum < maxTextUnmarshalerMode
|
||||
}
|
||||
|
||||
// DecOptions specifies decoding options.
|
||||
type DecOptions struct {
|
||||
// DupMapKey specifies whether to enforce duplicate map key.
|
||||
@@ -793,7 +817,7 @@ type DecOptions struct {
|
||||
// TagsMd specifies whether to allow CBOR tags (major type 6).
|
||||
TagsMd TagsMode
|
||||
|
||||
// IntDec specifies which Go integer type (int64 or uint64) to use
|
||||
// IntDec specifies which Go integer type (int64, uint64, or [big.Int]) to use
|
||||
// when decoding CBOR int (major type 0 and 1) to Go interface{}.
|
||||
IntDec IntDecMode
|
||||
|
||||
@@ -807,7 +831,7 @@ type DecOptions struct {
|
||||
ExtraReturnErrors ExtraDecErrorCond
|
||||
|
||||
// DefaultMapType specifies Go map type to create and decode to
|
||||
// when unmarshalling CBOR into an empty interface value.
|
||||
// when unmarshaling CBOR into an empty interface value.
|
||||
// By default, unmarshal uses map[interface{}]interface{}.
|
||||
DefaultMapType reflect.Type
|
||||
|
||||
@@ -879,6 +903,15 @@ type DecOptions struct {
|
||||
// BinaryUnmarshaler specifies how to decode into types that implement
|
||||
// encoding.BinaryUnmarshaler.
|
||||
BinaryUnmarshaler BinaryUnmarshalerMode
|
||||
|
||||
// TextUnmarshaler specifies how to decode into types that implement
|
||||
// encoding.TextUnmarshaler.
|
||||
TextUnmarshaler TextUnmarshalerMode
|
||||
|
||||
// JSONUnmarshalerTranscoder sets the transcoding scheme used to unmarshal types that
|
||||
// implement json.Unmarshaler but do not also implement cbor.Unmarshaler. If nil, decoding
|
||||
// behavior is not influenced by whether or not a type implements json.Unmarshaler.
|
||||
JSONUnmarshalerTranscoder Transcoder
|
||||
}
|
||||
|
||||
// DecMode returns DecMode with immutable options and no tags (safe for concurrency).
|
||||
@@ -1091,33 +1124,39 @@ func (opts DecOptions) decMode() (*decMode, error) { //nolint:gocritic // ignore
|
||||
return nil, errors.New("cbor: invalid BinaryUnmarshaler " + strconv.Itoa(int(opts.BinaryUnmarshaler)))
|
||||
}
|
||||
|
||||
if !opts.TextUnmarshaler.valid() {
|
||||
return nil, errors.New("cbor: invalid TextUnmarshaler " + strconv.Itoa(int(opts.TextUnmarshaler)))
|
||||
}
|
||||
|
||||
dm := decMode{
|
||||
dupMapKey: opts.DupMapKey,
|
||||
timeTag: opts.TimeTag,
|
||||
maxNestedLevels: opts.MaxNestedLevels,
|
||||
maxArrayElements: opts.MaxArrayElements,
|
||||
maxMapPairs: opts.MaxMapPairs,
|
||||
indefLength: opts.IndefLength,
|
||||
tagsMd: opts.TagsMd,
|
||||
intDec: opts.IntDec,
|
||||
mapKeyByteString: opts.MapKeyByteString,
|
||||
extraReturnErrors: opts.ExtraReturnErrors,
|
||||
defaultMapType: opts.DefaultMapType,
|
||||
utf8: opts.UTF8,
|
||||
fieldNameMatching: opts.FieldNameMatching,
|
||||
bigIntDec: opts.BigIntDec,
|
||||
defaultByteStringType: opts.DefaultByteStringType,
|
||||
byteStringToString: opts.ByteStringToString,
|
||||
fieldNameByteString: opts.FieldNameByteString,
|
||||
unrecognizedTagToAny: opts.UnrecognizedTagToAny,
|
||||
timeTagToAny: opts.TimeTagToAny,
|
||||
simpleValues: simpleValues,
|
||||
nanDec: opts.NaN,
|
||||
infDec: opts.Inf,
|
||||
byteStringToTime: opts.ByteStringToTime,
|
||||
byteStringExpectedFormat: opts.ByteStringExpectedFormat,
|
||||
bignumTag: opts.BignumTag,
|
||||
binaryUnmarshaler: opts.BinaryUnmarshaler,
|
||||
dupMapKey: opts.DupMapKey,
|
||||
timeTag: opts.TimeTag,
|
||||
maxNestedLevels: opts.MaxNestedLevels,
|
||||
maxArrayElements: opts.MaxArrayElements,
|
||||
maxMapPairs: opts.MaxMapPairs,
|
||||
indefLength: opts.IndefLength,
|
||||
tagsMd: opts.TagsMd,
|
||||
intDec: opts.IntDec,
|
||||
mapKeyByteString: opts.MapKeyByteString,
|
||||
extraReturnErrors: opts.ExtraReturnErrors,
|
||||
defaultMapType: opts.DefaultMapType,
|
||||
utf8: opts.UTF8,
|
||||
fieldNameMatching: opts.FieldNameMatching,
|
||||
bigIntDec: opts.BigIntDec,
|
||||
defaultByteStringType: opts.DefaultByteStringType,
|
||||
byteStringToString: opts.ByteStringToString,
|
||||
fieldNameByteString: opts.FieldNameByteString,
|
||||
unrecognizedTagToAny: opts.UnrecognizedTagToAny,
|
||||
timeTagToAny: opts.TimeTagToAny,
|
||||
simpleValues: simpleValues,
|
||||
nanDec: opts.NaN,
|
||||
infDec: opts.Inf,
|
||||
byteStringToTime: opts.ByteStringToTime,
|
||||
byteStringExpectedFormat: opts.ByteStringExpectedFormat,
|
||||
bignumTag: opts.BignumTag,
|
||||
binaryUnmarshaler: opts.BinaryUnmarshaler,
|
||||
textUnmarshaler: opts.TextUnmarshaler,
|
||||
jsonUnmarshalerTranscoder: opts.JSONUnmarshalerTranscoder,
|
||||
}
|
||||
|
||||
return &dm, nil
|
||||
@@ -1130,7 +1169,7 @@ type DecMode interface {
|
||||
// Unmarshal returns an error.
|
||||
//
|
||||
// See the documentation for Unmarshal for details.
|
||||
Unmarshal(data []byte, v interface{}) error
|
||||
Unmarshal(data []byte, v any) error
|
||||
|
||||
// UnmarshalFirst parses the first CBOR data item into the value pointed to by v
|
||||
// using the decoding mode. Any remaining bytes are returned in rest.
|
||||
@@ -1138,7 +1177,7 @@ type DecMode interface {
|
||||
// If v is nil, not a pointer, or a nil pointer, UnmarshalFirst returns an error.
|
||||
//
|
||||
// See the documentation for Unmarshal for details.
|
||||
UnmarshalFirst(data []byte, v interface{}) (rest []byte, err error)
|
||||
UnmarshalFirst(data []byte, v any) (rest []byte, err error)
|
||||
|
||||
// Valid checks whether data is a well-formed encoded CBOR data item and
|
||||
// that it complies with configurable restrictions such as MaxNestedLevels,
|
||||
@@ -1170,33 +1209,35 @@ type DecMode interface {
|
||||
}
|
||||
|
||||
type decMode struct {
|
||||
tags tagProvider
|
||||
dupMapKey DupMapKeyMode
|
||||
timeTag DecTagMode
|
||||
maxNestedLevels int
|
||||
maxArrayElements int
|
||||
maxMapPairs int
|
||||
indefLength IndefLengthMode
|
||||
tagsMd TagsMode
|
||||
intDec IntDecMode
|
||||
mapKeyByteString MapKeyByteStringMode
|
||||
extraReturnErrors ExtraDecErrorCond
|
||||
defaultMapType reflect.Type
|
||||
utf8 UTF8Mode
|
||||
fieldNameMatching FieldNameMatchingMode
|
||||
bigIntDec BigIntDecMode
|
||||
defaultByteStringType reflect.Type
|
||||
byteStringToString ByteStringToStringMode
|
||||
fieldNameByteString FieldNameByteStringMode
|
||||
unrecognizedTagToAny UnrecognizedTagToAnyMode
|
||||
timeTagToAny TimeTagToAnyMode
|
||||
simpleValues *SimpleValueRegistry
|
||||
nanDec NaNMode
|
||||
infDec InfMode
|
||||
byteStringToTime ByteStringToTimeMode
|
||||
byteStringExpectedFormat ByteStringExpectedFormatMode
|
||||
bignumTag BignumTagMode
|
||||
binaryUnmarshaler BinaryUnmarshalerMode
|
||||
tags tagProvider
|
||||
dupMapKey DupMapKeyMode
|
||||
timeTag DecTagMode
|
||||
maxNestedLevels int
|
||||
maxArrayElements int
|
||||
maxMapPairs int
|
||||
indefLength IndefLengthMode
|
||||
tagsMd TagsMode
|
||||
intDec IntDecMode
|
||||
mapKeyByteString MapKeyByteStringMode
|
||||
extraReturnErrors ExtraDecErrorCond
|
||||
defaultMapType reflect.Type
|
||||
utf8 UTF8Mode
|
||||
fieldNameMatching FieldNameMatchingMode
|
||||
bigIntDec BigIntDecMode
|
||||
defaultByteStringType reflect.Type
|
||||
byteStringToString ByteStringToStringMode
|
||||
fieldNameByteString FieldNameByteStringMode
|
||||
unrecognizedTagToAny UnrecognizedTagToAnyMode
|
||||
timeTagToAny TimeTagToAnyMode
|
||||
simpleValues *SimpleValueRegistry
|
||||
nanDec NaNMode
|
||||
infDec InfMode
|
||||
byteStringToTime ByteStringToTimeMode
|
||||
byteStringExpectedFormat ByteStringExpectedFormatMode
|
||||
bignumTag BignumTagMode
|
||||
binaryUnmarshaler BinaryUnmarshalerMode
|
||||
textUnmarshaler TextUnmarshalerMode
|
||||
jsonUnmarshalerTranscoder Transcoder
|
||||
}
|
||||
|
||||
var defaultDecMode, _ = DecOptions{}.decMode()
|
||||
@@ -1211,32 +1252,34 @@ func (dm *decMode) DecOptions() DecOptions {
|
||||
}
|
||||
|
||||
return DecOptions{
|
||||
DupMapKey: dm.dupMapKey,
|
||||
TimeTag: dm.timeTag,
|
||||
MaxNestedLevels: dm.maxNestedLevels,
|
||||
MaxArrayElements: dm.maxArrayElements,
|
||||
MaxMapPairs: dm.maxMapPairs,
|
||||
IndefLength: dm.indefLength,
|
||||
TagsMd: dm.tagsMd,
|
||||
IntDec: dm.intDec,
|
||||
MapKeyByteString: dm.mapKeyByteString,
|
||||
ExtraReturnErrors: dm.extraReturnErrors,
|
||||
DefaultMapType: dm.defaultMapType,
|
||||
UTF8: dm.utf8,
|
||||
FieldNameMatching: dm.fieldNameMatching,
|
||||
BigIntDec: dm.bigIntDec,
|
||||
DefaultByteStringType: dm.defaultByteStringType,
|
||||
ByteStringToString: dm.byteStringToString,
|
||||
FieldNameByteString: dm.fieldNameByteString,
|
||||
UnrecognizedTagToAny: dm.unrecognizedTagToAny,
|
||||
TimeTagToAny: dm.timeTagToAny,
|
||||
SimpleValues: simpleValues,
|
||||
NaN: dm.nanDec,
|
||||
Inf: dm.infDec,
|
||||
ByteStringToTime: dm.byteStringToTime,
|
||||
ByteStringExpectedFormat: dm.byteStringExpectedFormat,
|
||||
BignumTag: dm.bignumTag,
|
||||
BinaryUnmarshaler: dm.binaryUnmarshaler,
|
||||
DupMapKey: dm.dupMapKey,
|
||||
TimeTag: dm.timeTag,
|
||||
MaxNestedLevels: dm.maxNestedLevels,
|
||||
MaxArrayElements: dm.maxArrayElements,
|
||||
MaxMapPairs: dm.maxMapPairs,
|
||||
IndefLength: dm.indefLength,
|
||||
TagsMd: dm.tagsMd,
|
||||
IntDec: dm.intDec,
|
||||
MapKeyByteString: dm.mapKeyByteString,
|
||||
ExtraReturnErrors: dm.extraReturnErrors,
|
||||
DefaultMapType: dm.defaultMapType,
|
||||
UTF8: dm.utf8,
|
||||
FieldNameMatching: dm.fieldNameMatching,
|
||||
BigIntDec: dm.bigIntDec,
|
||||
DefaultByteStringType: dm.defaultByteStringType,
|
||||
ByteStringToString: dm.byteStringToString,
|
||||
FieldNameByteString: dm.fieldNameByteString,
|
||||
UnrecognizedTagToAny: dm.unrecognizedTagToAny,
|
||||
TimeTagToAny: dm.timeTagToAny,
|
||||
SimpleValues: simpleValues,
|
||||
NaN: dm.nanDec,
|
||||
Inf: dm.infDec,
|
||||
ByteStringToTime: dm.byteStringToTime,
|
||||
ByteStringExpectedFormat: dm.byteStringExpectedFormat,
|
||||
BignumTag: dm.bignumTag,
|
||||
BinaryUnmarshaler: dm.binaryUnmarshaler,
|
||||
TextUnmarshaler: dm.textUnmarshaler,
|
||||
JSONUnmarshalerTranscoder: dm.jsonUnmarshalerTranscoder,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1245,7 +1288,7 @@ func (dm *decMode) DecOptions() DecOptions {
|
||||
// Unmarshal returns an error.
|
||||
//
|
||||
// See the documentation for Unmarshal for details.
|
||||
func (dm *decMode) Unmarshal(data []byte, v interface{}) error {
|
||||
func (dm *decMode) Unmarshal(data []byte, v any) error {
|
||||
d := decoder{data: data, dm: dm}
|
||||
|
||||
// Check well-formedness.
|
||||
@@ -1265,7 +1308,7 @@ func (dm *decMode) Unmarshal(data []byte, v interface{}) error {
|
||||
// If v is nil, not a pointer, or a nil pointer, UnmarshalFirst returns an error.
|
||||
//
|
||||
// See the documentation for Unmarshal for details.
|
||||
func (dm *decMode) UnmarshalFirst(data []byte, v interface{}) (rest []byte, err error) {
|
||||
func (dm *decMode) UnmarshalFirst(data []byte, v any) (rest []byte, err error) {
|
||||
d := decoder{data: data, dm: dm}
|
||||
|
||||
// check well-formedness.
|
||||
@@ -1341,13 +1384,13 @@ type decoder struct {
|
||||
// If CBOR data item fails to be decoded into v,
|
||||
// error is returned and offset is moved to the next CBOR data item.
|
||||
// Precondition: d.data contains at least one well-formed CBOR data item.
|
||||
func (d *decoder) value(v interface{}) error {
|
||||
func (d *decoder) value(v any) error {
|
||||
// v can't be nil, non-pointer, or nil pointer value.
|
||||
if v == nil {
|
||||
return &InvalidUnmarshalError{"cbor: Unmarshal(nil)"}
|
||||
}
|
||||
rv := reflect.ValueOf(v)
|
||||
if rv.Kind() != reflect.Ptr {
|
||||
if rv.Kind() != reflect.Pointer {
|
||||
return &InvalidUnmarshalError{"cbor: Unmarshal(non-pointer " + rv.Type().String() + ")"}
|
||||
} else if rv.IsNil() {
|
||||
return &InvalidUnmarshalError{"cbor: Unmarshal(nil " + rv.Type().String() + ")"}
|
||||
@@ -1361,9 +1404,9 @@ func (d *decoder) value(v interface{}) error {
|
||||
func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolint:gocyclo
|
||||
|
||||
// Decode CBOR nil or CBOR undefined to pointer value by setting pointer value to nil.
|
||||
if d.nextCBORNil() && v.Kind() == reflect.Ptr {
|
||||
if d.nextCBORNil() && v.Kind() == reflect.Pointer {
|
||||
d.skip()
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
v.SetZero()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1387,7 +1430,7 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
|
||||
registeredType := d.dm.tags.getTypeFromTagNum(tagNums)
|
||||
if registeredType != nil {
|
||||
if registeredType.Implements(tInfo.nonPtrType) ||
|
||||
reflect.PtrTo(registeredType).Implements(tInfo.nonPtrType) {
|
||||
reflect.PointerTo(registeredType).Implements(tInfo.nonPtrType) {
|
||||
v.Set(reflect.New(registeredType))
|
||||
v = v.Elem()
|
||||
tInfo = getTypeInfo(registeredType)
|
||||
@@ -1399,7 +1442,7 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
|
||||
|
||||
// Create new value for the pointer v to point to.
|
||||
// At this point, CBOR value is not nil/undefined if v is a pointer.
|
||||
for v.Kind() == reflect.Ptr {
|
||||
for v.Kind() == reflect.Pointer {
|
||||
if v.IsNil() {
|
||||
if !v.CanSet() {
|
||||
d.skip()
|
||||
@@ -1460,6 +1503,17 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
|
||||
|
||||
case specialTypeUnmarshalerIface:
|
||||
return d.parseToUnmarshaler(v)
|
||||
|
||||
case specialTypeUnexportedUnmarshalerIface:
|
||||
return d.parseToUnexportedUnmarshaler(v)
|
||||
|
||||
case specialTypeJSONUnmarshalerIface:
|
||||
// This special type implies that the type does not also implement
|
||||
// cbor.Umarshaler.
|
||||
if d.dm.jsonUnmarshalerTranscoder == nil {
|
||||
break
|
||||
}
|
||||
return d.parseToJSONUnmarshaler(v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1516,14 +1570,14 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
|
||||
return err
|
||||
}
|
||||
copied = copied || converted
|
||||
return fillByteString(t, b, !copied, v, d.dm.byteStringToString, d.dm.binaryUnmarshaler)
|
||||
return fillByteString(t, b, !copied, v, d.dm.byteStringToString, d.dm.binaryUnmarshaler, d.dm.textUnmarshaler)
|
||||
|
||||
case cborTypeTextString:
|
||||
b, err := d.parseTextString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fillTextString(t, b, v)
|
||||
return fillTextString(t, b, v, d.dm.textUnmarshaler)
|
||||
|
||||
case cborTypePrimitives:
|
||||
_, ai, val := d.getHead()
|
||||
@@ -1575,7 +1629,7 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
|
||||
return nil
|
||||
}
|
||||
if tInfo.nonPtrKind == reflect.Slice || tInfo.nonPtrKind == reflect.Array {
|
||||
return fillByteString(t, b, !copied, v, ByteStringToStringForbidden, d.dm.binaryUnmarshaler)
|
||||
return fillByteString(t, b, !copied, v, ByteStringToStringForbidden, d.dm.binaryUnmarshaler, d.dm.textUnmarshaler)
|
||||
}
|
||||
if bi.IsUint64() {
|
||||
return fillPositiveInt(t, bi.Uint64(), v)
|
||||
@@ -1598,7 +1652,7 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
|
||||
return nil
|
||||
}
|
||||
if tInfo.nonPtrKind == reflect.Slice || tInfo.nonPtrKind == reflect.Array {
|
||||
return fillByteString(t, b, !copied, v, ByteStringToStringForbidden, d.dm.binaryUnmarshaler)
|
||||
return fillByteString(t, b, !copied, v, ByteStringToStringForbidden, d.dm.binaryUnmarshaler, d.dm.textUnmarshaler)
|
||||
}
|
||||
if bi.IsInt64() {
|
||||
return fillNegativeInt(t, bi.Int64(), v)
|
||||
@@ -1788,12 +1842,12 @@ func (d *decoder) parseToTime() (time.Time, bool, error) {
|
||||
// parseToUnmarshaler parses CBOR data to value implementing Unmarshaler interface.
|
||||
// It assumes data is well-formed, and does not perform bounds checking.
|
||||
func (d *decoder) parseToUnmarshaler(v reflect.Value) error {
|
||||
if d.nextCBORNil() && v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
if d.nextCBORNil() && v.Kind() == reflect.Pointer && v.IsNil() {
|
||||
d.skip()
|
||||
return nil
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Ptr && v.CanAddr() {
|
||||
if v.Kind() != reflect.Pointer && v.CanAddr() {
|
||||
v = v.Addr()
|
||||
}
|
||||
if u, ok := v.Interface().(Unmarshaler); ok {
|
||||
@@ -1805,9 +1859,55 @@ func (d *decoder) parseToUnmarshaler(v reflect.Value) error {
|
||||
return errors.New("cbor: failed to assert " + v.Type().String() + " as cbor.Unmarshaler")
|
||||
}
|
||||
|
||||
// parseToUnexportedUnmarshaler parses CBOR data to value implementing unmarshaler interface.
|
||||
// It assumes data is well-formed, and does not perform bounds checking.
|
||||
func (d *decoder) parseToUnexportedUnmarshaler(v reflect.Value) error {
|
||||
if d.nextCBORNil() && v.Kind() == reflect.Pointer && v.IsNil() {
|
||||
d.skip()
|
||||
return nil
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Pointer && v.CanAddr() {
|
||||
v = v.Addr()
|
||||
}
|
||||
if u, ok := v.Interface().(unmarshaler); ok {
|
||||
start := d.off
|
||||
d.skip()
|
||||
return u.unmarshalCBOR(d.data[start:d.off])
|
||||
}
|
||||
d.skip()
|
||||
return errors.New("cbor: failed to assert " + v.Type().String() + " as cbor.unmarshaler")
|
||||
}
|
||||
|
||||
// parseToJSONUnmarshaler parses CBOR data to be transcoded to JSON and passed to the value's
|
||||
// implementation of the json.Unmarshaler interface. It assumes data is well-formed, and does not
|
||||
// perform bounds checking.
|
||||
func (d *decoder) parseToJSONUnmarshaler(v reflect.Value) error {
|
||||
if d.nextCBORNil() && v.Kind() == reflect.Pointer && v.IsNil() {
|
||||
d.skip()
|
||||
return nil
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Pointer && v.CanAddr() {
|
||||
v = v.Addr()
|
||||
}
|
||||
if u, ok := v.Interface().(jsonUnmarshaler); ok {
|
||||
start := d.off
|
||||
d.skip()
|
||||
e := getEncodeBuffer()
|
||||
defer putEncodeBuffer(e)
|
||||
if err := d.dm.jsonUnmarshalerTranscoder.Transcode(e, bytes.NewReader(d.data[start:d.off])); err != nil {
|
||||
return &TranscodeError{err: err, rtype: v.Type(), sourceFormat: "cbor", targetFormat: "json"}
|
||||
}
|
||||
return u.UnmarshalJSON(e.Bytes())
|
||||
}
|
||||
d.skip()
|
||||
return errors.New("cbor: failed to assert " + v.Type().String() + " as json.Unmarshaler")
|
||||
}
|
||||
|
||||
// parse parses CBOR data and returns value in default Go type.
|
||||
// It assumes data is well-formed, and does not perform bounds checking.
|
||||
func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //nolint:gocyclo
|
||||
func (d *decoder) parse(skipSelfDescribedTag bool) (any, error) { //nolint:gocyclo
|
||||
// Strip self-described CBOR tag number.
|
||||
if skipSelfDescribedTag {
|
||||
for d.nextCBORType() == cborTypeTag {
|
||||
@@ -2224,15 +2324,15 @@ func (d *decoder) parseTextString() ([]byte, error) {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (d *decoder) parseArray() ([]interface{}, error) {
|
||||
func (d *decoder) parseArray() ([]any, error) {
|
||||
_, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag()
|
||||
hasSize := !indefiniteLength
|
||||
count := int(val)
|
||||
if !hasSize {
|
||||
count = d.numOfItemsUntilBreak() // peek ahead to get array size to preallocate slice for better performance
|
||||
}
|
||||
v := make([]interface{}, count)
|
||||
var e interface{}
|
||||
v := make([]any, count)
|
||||
var e any
|
||||
var err, lastErr error
|
||||
for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
|
||||
if e, lastErr = d.parse(true); lastErr != nil {
|
||||
@@ -2290,20 +2390,19 @@ func (d *decoder) parseArrayToArray(v reflect.Value, tInfo *typeInfo) error {
|
||||
}
|
||||
// Set remaining Go array elements to zero values.
|
||||
if gi < vLen {
|
||||
zeroV := reflect.Zero(tInfo.elemTypeInfo.typ)
|
||||
for ; gi < vLen; gi++ {
|
||||
v.Index(gi).Set(zeroV)
|
||||
v.Index(gi).SetZero()
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *decoder) parseMap() (interface{}, error) {
|
||||
func (d *decoder) parseMap() (any, error) {
|
||||
_, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag()
|
||||
hasSize := !indefiniteLength
|
||||
count := int(val)
|
||||
m := make(map[interface{}]interface{})
|
||||
var k, e interface{}
|
||||
m := make(map[any]any)
|
||||
var k, e any
|
||||
var err, lastErr error
|
||||
keyCount := 0
|
||||
for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
|
||||
@@ -2376,13 +2475,13 @@ func (d *decoder) parseMapToMap(v reflect.Value, tInfo *typeInfo) error { //noli
|
||||
}
|
||||
keyType, eleType := tInfo.keyTypeInfo.typ, tInfo.elemTypeInfo.typ
|
||||
reuseKey, reuseEle := isImmutableKind(tInfo.keyTypeInfo.kind), isImmutableKind(tInfo.elemTypeInfo.kind)
|
||||
var keyValue, eleValue, zeroKeyValue, zeroEleValue reflect.Value
|
||||
var keyValue, eleValue reflect.Value
|
||||
keyIsInterfaceType := keyType == typeIntf // If key type is interface{}, need to check if key value is hashable.
|
||||
var err, lastErr error
|
||||
keyCount := v.Len()
|
||||
var existingKeys map[interface{}]bool // Store existing map keys, used for detecting duplicate map key.
|
||||
var existingKeys map[any]bool // Store existing map keys, used for detecting duplicate map key.
|
||||
if d.dm.dupMapKey == DupMapKeyEnforcedAPF {
|
||||
existingKeys = make(map[interface{}]bool, keyCount)
|
||||
existingKeys = make(map[any]bool, keyCount)
|
||||
if keyCount > 0 {
|
||||
vKeys := v.MapKeys()
|
||||
for i := 0; i < len(vKeys); i++ {
|
||||
@@ -2395,10 +2494,7 @@ func (d *decoder) parseMapToMap(v reflect.Value, tInfo *typeInfo) error { //noli
|
||||
if !keyValue.IsValid() {
|
||||
keyValue = reflect.New(keyType).Elem()
|
||||
} else if !reuseKey {
|
||||
if !zeroKeyValue.IsValid() {
|
||||
zeroKeyValue = reflect.Zero(keyType)
|
||||
}
|
||||
keyValue.Set(zeroKeyValue)
|
||||
keyValue.SetZero()
|
||||
}
|
||||
if lastErr = d.parseToValue(keyValue, tInfo.keyTypeInfo); lastErr != nil {
|
||||
if err == nil {
|
||||
@@ -2413,7 +2509,7 @@ func (d *decoder) parseMapToMap(v reflect.Value, tInfo *typeInfo) error { //noli
|
||||
if !isHashableValue(keyValue.Elem()) {
|
||||
var converted bool
|
||||
if d.dm.mapKeyByteString == MapKeyByteStringAllowed {
|
||||
var k interface{}
|
||||
var k any
|
||||
k, converted = convertByteSliceToByteString(keyValue.Elem().Interface())
|
||||
if converted {
|
||||
keyValue.Set(reflect.ValueOf(k))
|
||||
@@ -2433,10 +2529,7 @@ func (d *decoder) parseMapToMap(v reflect.Value, tInfo *typeInfo) error { //noli
|
||||
if !eleValue.IsValid() {
|
||||
eleValue = reflect.New(eleType).Elem()
|
||||
} else if !reuseEle {
|
||||
if !zeroEleValue.IsValid() {
|
||||
zeroEleValue = reflect.Zero(eleType)
|
||||
}
|
||||
eleValue.Set(zeroEleValue)
|
||||
eleValue.SetZero()
|
||||
}
|
||||
if lastErr := d.parseToValue(eleValue, tInfo.elemTypeInfo); lastErr != nil {
|
||||
if err == nil {
|
||||
@@ -2584,7 +2677,7 @@ func (d *decoder) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error { //n
|
||||
|
||||
// Keeps track of CBOR map keys to detect duplicate map key
|
||||
keyCount := 0
|
||||
var mapKeys map[interface{}]struct{}
|
||||
var mapKeys map[any]struct{}
|
||||
|
||||
errOnUnknownField := (d.dm.extraReturnErrors & ExtraDecErrorUnknownField) > 0
|
||||
|
||||
@@ -2594,7 +2687,7 @@ MapEntryLoop:
|
||||
|
||||
// If duplicate field detection is enabled and the key at index j did not match any
|
||||
// field, k will hold the map key.
|
||||
var k interface{}
|
||||
var k any
|
||||
|
||||
t := d.nextCBORType()
|
||||
if t == cborTypeTextString || (t == cborTypeByteString && d.dm.fieldNameByteString == FieldNameByteStringAllowed) {
|
||||
@@ -2764,7 +2857,7 @@ MapEntryLoop:
|
||||
// check is never reached.
|
||||
if d.dm.dupMapKey == DupMapKeyEnforcedAPF {
|
||||
if mapKeys == nil {
|
||||
mapKeys = make(map[interface{}]struct{}, 1)
|
||||
mapKeys = make(map[any]struct{}, 1)
|
||||
}
|
||||
mapKeys[k] = struct{}{}
|
||||
newKeyCount := len(mapKeys)
|
||||
@@ -2968,20 +3061,25 @@ func (d *decoder) nextCBORNil() bool {
|
||||
return d.data[d.off] == 0xf6 || d.data[d.off] == 0xf7
|
||||
}
|
||||
|
||||
type jsonUnmarshaler interface{ UnmarshalJSON([]byte) error }
|
||||
|
||||
var (
|
||||
typeIntf = reflect.TypeOf([]interface{}(nil)).Elem()
|
||||
typeTime = reflect.TypeOf(time.Time{})
|
||||
typeBigInt = reflect.TypeOf(big.Int{})
|
||||
typeUnmarshaler = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
|
||||
typeBinaryUnmarshaler = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem()
|
||||
typeString = reflect.TypeOf("")
|
||||
typeByteSlice = reflect.TypeOf([]byte(nil))
|
||||
typeIntf = reflect.TypeOf([]any(nil)).Elem()
|
||||
typeTime = reflect.TypeOf(time.Time{})
|
||||
typeBigInt = reflect.TypeOf(big.Int{})
|
||||
typeUnmarshaler = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
|
||||
typeUnexportedUnmarshaler = reflect.TypeOf((*unmarshaler)(nil)).Elem()
|
||||
typeBinaryUnmarshaler = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem()
|
||||
typeTextUnmarshaler = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
||||
typeJSONUnmarshaler = reflect.TypeOf((*jsonUnmarshaler)(nil)).Elem()
|
||||
typeString = reflect.TypeOf("")
|
||||
typeByteSlice = reflect.TypeOf([]byte(nil))
|
||||
)
|
||||
|
||||
func fillNil(_ cborType, v reflect.Value) error {
|
||||
switch v.Kind() {
|
||||
case reflect.Slice, reflect.Map, reflect.Interface, reflect.Ptr:
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
case reflect.Slice, reflect.Map, reflect.Interface, reflect.Pointer:
|
||||
v.SetZero()
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
@@ -3082,8 +3180,8 @@ func fillFloat(t cborType, val float64, v reflect.Value) error {
|
||||
return &UnmarshalTypeError{CBORType: t.String(), GoType: v.Type().String()}
|
||||
}
|
||||
|
||||
func fillByteString(t cborType, val []byte, shared bool, v reflect.Value, bsts ByteStringToStringMode, bum BinaryUnmarshalerMode) error {
|
||||
if bum == BinaryUnmarshalerByteString && reflect.PtrTo(v.Type()).Implements(typeBinaryUnmarshaler) {
|
||||
func fillByteString(t cborType, val []byte, shared bool, v reflect.Value, bsts ByteStringToStringMode, bum BinaryUnmarshalerMode, tum TextUnmarshalerMode) error {
|
||||
if bum == BinaryUnmarshalerByteString && reflect.PointerTo(v.Type()).Implements(typeBinaryUnmarshaler) {
|
||||
if v.CanAddr() {
|
||||
v = v.Addr()
|
||||
if u, ok := v.Interface().(encoding.BinaryUnmarshaler); ok {
|
||||
@@ -3095,9 +3193,26 @@ func fillByteString(t cborType, val []byte, shared bool, v reflect.Value, bsts B
|
||||
}
|
||||
return errors.New("cbor: cannot set new value for " + v.Type().String())
|
||||
}
|
||||
if bsts != ByteStringToStringForbidden && v.Kind() == reflect.String {
|
||||
v.SetString(string(val))
|
||||
return nil
|
||||
if bsts != ByteStringToStringForbidden {
|
||||
if tum == TextUnmarshalerTextString && reflect.PointerTo(v.Type()).Implements(typeTextUnmarshaler) {
|
||||
if v.CanAddr() {
|
||||
v = v.Addr()
|
||||
if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
|
||||
// The contract of TextUnmarshaler forbids retaining the input
|
||||
// bytes, so no copying is required even if val is shared.
|
||||
if err := u.UnmarshalText(val); err != nil {
|
||||
return fmt.Errorf("cbor: cannot unmarshal text for %s: %w", v.Type(), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("cbor: cannot set new value for " + v.Type().String())
|
||||
}
|
||||
|
||||
if v.Kind() == reflect.String {
|
||||
v.SetString(string(val))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8 {
|
||||
src := val
|
||||
@@ -3117,9 +3232,8 @@ func fillByteString(t cborType, val []byte, shared bool, v reflect.Value, bsts B
|
||||
}
|
||||
// Set remaining Go array elements to zero values.
|
||||
if i < vLen {
|
||||
zeroV := reflect.Zero(reflect.TypeOf(byte(0)))
|
||||
for ; i < vLen; i++ {
|
||||
v.Index(i).Set(zeroV)
|
||||
v.Index(i).SetZero()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -3127,11 +3241,28 @@ func fillByteString(t cborType, val []byte, shared bool, v reflect.Value, bsts B
|
||||
return &UnmarshalTypeError{CBORType: t.String(), GoType: v.Type().String()}
|
||||
}
|
||||
|
||||
func fillTextString(t cborType, val []byte, v reflect.Value) error {
|
||||
func fillTextString(t cborType, val []byte, v reflect.Value, tum TextUnmarshalerMode) error {
|
||||
// Check if the value implements TextUnmarshaler and the mode allows it
|
||||
if tum == TextUnmarshalerTextString && reflect.PointerTo(v.Type()).Implements(typeTextUnmarshaler) {
|
||||
if v.CanAddr() {
|
||||
v = v.Addr()
|
||||
if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
|
||||
// The contract of TextUnmarshaler forbids retaining the input
|
||||
// bytes, so no copying is required even if val is shared.
|
||||
if err := u.UnmarshalText(val); err != nil {
|
||||
return fmt.Errorf("cbor: cannot unmarshal text for %s: %w", v.Type(), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("cbor: cannot set new value for " + v.Type().String())
|
||||
}
|
||||
|
||||
if v.Kind() == reflect.String {
|
||||
v.SetString(string(val))
|
||||
return nil
|
||||
}
|
||||
|
||||
return &UnmarshalTypeError{CBORType: t.String(), GoType: v.Type().String()}
|
||||
}
|
||||
|
||||
@@ -3172,7 +3303,7 @@ func isHashableValue(rv reflect.Value) bool {
|
||||
// This function also handles nested tags.
|
||||
// CBOR data is already verified to be well-formed before this function is used,
|
||||
// so the recursion won't exceed max nested levels.
|
||||
func convertByteSliceToByteString(v interface{}) (interface{}, bool) {
|
||||
func convertByteSliceToByteString(v any) (any, bool) {
|
||||
switch v := v.(type) {
|
||||
case []byte:
|
||||
return ByteString(v), true
|
||||
|
||||
+37
-14
@@ -2,15 +2,15 @@
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
/*
|
||||
Package cbor is a modern CBOR codec (RFC 8949 & RFC 7049) with CBOR tags,
|
||||
Go struct tags (toarray/keyasint/omitempty), Core Deterministic Encoding,
|
||||
Package cbor is a modern CBOR codec (RFC 8949 & RFC 8742) with CBOR tags,
|
||||
Go struct tag options (toarray/keyasint/omitempty/omitzero), Core Deterministic Encoding,
|
||||
CTAP2, Canonical CBOR, float64->32->16, and duplicate map key detection.
|
||||
|
||||
Encoding options allow "preferred serialization" by encoding integers and floats
|
||||
to their smallest forms (e.g. float16) when values fit.
|
||||
|
||||
Struct tags like "keyasint", "toarray" and "omitempty" make CBOR data smaller
|
||||
and easier to use with structs.
|
||||
Struct tag options "keyasint", "toarray", "omitempty", and "omitzero" reduce encoding size
|
||||
and reduce programming effort.
|
||||
|
||||
For example, "toarray" tag makes struct fields encode to CBOR array elements. And
|
||||
"keyasint" makes a field encode to an element of CBOR map with specified int key.
|
||||
@@ -23,11 +23,19 @@ The Quick Start guide is at https://github.com/fxamacker/cbor#quick-start
|
||||
|
||||
Function signatures identical to encoding/json include:
|
||||
|
||||
Marshal, Unmarshal, NewEncoder, NewDecoder, (*Encoder).Encode, (*Decoder).Decode.
|
||||
Marshal, Unmarshal, NewEncoder, NewDecoder, (*Encoder).Encode, (*Decoder).Decode
|
||||
|
||||
Standard interfaces include:
|
||||
|
||||
BinaryMarshaler, BinaryUnmarshaler, Marshaler, and Unmarshaler.
|
||||
BinaryMarshaler, BinaryUnmarshaler, Marshaler, and Unmarshaler
|
||||
|
||||
Diagnostic functions translate CBOR data item into Diagnostic Notation:
|
||||
|
||||
Diagnose, DiagnoseFirst
|
||||
|
||||
Functions that simplify using CBOR Sequences (RFC 8742) include:
|
||||
|
||||
UnmarshalFirst
|
||||
|
||||
Custom encoding and decoding is possible by implementing standard interfaces for
|
||||
user-defined Go types.
|
||||
@@ -50,19 +58,19 @@ Modes are intended to be reused and are safe for concurrent use.
|
||||
|
||||
EncMode and DecMode Interfaces
|
||||
|
||||
// EncMode interface uses immutable options and is safe for concurrent use.
|
||||
type EncMode interface {
|
||||
// EncMode interface uses immutable options and is safe for concurrent use.
|
||||
type EncMode interface {
|
||||
Marshal(v interface{}) ([]byte, error)
|
||||
NewEncoder(w io.Writer) *Encoder
|
||||
EncOptions() EncOptions // returns copy of options
|
||||
}
|
||||
}
|
||||
|
||||
// DecMode interface uses immutable options and is safe for concurrent use.
|
||||
type DecMode interface {
|
||||
// DecMode interface uses immutable options and is safe for concurrent use.
|
||||
type DecMode interface {
|
||||
Unmarshal(data []byte, v interface{}) error
|
||||
NewDecoder(r io.Reader) *Decoder
|
||||
DecOptions() DecOptions // returns copy of options
|
||||
}
|
||||
}
|
||||
|
||||
Using Default Encoding Mode
|
||||
|
||||
@@ -78,6 +86,16 @@ Using Default Decoding Mode
|
||||
decoder := cbor.NewDecoder(r)
|
||||
err = decoder.Decode(&v)
|
||||
|
||||
Using Default Mode of UnmarshalFirst to Decode CBOR Sequences
|
||||
|
||||
// Decode the first CBOR data item and return remaining bytes:
|
||||
rest, err = cbor.UnmarshalFirst(b, &v) // decode []byte b to v
|
||||
|
||||
Using Extended Diagnostic Notation (EDN) to represent CBOR data
|
||||
|
||||
// Translate the first CBOR data item into text and return remaining bytes.
|
||||
text, rest, err = cbor.DiagnoseFirst(b) // decode []byte b to text
|
||||
|
||||
Creating and Using Encoding Modes
|
||||
|
||||
// Create EncOptions using either struct literal or a function.
|
||||
@@ -111,15 +129,20 @@ Decoding Options: https://github.com/fxamacker/cbor#decoding-options
|
||||
Struct tags like `cbor:"name,omitempty"` and `json:"name,omitempty"` work as expected.
|
||||
If both struct tags are specified then `cbor` is used.
|
||||
|
||||
Struct tags like "keyasint", "toarray", and "omitempty" make it easy to use
|
||||
Struct tag options like "keyasint", "toarray", "omitempty", and "omitzero" make it easy to use
|
||||
very compact formats like COSE and CWT (CBOR Web Tokens) with structs.
|
||||
|
||||
The "omitzero" option omits zero values from encoding, matching
|
||||
[stdlib encoding/json behavior](https://pkg.go.dev/encoding/json#Marshal).
|
||||
When specified in the `cbor` tag, the option is always honored.
|
||||
When specified in the `json` tag, the option is honored when building with Go 1.24+.
|
||||
|
||||
For example, "toarray" makes struct fields encode to array elements. And "keyasint"
|
||||
makes struct fields encode to elements of CBOR map with int keys.
|
||||
|
||||
https://raw.githubusercontent.com/fxamacker/images/master/cbor/v2.0.0/cbor_easy_api.png
|
||||
|
||||
Struct tags are listed at https://github.com/fxamacker/cbor#struct-tags-1
|
||||
Struct tag options are listed at https://github.com/fxamacker/cbor#struct-tags-1
|
||||
|
||||
# Tests and Fuzzing
|
||||
|
||||
|
||||
+376
-66
@@ -58,8 +58,10 @@ import (
|
||||
//
|
||||
// Marshal supports format string stored under the "cbor" key in the struct
|
||||
// field's tag. CBOR format string can specify the name of the field,
|
||||
// "omitempty" and "keyasint" options, and special case "-" for field omission.
|
||||
// If "cbor" key is absent, Marshal uses "json" key.
|
||||
// "omitempty", "omitzero" and "keyasint" options, and special case "-" for
|
||||
// field omission. If "cbor" key is absent, Marshal uses "json" key.
|
||||
// When using the "json" key, the "omitzero" option is honored when building
|
||||
// with Go 1.24+ to match stdlib encoding/json behavior.
|
||||
//
|
||||
// Struct field name is treated as integer if it has "keyasint" option in
|
||||
// its format string. The format string must specify an integer as its
|
||||
@@ -67,8 +69,8 @@ import (
|
||||
//
|
||||
// Special struct field "_" is used to specify struct level options, such as
|
||||
// "toarray". "toarray" option enables Go struct to be encoded as CBOR array.
|
||||
// "omitempty" is disabled by "toarray" to ensure that the same number
|
||||
// of elements are encoded every time.
|
||||
// "omitempty" and "omitzero" are disabled by "toarray" to ensure that the
|
||||
// same number of elements are encoded every time.
|
||||
//
|
||||
// Anonymous struct fields are marshaled as if their exported fields
|
||||
// were fields in the outer struct. Marshal follows the same struct fields
|
||||
@@ -92,7 +94,7 @@ import (
|
||||
//
|
||||
// Values of other types cannot be encoded in CBOR. Attempting
|
||||
// to encode such a value causes Marshal to return an UnsupportedTypeError.
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
func Marshal(v any) ([]byte, error) {
|
||||
return defaultEncMode.Marshal(v)
|
||||
}
|
||||
|
||||
@@ -103,7 +105,7 @@ func Marshal(v interface{}) ([]byte, error) {
|
||||
// partially encoded data if error is returned.
|
||||
//
|
||||
// See Marshal for more details.
|
||||
func MarshalToBuffer(v interface{}, buf *bytes.Buffer) error {
|
||||
func MarshalToBuffer(v any, buf *bytes.Buffer) error {
|
||||
return defaultEncMode.MarshalToBuffer(v, buf)
|
||||
}
|
||||
|
||||
@@ -130,6 +132,20 @@ func (e *MarshalerError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
type TranscodeError struct {
|
||||
err error
|
||||
rtype reflect.Type
|
||||
sourceFormat, targetFormat string
|
||||
}
|
||||
|
||||
func (e TranscodeError) Error() string {
|
||||
return "cbor: cannot transcode from " + e.sourceFormat + " to " + e.targetFormat + ": " + e.err.Error()
|
||||
}
|
||||
|
||||
func (e TranscodeError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
// UnsupportedTypeError is returned by Marshal when attempting to encode value
|
||||
// of an unsupported type.
|
||||
type UnsupportedTypeError struct {
|
||||
@@ -291,24 +307,51 @@ func (icm InfConvertMode) valid() bool {
|
||||
return icm >= 0 && icm < maxInfConvert
|
||||
}
|
||||
|
||||
// TimeMode specifies how to encode time.Time values.
|
||||
// TimeMode specifies how to encode time.Time values in compliance with RFC 8949 (CBOR):
|
||||
// - Section 3.4.1: Standard Date/Time String
|
||||
// - Section 3.4.2: Epoch-Based Date/Time
|
||||
// For more info, see:
|
||||
// - https://www.rfc-editor.org/rfc/rfc8949.html
|
||||
// NOTE: User applications that prefer to encode time with fractional seconds to an integer
|
||||
// (instead of floating point or text string) can use a CBOR tag number not assigned by IANA:
|
||||
// 1. Define a user-defined type in Go with just a time.Time or int64 as its data.
|
||||
// 2. Implement the cbor.Marshaler and cbor.Unmarshaler interface for that user-defined type
|
||||
// to encode or decode the tagged data item with an enclosed integer content.
|
||||
type TimeMode int
|
||||
|
||||
const (
|
||||
// TimeUnix causes time.Time to be encoded as epoch time in integer with second precision.
|
||||
// TimeUnix causes time.Time to encode to a CBOR time (tag 1) with an integer content
|
||||
// representing seconds elapsed (with 1-second precision) since UNIX Epoch UTC.
|
||||
// The TimeUnix option is location independent and has a clear precision guarantee.
|
||||
TimeUnix TimeMode = iota
|
||||
|
||||
// TimeUnixMicro causes time.Time to be encoded as epoch time in float-point rounded to microsecond precision.
|
||||
// TimeUnixMicro causes time.Time to encode to a CBOR time (tag 1) with a floating point content
|
||||
// representing seconds elapsed (with up to 1-microsecond precision) since UNIX Epoch UTC.
|
||||
// NOTE: The floating point content is encoded to the shortest floating-point encoding that preserves
|
||||
// the 64-bit floating point value. I.e., the floating point encoding can be IEEE 764:
|
||||
// binary64, binary32, or binary16 depending on the content's value.
|
||||
TimeUnixMicro
|
||||
|
||||
// TimeUnixDynamic causes time.Time to be encoded as integer if time.Time doesn't have fractional seconds,
|
||||
// otherwise float-point rounded to microsecond precision.
|
||||
// TimeUnixDynamic causes time.Time to encode to a CBOR time (tag 1) with either an integer content or
|
||||
// a floating point content, depending on the content's value. This option is equivalent to dynamically
|
||||
// choosing TimeUnix if time.Time doesn't have fractional seconds, and using TimeUnixMicro if time.Time
|
||||
// has fractional seconds.
|
||||
TimeUnixDynamic
|
||||
|
||||
// TimeRFC3339 causes time.Time to be encoded as RFC3339 formatted string with second precision.
|
||||
// TimeRFC3339 causes time.Time to encode to a CBOR time (tag 0) with a text string content
|
||||
// representing the time using 1-second precision in RFC3339 format. If the time.Time has a
|
||||
// non-UTC timezone then a "localtime - UTC" numeric offset will be included as specified in RFC3339.
|
||||
// NOTE: User applications can avoid including the RFC3339 numeric offset by:
|
||||
// - providing a time.Time value set to UTC, or
|
||||
// - using the TimeUnix, TimeUnixMicro, or TimeUnixDynamic option instead of TimeRFC3339.
|
||||
TimeRFC3339
|
||||
|
||||
// TimeRFC3339Nano causes time.Time to be encoded as RFC3339 formatted string with nanosecond precision.
|
||||
// TimeRFC3339Nano causes time.Time to encode to a CBOR time (tag 0) with a text string content
|
||||
// representing the time using 1-nanosecond precision in RFC3339 format. If the time.Time has a
|
||||
// non-UTC timezone then a "localtime - UTC" numeric offset will be included as specified in RFC3339.
|
||||
// NOTE: User applications can avoid including the RFC3339 numeric offset by:
|
||||
// - providing a time.Time value set to UTC, or
|
||||
// - using the TimeUnix, TimeUnixMicro, or TimeUnixDynamic option instead of TimeRFC3339Nano.
|
||||
TimeRFC3339Nano
|
||||
|
||||
maxTimeMode
|
||||
@@ -481,6 +524,24 @@ func (bmm BinaryMarshalerMode) valid() bool {
|
||||
return bmm >= 0 && bmm < maxBinaryMarshalerMode
|
||||
}
|
||||
|
||||
// TextMarshalerMode specifies how to encode types that implement encoding.TextMarshaler.
|
||||
type TextMarshalerMode int
|
||||
|
||||
const (
|
||||
// TextMarshalerNone does not recognize TextMarshaler implementations during encode.
|
||||
// This is the default behavior.
|
||||
TextMarshalerNone TextMarshalerMode = iota
|
||||
|
||||
// TextMarshalerTextString encodes the output of MarshalText to a CBOR text string.
|
||||
TextMarshalerTextString
|
||||
|
||||
maxTextMarshalerMode
|
||||
)
|
||||
|
||||
func (tmm TextMarshalerMode) valid() bool {
|
||||
return tmm >= 0 && tmm < maxTextMarshalerMode
|
||||
}
|
||||
|
||||
// EncOptions specifies encoding options.
|
||||
type EncOptions struct {
|
||||
// Sort specifies sorting order.
|
||||
@@ -538,6 +599,14 @@ type EncOptions struct {
|
||||
|
||||
// BinaryMarshaler specifies how to encode types that implement encoding.BinaryMarshaler.
|
||||
BinaryMarshaler BinaryMarshalerMode
|
||||
|
||||
// TextMarshaler specifies how to encode types that implement encoding.TextMarshaler.
|
||||
TextMarshaler TextMarshalerMode
|
||||
|
||||
// JSONMarshalerTranscoder sets the transcoding scheme used to marshal types that implement
|
||||
// json.Marshaler but do not also implement cbor.Marshaler. If nil, encoding behavior is not
|
||||
// influenced by whether or not a type implements json.Marshaler.
|
||||
JSONMarshalerTranscoder Transcoder
|
||||
}
|
||||
|
||||
// CanonicalEncOptions returns EncOptions for "Canonical CBOR" encoding,
|
||||
@@ -748,6 +817,9 @@ func (opts EncOptions) encMode() (*encMode, error) { //nolint:gocritic // ignore
|
||||
if !opts.BinaryMarshaler.valid() {
|
||||
return nil, errors.New("cbor: invalid BinaryMarshaler " + strconv.Itoa(int(opts.BinaryMarshaler)))
|
||||
}
|
||||
if !opts.TextMarshaler.valid() {
|
||||
return nil, errors.New("cbor: invalid TextMarshaler " + strconv.Itoa(int(opts.TextMarshaler)))
|
||||
}
|
||||
em := encMode{
|
||||
sort: opts.Sort,
|
||||
shortestFloat: opts.ShortestFloat,
|
||||
@@ -767,13 +839,15 @@ func (opts EncOptions) encMode() (*encMode, error) { //nolint:gocritic // ignore
|
||||
byteSliceLaterEncodingTag: byteSliceLaterEncodingTag,
|
||||
byteArray: opts.ByteArray,
|
||||
binaryMarshaler: opts.BinaryMarshaler,
|
||||
textMarshaler: opts.TextMarshaler,
|
||||
jsonMarshalerTranscoder: opts.JSONMarshalerTranscoder,
|
||||
}
|
||||
return &em, nil
|
||||
}
|
||||
|
||||
// EncMode is the main interface for CBOR encoding.
|
||||
type EncMode interface {
|
||||
Marshal(v interface{}) ([]byte, error)
|
||||
Marshal(v any) ([]byte, error)
|
||||
NewEncoder(w io.Writer) *Encoder
|
||||
EncOptions() EncOptions
|
||||
}
|
||||
@@ -783,7 +857,7 @@ type EncMode interface {
|
||||
// into the built-in buffer pool.
|
||||
type UserBufferEncMode interface {
|
||||
EncMode
|
||||
MarshalToBuffer(v interface{}, buf *bytes.Buffer) error
|
||||
MarshalToBuffer(v any, buf *bytes.Buffer) error
|
||||
|
||||
// This private method is to prevent users implementing
|
||||
// this interface and so future additions to it will
|
||||
@@ -812,6 +886,8 @@ type encMode struct {
|
||||
byteSliceLaterEncodingTag uint64
|
||||
byteArray ByteArrayMode
|
||||
binaryMarshaler BinaryMarshalerMode
|
||||
textMarshaler TextMarshalerMode
|
||||
jsonMarshalerTranscoder Transcoder
|
||||
}
|
||||
|
||||
var defaultEncMode, _ = EncOptions{}.encMode()
|
||||
@@ -888,22 +964,24 @@ func getMarshalerDecMode(indefLength IndefLengthMode, tagsMd TagsMode) *decMode
|
||||
// EncOptions returns user specified options used to create this EncMode.
|
||||
func (em *encMode) EncOptions() EncOptions {
|
||||
return EncOptions{
|
||||
Sort: em.sort,
|
||||
ShortestFloat: em.shortestFloat,
|
||||
NaNConvert: em.nanConvert,
|
||||
InfConvert: em.infConvert,
|
||||
BigIntConvert: em.bigIntConvert,
|
||||
Time: em.time,
|
||||
TimeTag: em.timeTag,
|
||||
IndefLength: em.indefLength,
|
||||
NilContainers: em.nilContainers,
|
||||
TagsMd: em.tagsMd,
|
||||
OmitEmpty: em.omitEmpty,
|
||||
String: em.stringType,
|
||||
FieldName: em.fieldName,
|
||||
ByteSliceLaterFormat: em.byteSliceLaterFormat,
|
||||
ByteArray: em.byteArray,
|
||||
BinaryMarshaler: em.binaryMarshaler,
|
||||
Sort: em.sort,
|
||||
ShortestFloat: em.shortestFloat,
|
||||
NaNConvert: em.nanConvert,
|
||||
InfConvert: em.infConvert,
|
||||
BigIntConvert: em.bigIntConvert,
|
||||
Time: em.time,
|
||||
TimeTag: em.timeTag,
|
||||
IndefLength: em.indefLength,
|
||||
NilContainers: em.nilContainers,
|
||||
TagsMd: em.tagsMd,
|
||||
OmitEmpty: em.omitEmpty,
|
||||
String: em.stringType,
|
||||
FieldName: em.fieldName,
|
||||
ByteSliceLaterFormat: em.byteSliceLaterFormat,
|
||||
ByteArray: em.byteArray,
|
||||
BinaryMarshaler: em.binaryMarshaler,
|
||||
TextMarshaler: em.textMarshaler,
|
||||
JSONMarshalerTranscoder: em.jsonMarshalerTranscoder,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -921,7 +999,7 @@ func (em *encMode) encTagBytes(t reflect.Type) []byte {
|
||||
// Marshal returns the CBOR encoding of v using em encoding mode.
|
||||
//
|
||||
// See the documentation for Marshal for details.
|
||||
func (em *encMode) Marshal(v interface{}) ([]byte, error) {
|
||||
func (em *encMode) Marshal(v any) ([]byte, error) {
|
||||
e := getEncodeBuffer()
|
||||
|
||||
if err := encode(e, em, reflect.ValueOf(v)); err != nil {
|
||||
@@ -943,7 +1021,7 @@ func (em *encMode) Marshal(v interface{}) ([]byte, error) {
|
||||
// partially encoded data if error is returned.
|
||||
//
|
||||
// See Marshal for more details.
|
||||
func (em *encMode) MarshalToBuffer(v interface{}, buf *bytes.Buffer) error {
|
||||
func (em *encMode) MarshalToBuffer(v any, buf *bytes.Buffer) error {
|
||||
if buf == nil {
|
||||
return fmt.Errorf("cbor: encoding buffer provided by user is nil")
|
||||
}
|
||||
@@ -957,7 +1035,7 @@ func (em *encMode) NewEncoder(w io.Writer) *Encoder {
|
||||
|
||||
// encodeBufferPool caches unused bytes.Buffer objects for later reuse.
|
||||
var encodeBufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
New: func() any {
|
||||
e := new(bytes.Buffer)
|
||||
e.Grow(32) // TODO: make this configurable
|
||||
return e
|
||||
@@ -975,6 +1053,7 @@ func putEncodeBuffer(e *bytes.Buffer) {
|
||||
|
||||
type encodeFunc func(e *bytes.Buffer, em *encMode, v reflect.Value) error
|
||||
type isEmptyFunc func(em *encMode, v reflect.Value) (empty bool, err error)
|
||||
type isZeroFunc func(v reflect.Value) (zero bool, err error)
|
||||
|
||||
func encode(e *bytes.Buffer, em *encMode, v reflect.Value) error {
|
||||
if !v.IsValid() {
|
||||
@@ -983,7 +1062,7 @@ func encode(e *bytes.Buffer, em *encMode, v reflect.Value) error {
|
||||
return nil
|
||||
}
|
||||
vt := v.Type()
|
||||
f, _ := getEncodeFunc(vt)
|
||||
f, _, _ := getEncodeFunc(vt)
|
||||
if f == nil {
|
||||
return &UnsupportedTypeError{vt}
|
||||
}
|
||||
@@ -1483,6 +1562,15 @@ func encodeStruct(e *bytes.Buffer, em *encMode, v reflect.Value) (err error) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if f.omitZero {
|
||||
zero, err := f.izf(fv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if zero {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !f.keyAsInt && em.fieldName == FieldNameToByteString {
|
||||
e.Write(f.cborNameByteString)
|
||||
@@ -1665,6 +1753,107 @@ func (bme binaryMarshalerEncoder) isEmpty(em *encMode, v reflect.Value) (bool, e
|
||||
return len(data) == 0, nil
|
||||
}
|
||||
|
||||
type textMarshalerEncoder struct {
|
||||
alternateEncode encodeFunc
|
||||
alternateIsEmpty isEmptyFunc
|
||||
}
|
||||
|
||||
func (tme textMarshalerEncoder) encode(e *bytes.Buffer, em *encMode, v reflect.Value) error {
|
||||
if em.textMarshaler == TextMarshalerNone {
|
||||
return tme.alternateEncode(e, em, v)
|
||||
}
|
||||
|
||||
vt := v.Type()
|
||||
m, ok := v.Interface().(encoding.TextMarshaler)
|
||||
if !ok {
|
||||
pv := reflect.New(vt)
|
||||
pv.Elem().Set(v)
|
||||
m = pv.Interface().(encoding.TextMarshaler)
|
||||
}
|
||||
data, err := m.MarshalText()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cbor: cannot marshal text for %s: %w", vt, err)
|
||||
}
|
||||
if b := em.encTagBytes(vt); b != nil {
|
||||
e.Write(b)
|
||||
}
|
||||
|
||||
encodeHead(e, byte(cborTypeTextString), uint64(len(data)))
|
||||
e.Write(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tme textMarshalerEncoder) isEmpty(em *encMode, v reflect.Value) (bool, error) {
|
||||
if em.textMarshaler == TextMarshalerNone {
|
||||
return tme.alternateIsEmpty(em, v)
|
||||
}
|
||||
|
||||
m, ok := v.Interface().(encoding.TextMarshaler)
|
||||
if !ok {
|
||||
pv := reflect.New(v.Type())
|
||||
pv.Elem().Set(v)
|
||||
m = pv.Interface().(encoding.TextMarshaler)
|
||||
}
|
||||
data, err := m.MarshalText()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("cbor: cannot marshal text for %s: %w", v.Type(), err)
|
||||
}
|
||||
return len(data) == 0, nil
|
||||
}
|
||||
|
||||
type jsonMarshalerEncoder struct {
|
||||
alternateEncode encodeFunc
|
||||
alternateIsEmpty isEmptyFunc
|
||||
}
|
||||
|
||||
func (jme jsonMarshalerEncoder) encode(e *bytes.Buffer, em *encMode, v reflect.Value) error {
|
||||
if em.jsonMarshalerTranscoder == nil {
|
||||
return jme.alternateEncode(e, em, v)
|
||||
}
|
||||
|
||||
vt := v.Type()
|
||||
m, ok := v.Interface().(jsonMarshaler)
|
||||
if !ok {
|
||||
pv := reflect.New(vt)
|
||||
pv.Elem().Set(v)
|
||||
m = pv.Interface().(jsonMarshaler)
|
||||
}
|
||||
|
||||
json, err := m.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offset := e.Len()
|
||||
|
||||
if b := em.encTagBytes(vt); b != nil {
|
||||
e.Write(b)
|
||||
}
|
||||
|
||||
if err := em.jsonMarshalerTranscoder.Transcode(e, bytes.NewReader(json)); err != nil {
|
||||
return &TranscodeError{err: err, rtype: vt, sourceFormat: "json", targetFormat: "cbor"}
|
||||
}
|
||||
|
||||
// Validate that the transcode function has written exactly one well-formed data item.
|
||||
d := decoder{data: e.Bytes()[offset:], dm: getMarshalerDecMode(em.indefLength, em.tagsMd)}
|
||||
if err := d.wellformed(false, true); err != nil {
|
||||
e.Truncate(offset)
|
||||
return &TranscodeError{err: err, rtype: vt, sourceFormat: "json", targetFormat: "cbor"}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (jme jsonMarshalerEncoder) isEmpty(em *encMode, v reflect.Value) (bool, error) {
|
||||
if em.jsonMarshalerTranscoder == nil {
|
||||
return jme.alternateIsEmpty(em, v)
|
||||
}
|
||||
|
||||
// As with types implementing cbor.Marshaler, transcoded json.Marshaler values always encode
|
||||
// as exactly one complete CBOR data item.
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func encodeMarshalerType(e *bytes.Buffer, em *encMode, v reflect.Value) error {
|
||||
if em.tagsMd == TagsForbidden && v.Type() == typeRawTag {
|
||||
return errors.New("cbor: cannot encode cbor.RawTag when TagsMd is TagsForbidden")
|
||||
@@ -1768,41 +1957,45 @@ func encodeHead(e *bytes.Buffer, t byte, n uint64) int {
|
||||
return headSize
|
||||
}
|
||||
|
||||
type jsonMarshaler interface{ MarshalJSON() ([]byte, error) }
|
||||
|
||||
var (
|
||||
typeMarshaler = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
||||
typeBinaryMarshaler = reflect.TypeOf((*encoding.BinaryMarshaler)(nil)).Elem()
|
||||
typeTextMarshaler = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
||||
typeJSONMarshaler = reflect.TypeOf((*jsonMarshaler)(nil)).Elem()
|
||||
typeRawMessage = reflect.TypeOf(RawMessage(nil))
|
||||
typeByteString = reflect.TypeOf(ByteString(""))
|
||||
)
|
||||
|
||||
func getEncodeFuncInternal(t reflect.Type) (ef encodeFunc, ief isEmptyFunc) {
|
||||
func getEncodeFuncInternal(t reflect.Type) (ef encodeFunc, ief isEmptyFunc, izf isZeroFunc) {
|
||||
k := t.Kind()
|
||||
if k == reflect.Ptr {
|
||||
return getEncodeIndirectValueFunc(t), isEmptyPtr
|
||||
if k == reflect.Pointer {
|
||||
return getEncodeIndirectValueFunc(t), isEmptyPtr, getIsZeroFunc(t)
|
||||
}
|
||||
switch t {
|
||||
case typeSimpleValue:
|
||||
return encodeMarshalerType, isEmptyUint
|
||||
return encodeMarshalerType, isEmptyUint, getIsZeroFunc(t)
|
||||
|
||||
case typeTag:
|
||||
return encodeTag, alwaysNotEmpty
|
||||
return encodeTag, alwaysNotEmpty, getIsZeroFunc(t)
|
||||
|
||||
case typeTime:
|
||||
return encodeTime, alwaysNotEmpty
|
||||
return encodeTime, alwaysNotEmpty, getIsZeroFunc(t)
|
||||
|
||||
case typeBigInt:
|
||||
return encodeBigInt, alwaysNotEmpty
|
||||
return encodeBigInt, alwaysNotEmpty, getIsZeroFunc(t)
|
||||
|
||||
case typeRawMessage:
|
||||
return encodeMarshalerType, isEmptySlice
|
||||
return encodeMarshalerType, isEmptySlice, getIsZeroFunc(t)
|
||||
|
||||
case typeByteString:
|
||||
return encodeMarshalerType, isEmptyString
|
||||
return encodeMarshalerType, isEmptyString, getIsZeroFunc(t)
|
||||
}
|
||||
if reflect.PtrTo(t).Implements(typeMarshaler) {
|
||||
return encodeMarshalerType, alwaysNotEmpty
|
||||
if reflect.PointerTo(t).Implements(typeMarshaler) {
|
||||
return encodeMarshalerType, alwaysNotEmpty, getIsZeroFunc(t)
|
||||
}
|
||||
if reflect.PtrTo(t).Implements(typeBinaryMarshaler) {
|
||||
if reflect.PointerTo(t).Implements(typeBinaryMarshaler) {
|
||||
defer func() {
|
||||
// capture encoding method used for modes that disable BinaryMarshaler
|
||||
bme := binaryMarshalerEncoder{
|
||||
@@ -1813,41 +2006,65 @@ func getEncodeFuncInternal(t reflect.Type) (ef encodeFunc, ief isEmptyFunc) {
|
||||
ief = bme.isEmpty
|
||||
}()
|
||||
}
|
||||
if reflect.PointerTo(t).Implements(typeTextMarshaler) {
|
||||
defer func() {
|
||||
// capture encoding method used for modes that disable TextMarshaler
|
||||
tme := textMarshalerEncoder{
|
||||
alternateEncode: ef,
|
||||
alternateIsEmpty: ief,
|
||||
}
|
||||
ef = tme.encode
|
||||
ief = tme.isEmpty
|
||||
}()
|
||||
}
|
||||
if reflect.PointerTo(t).Implements(typeJSONMarshaler) {
|
||||
defer func() {
|
||||
// capture encoding method used for modes that don't support transcoding
|
||||
// from types that implement json.Marshaler.
|
||||
jme := jsonMarshalerEncoder{
|
||||
alternateEncode: ef,
|
||||
alternateIsEmpty: ief,
|
||||
}
|
||||
ef = jme.encode
|
||||
ief = jme.isEmpty
|
||||
}()
|
||||
}
|
||||
|
||||
switch k {
|
||||
case reflect.Bool:
|
||||
return encodeBool, isEmptyBool
|
||||
return encodeBool, isEmptyBool, getIsZeroFunc(t)
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return encodeInt, isEmptyInt
|
||||
return encodeInt, isEmptyInt, getIsZeroFunc(t)
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return encodeUint, isEmptyUint
|
||||
return encodeUint, isEmptyUint, getIsZeroFunc(t)
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return encodeFloat, isEmptyFloat
|
||||
return encodeFloat, isEmptyFloat, getIsZeroFunc(t)
|
||||
|
||||
case reflect.String:
|
||||
return encodeString, isEmptyString
|
||||
return encodeString, isEmptyString, getIsZeroFunc(t)
|
||||
|
||||
case reflect.Slice:
|
||||
if t.Elem().Kind() == reflect.Uint8 {
|
||||
return encodeByteString, isEmptySlice
|
||||
return encodeByteString, isEmptySlice, getIsZeroFunc(t)
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case reflect.Array:
|
||||
f, _ := getEncodeFunc(t.Elem())
|
||||
f, _, _ := getEncodeFunc(t.Elem())
|
||||
if f == nil {
|
||||
return nil, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
return arrayEncodeFunc{f: f}.encode, isEmptySlice
|
||||
return arrayEncodeFunc{f: f}.encode, isEmptySlice, getIsZeroFunc(t)
|
||||
|
||||
case reflect.Map:
|
||||
f := getEncodeMapFunc(t)
|
||||
if f == nil {
|
||||
return nil, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
return f, isEmptyMap
|
||||
return f, isEmptyMap, getIsZeroFunc(t)
|
||||
|
||||
case reflect.Struct:
|
||||
// Get struct's special field "_" tag options
|
||||
@@ -1855,31 +2072,31 @@ func getEncodeFuncInternal(t reflect.Type) (ef encodeFunc, ief isEmptyFunc) {
|
||||
tag := f.Tag.Get("cbor")
|
||||
if tag != "-" {
|
||||
if hasToArrayOption(tag) {
|
||||
return encodeStructToArray, isEmptyStruct
|
||||
return encodeStructToArray, isEmptyStruct, isZeroFieldStruct
|
||||
}
|
||||
}
|
||||
}
|
||||
return encodeStruct, isEmptyStruct
|
||||
return encodeStruct, isEmptyStruct, getIsZeroFunc(t)
|
||||
|
||||
case reflect.Interface:
|
||||
return encodeIntf, isEmptyIntf
|
||||
return encodeIntf, isEmptyIntf, getIsZeroFunc(t)
|
||||
}
|
||||
return nil, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func getEncodeIndirectValueFunc(t reflect.Type) encodeFunc {
|
||||
for t.Kind() == reflect.Ptr {
|
||||
for t.Kind() == reflect.Pointer {
|
||||
t = t.Elem()
|
||||
}
|
||||
f, _ := getEncodeFunc(t)
|
||||
f, _, _ := getEncodeFunc(t)
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
return func(e *bytes.Buffer, em *encMode, v reflect.Value) error {
|
||||
for v.Kind() == reflect.Ptr && !v.IsNil() {
|
||||
for v.Kind() == reflect.Pointer && !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
if v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
if v.Kind() == reflect.Pointer && v.IsNil() {
|
||||
e.Write(cborNil)
|
||||
return nil
|
||||
}
|
||||
@@ -1987,3 +2204,96 @@ func float32NaNFromReflectValue(v reflect.Value) float32 {
|
||||
f32 := p.Convert(reflect.TypeOf((*float32)(nil))).Elem().Interface().(float32)
|
||||
return f32
|
||||
}
|
||||
|
||||
type isZeroer interface {
|
||||
IsZero() bool
|
||||
}
|
||||
|
||||
var isZeroerType = reflect.TypeOf((*isZeroer)(nil)).Elem()
|
||||
|
||||
// getIsZeroFunc returns a function for the given type that can be called to determine if a given value is zero.
|
||||
// Types that implement `IsZero() bool` are delegated to for non-nil values.
|
||||
// Types that do not implement `IsZero() bool` use the reflect.Value#IsZero() implementation.
|
||||
// The returned function matches behavior of stdlib encoding/json behavior in Go 1.24+.
|
||||
func getIsZeroFunc(t reflect.Type) isZeroFunc {
|
||||
// Provide a function that uses a type's IsZero method if defined.
|
||||
switch {
|
||||
case t == nil:
|
||||
return isZeroDefault
|
||||
case t.Kind() == reflect.Interface && t.Implements(isZeroerType):
|
||||
return isZeroInterfaceCustom
|
||||
case t.Kind() == reflect.Pointer && t.Implements(isZeroerType):
|
||||
return isZeroPointerCustom
|
||||
case t.Implements(isZeroerType):
|
||||
return isZeroCustom
|
||||
case reflect.PointerTo(t).Implements(isZeroerType):
|
||||
return isZeroAddrCustom
|
||||
default:
|
||||
return isZeroDefault
|
||||
}
|
||||
}
|
||||
|
||||
// isZeroInterfaceCustom returns true for nil or pointer-to-nil values,
|
||||
// and delegates to the custom IsZero() implementation otherwise.
|
||||
func isZeroInterfaceCustom(v reflect.Value) (bool, error) {
|
||||
kind := v.Kind()
|
||||
|
||||
switch kind {
|
||||
case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer, reflect.Interface, reflect.Slice:
|
||||
if v.IsNil() {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Interface, reflect.Pointer:
|
||||
if elem := v.Elem(); elem.Kind() == reflect.Pointer && elem.IsNil() {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return v.Interface().(isZeroer).IsZero(), nil
|
||||
}
|
||||
|
||||
// isZeroPointerCustom returns true for nil values,
|
||||
// and delegates to the custom IsZero() implementation otherwise.
|
||||
func isZeroPointerCustom(v reflect.Value) (bool, error) {
|
||||
if v.IsNil() {
|
||||
return true, nil
|
||||
}
|
||||
return v.Interface().(isZeroer).IsZero(), nil
|
||||
}
|
||||
|
||||
// isZeroCustom delegates to the custom IsZero() implementation.
|
||||
func isZeroCustom(v reflect.Value) (bool, error) {
|
||||
return v.Interface().(isZeroer).IsZero(), nil
|
||||
}
|
||||
|
||||
// isZeroAddrCustom delegates to the custom IsZero() implementation of the addr of the value.
|
||||
func isZeroAddrCustom(v reflect.Value) (bool, error) {
|
||||
if !v.CanAddr() {
|
||||
// Temporarily box v so we can take the address.
|
||||
v2 := reflect.New(v.Type()).Elem()
|
||||
v2.Set(v)
|
||||
v = v2
|
||||
}
|
||||
return v.Addr().Interface().(isZeroer).IsZero(), nil
|
||||
}
|
||||
|
||||
// isZeroDefault calls reflect.Value#IsZero()
|
||||
func isZeroDefault(v reflect.Value) (bool, error) {
|
||||
if !v.IsValid() {
|
||||
// v is zero value
|
||||
return true, nil
|
||||
}
|
||||
return v.IsZero(), nil
|
||||
}
|
||||
|
||||
// isZeroFieldStruct is used to determine whether to omit toarray structs
|
||||
func isZeroFieldStruct(v reflect.Value) (bool, error) {
|
||||
structType, err := getEncodingStructType(v.Type())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return len(structType.fields) == 0, nil
|
||||
}
|
||||
|
||||
+4
-6
@@ -1,8 +1,6 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
//go:build go1.20
|
||||
|
||||
package cbor
|
||||
|
||||
import (
|
||||
@@ -67,8 +65,8 @@ func (me *mapKeyValueEncodeFunc) encodeKeyValues(e *bytes.Buffer, em *encMode, v
|
||||
}
|
||||
|
||||
func getEncodeMapFunc(t reflect.Type) encodeFunc {
|
||||
kf, _ := getEncodeFunc(t.Key())
|
||||
ef, _ := getEncodeFunc(t.Elem())
|
||||
kf, _, _ := getEncodeFunc(t.Key())
|
||||
ef, _, _ := getEncodeFunc(t.Elem())
|
||||
if kf == nil || ef == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -76,13 +74,13 @@ func getEncodeMapFunc(t reflect.Type) encodeFunc {
|
||||
kf: kf,
|
||||
ef: ef,
|
||||
kpool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
New: func() any {
|
||||
rk := reflect.New(t.Key()).Elem()
|
||||
return &rk
|
||||
},
|
||||
},
|
||||
vpool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
New: func() any {
|
||||
rv := reflect.New(t.Elem()).Elem()
|
||||
return &rv
|
||||
},
|
||||
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
//go:build !go1.20
|
||||
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type mapKeyValueEncodeFunc struct {
|
||||
kf, ef encodeFunc
|
||||
}
|
||||
|
||||
func (me *mapKeyValueEncodeFunc) encodeKeyValues(e *bytes.Buffer, em *encMode, v reflect.Value, kvs []keyValue) error {
|
||||
if kvs == nil {
|
||||
for i, iter := 0, v.MapRange(); iter.Next(); i++ {
|
||||
if err := me.kf(e, em, iter.Key()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := me.ef(e, em, iter.Value()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
initial := e.Len()
|
||||
for i, iter := 0, v.MapRange(); iter.Next(); i++ {
|
||||
offset := e.Len()
|
||||
if err := me.kf(e, em, iter.Key()); err != nil {
|
||||
return err
|
||||
}
|
||||
valueOffset := e.Len()
|
||||
if err := me.ef(e, em, iter.Value()); err != nil {
|
||||
return err
|
||||
}
|
||||
kvs[i] = keyValue{
|
||||
offset: offset - initial,
|
||||
valueOffset: valueOffset - initial,
|
||||
nextOffset: e.Len() - initial,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getEncodeMapFunc(t reflect.Type) encodeFunc {
|
||||
kf, _ := getEncodeFunc(t.Key())
|
||||
ef, _ := getEncodeFunc(t.Elem())
|
||||
if kf == nil || ef == nil {
|
||||
return nil
|
||||
}
|
||||
mkv := &mapKeyValueEncodeFunc{kf: kf, ef: ef}
|
||||
return mapEncodeFunc{
|
||||
e: mkv.encodeKeyValues,
|
||||
}.encode
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
//go:build go1.24
|
||||
|
||||
package cbor
|
||||
|
||||
var jsonStdlibSupportsOmitzero = true
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
//go:build !go1.24
|
||||
|
||||
package cbor
|
||||
|
||||
var jsonStdlibSupportsOmitzero = false
|
||||
+29
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
package cbor
|
||||
|
||||
import (
|
||||
@@ -45,6 +48,9 @@ func (sv SimpleValue) MarshalCBOR() ([]byte, error) {
|
||||
}
|
||||
|
||||
// UnmarshalCBOR decodes CBOR simple value (major type 7) to SimpleValue.
|
||||
//
|
||||
// Deprecated: No longer used by this codec; kept for compatibility
|
||||
// with user apps that directly call this function.
|
||||
func (sv *SimpleValue) UnmarshalCBOR(data []byte) error {
|
||||
if sv == nil {
|
||||
return errors.New("cbor.SimpleValue: UnmarshalCBOR on nil pointer")
|
||||
@@ -52,6 +58,29 @@ func (sv *SimpleValue) UnmarshalCBOR(data []byte) error {
|
||||
|
||||
d := decoder{data: data, dm: defaultDecMode}
|
||||
|
||||
// Check well-formedness of CBOR data item.
|
||||
// SimpleValue.UnmarshalCBOR() is exported, so
|
||||
// the codec needs to support same behavior for:
|
||||
// - Unmarshal(data, *SimpleValue)
|
||||
// - SimpleValue.UnmarshalCBOR(data)
|
||||
err := d.wellformed(false, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sv.unmarshalCBOR(data)
|
||||
}
|
||||
|
||||
// unmarshalCBOR decodes CBOR simple value (major type 7) to SimpleValue.
|
||||
// This function assumes data is well-formed, and does not perform bounds checking.
|
||||
// This function is called by Unmarshal().
|
||||
func (sv *SimpleValue) unmarshalCBOR(data []byte) error {
|
||||
if sv == nil {
|
||||
return errors.New("cbor.SimpleValue: UnmarshalCBOR on nil pointer")
|
||||
}
|
||||
|
||||
d := decoder{data: data, dm: defaultDecMode}
|
||||
|
||||
typ, ai, val := d.getHead()
|
||||
|
||||
if typ != cborTypePrimitives {
|
||||
|
||||
+2
-2
@@ -26,7 +26,7 @@ func NewDecoder(r io.Reader) *Decoder {
|
||||
}
|
||||
|
||||
// Decode reads CBOR value and decodes it into the value pointed to by v.
|
||||
func (dec *Decoder) Decode(v interface{}) error {
|
||||
func (dec *Decoder) Decode(v any) error {
|
||||
_, err := dec.readNext()
|
||||
if err != nil {
|
||||
// Return validation error or read error.
|
||||
@@ -170,7 +170,7 @@ func NewEncoder(w io.Writer) *Encoder {
|
||||
}
|
||||
|
||||
// Encode writes the CBOR encoding of v.
|
||||
func (enc *Encoder) Encode(v interface{}) error {
|
||||
func (enc *Encoder) Encode(v any) error {
|
||||
if len(enc.indefTypes) > 0 && v != nil {
|
||||
indefType := enc.indefTypes[len(enc.indefTypes)-1]
|
||||
if indefType == cborTypeTextString {
|
||||
|
||||
+13
-5
@@ -18,9 +18,11 @@ type field struct {
|
||||
typ reflect.Type
|
||||
ef encodeFunc
|
||||
ief isEmptyFunc
|
||||
izf isZeroFunc
|
||||
typInfo *typeInfo // used to decoder to reuse type info
|
||||
tagged bool // used to choose dominant field (at the same level tagged fields dominate untagged fields)
|
||||
omitEmpty bool // used to skip empty field
|
||||
omitZero bool // used to skip zero field
|
||||
keyAsInt bool // used to encode/decode field name as int
|
||||
}
|
||||
|
||||
@@ -157,7 +159,7 @@ func appendFields(
|
||||
f := t.Field(i)
|
||||
|
||||
ft := f.Type
|
||||
for ft.Kind() == reflect.Ptr {
|
||||
for ft.Kind() == reflect.Pointer {
|
||||
ft = ft.Elem()
|
||||
}
|
||||
|
||||
@@ -165,9 +167,11 @@ func appendFields(
|
||||
continue
|
||||
}
|
||||
|
||||
cborTag := true
|
||||
tag := f.Tag.Get("cbor")
|
||||
if tag == "" {
|
||||
tag = f.Tag.Get("json")
|
||||
cborTag = false
|
||||
}
|
||||
if tag == "-" {
|
||||
continue
|
||||
@@ -177,7 +181,7 @@ func appendFields(
|
||||
|
||||
// Parse field tag options
|
||||
var tagFieldName string
|
||||
var omitempty, keyasint bool
|
||||
var omitempty, omitzero, keyasint bool
|
||||
for j := 0; tag != ""; j++ {
|
||||
var token string
|
||||
idx := strings.IndexByte(tag, ',')
|
||||
@@ -192,6 +196,10 @@ func appendFields(
|
||||
switch token {
|
||||
case "omitempty":
|
||||
omitempty = true
|
||||
case "omitzero":
|
||||
if cborTag || jsonStdlibSupportsOmitzero {
|
||||
omitzero = true
|
||||
}
|
||||
case "keyasint":
|
||||
keyasint = true
|
||||
}
|
||||
@@ -213,6 +221,7 @@ func appendFields(
|
||||
idx: fIdx,
|
||||
typ: f.Type,
|
||||
omitEmpty: omitempty,
|
||||
omitZero: omitzero,
|
||||
keyAsInt: keyasint,
|
||||
tagged: tagged})
|
||||
} else {
|
||||
@@ -230,8 +239,7 @@ func appendFields(
|
||||
// a nonexportable anonymous field of struct type.
|
||||
// Nonexportable anonymous field of struct type can contain exportable fields.
|
||||
func isFieldExportable(f reflect.StructField, fk reflect.Kind) bool { //nolint:gocritic // ignore hugeParam
|
||||
exportable := f.PkgPath == ""
|
||||
return exportable || (f.Anonymous && fk == reflect.Struct)
|
||||
return f.IsExported() || (f.Anonymous && fk == reflect.Struct)
|
||||
}
|
||||
|
||||
type embeddedFieldNullPtrFunc func(reflect.Value) (reflect.Value, error)
|
||||
@@ -244,7 +252,7 @@ func getFieldValue(v reflect.Value, idx []int, f embeddedFieldNullPtrFunc) (fv r
|
||||
fv = fv.Field(n)
|
||||
|
||||
if i < len(idx)-1 {
|
||||
if fv.Kind() == reflect.Ptr && fv.Type().Elem().Kind() == reflect.Struct {
|
||||
if fv.Kind() == reflect.Pointer && fv.Type().Elem().Kind() == reflect.Struct {
|
||||
if fv.IsNil() {
|
||||
// Null pointer to embedded struct field
|
||||
fv, err = f(fv)
|
||||
|
||||
+39
-9
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) Faye Amacker. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
package cbor
|
||||
|
||||
import (
|
||||
@@ -7,27 +10,54 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Tag represents CBOR tag data, including tag number and unmarshaled tag content. Marshaling and
|
||||
// unmarshaling of tag content is subject to any encode and decode options that would apply to
|
||||
// enclosed data item if it were to appear outside of a tag.
|
||||
// Tag represents a tagged data item (CBOR major type 6), comprising a tag number and the unmarshaled tag content.
|
||||
// NOTE: The same encoding and decoding options that apply to untagged CBOR data items also applies to tag content
|
||||
// during encoding and decoding.
|
||||
type Tag struct {
|
||||
Number uint64
|
||||
Content interface{}
|
||||
Content any
|
||||
}
|
||||
|
||||
// RawTag represents CBOR tag data, including tag number and raw tag content.
|
||||
// RawTag implements Unmarshaler and Marshaler interfaces.
|
||||
// RawTag represents a tagged data item (CBOR major type 6), comprising a tag number and the raw tag content.
|
||||
// The raw tag content (enclosed data item) is a CBOR-encoded data item.
|
||||
// RawTag can be used to delay decoding a CBOR data item or precompute encoding a CBOR data item.
|
||||
type RawTag struct {
|
||||
Number uint64
|
||||
Content RawMessage
|
||||
}
|
||||
|
||||
// UnmarshalCBOR sets *t with tag number and raw tag content copied from data.
|
||||
// UnmarshalCBOR sets *t with the tag number and the raw tag content copied from data.
|
||||
//
|
||||
// Deprecated: No longer used by this codec; kept for compatibility
|
||||
// with user apps that directly call this function.
|
||||
func (t *RawTag) UnmarshalCBOR(data []byte) error {
|
||||
if t == nil {
|
||||
return errors.New("cbor.RawTag: UnmarshalCBOR on nil pointer")
|
||||
}
|
||||
|
||||
d := decoder{data: data, dm: defaultDecMode}
|
||||
|
||||
// Check if data is a well-formed CBOR data item.
|
||||
// RawTag.UnmarshalCBOR() is exported, so
|
||||
// the codec needs to support same behavior for:
|
||||
// - Unmarshal(data, *RawTag)
|
||||
// - RawTag.UnmarshalCBOR(data)
|
||||
err := d.wellformed(false, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return t.unmarshalCBOR(data)
|
||||
}
|
||||
|
||||
// unmarshalCBOR sets *t with the tag number and the raw tag content copied from data.
|
||||
// This function assumes data is well-formed, and does not perform bounds checking.
|
||||
// This function is called by Unmarshal().
|
||||
func (t *RawTag) unmarshalCBOR(data []byte) error {
|
||||
if t == nil {
|
||||
return errors.New("cbor.RawTag: UnmarshalCBOR on nil pointer")
|
||||
}
|
||||
|
||||
// Decoding CBOR null and undefined to cbor.RawTag is no-op.
|
||||
if len(data) == 1 && (data[0] == 0xf6 || data[0] == 0xf7) {
|
||||
return nil
|
||||
@@ -193,7 +223,7 @@ func (t *syncTagSet) Add(opts TagOptions, contentType reflect.Type, num uint64,
|
||||
if contentType == nil {
|
||||
return errors.New("cbor: cannot add nil content type to TagSet")
|
||||
}
|
||||
for contentType.Kind() == reflect.Ptr {
|
||||
for contentType.Kind() == reflect.Pointer {
|
||||
contentType = contentType.Elem()
|
||||
}
|
||||
tag, err := newTagItem(opts, contentType, num, nestedNum...)
|
||||
@@ -216,7 +246,7 @@ func (t *syncTagSet) Add(opts TagOptions, contentType reflect.Type, num uint64,
|
||||
|
||||
// Remove removes given tag content type from TagSet.
|
||||
func (t *syncTagSet) Remove(contentType reflect.Type) {
|
||||
for contentType.Kind() == reflect.Ptr {
|
||||
for contentType.Kind() == reflect.Pointer {
|
||||
contentType = contentType.Elem()
|
||||
}
|
||||
t.Lock()
|
||||
|
||||
+9
-7
@@ -1,26 +1,28 @@
|
||||
version: "2"
|
||||
|
||||
run:
|
||||
timeout: 1m
|
||||
tests: true
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
default: none
|
||||
enable: # please keep this alphabetized
|
||||
- asasalint
|
||||
- asciicheck
|
||||
- copyloopvar
|
||||
- dupl
|
||||
- errcheck
|
||||
- forcetypeassert
|
||||
- goconst
|
||||
- gocritic
|
||||
- gofmt
|
||||
- goimports
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
- musttag
|
||||
- revive
|
||||
- staticcheck
|
||||
- typecheck
|
||||
- unused
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 10
|
||||
|
||||
+4
-4
@@ -77,7 +77,7 @@ func newSink(fn func(prefix, args string), formatter Formatter) logr.LogSink {
|
||||
write: fn,
|
||||
}
|
||||
// For skipping fnlogger.Info and fnlogger.Error.
|
||||
l.Formatter.AddCallDepth(1)
|
||||
l.AddCallDepth(1) // via Formatter
|
||||
return l
|
||||
}
|
||||
|
||||
@@ -164,17 +164,17 @@ type fnlogger struct {
|
||||
}
|
||||
|
||||
func (l fnlogger) WithName(name string) logr.LogSink {
|
||||
l.Formatter.AddName(name)
|
||||
l.AddName(name) // via Formatter
|
||||
return &l
|
||||
}
|
||||
|
||||
func (l fnlogger) WithValues(kvList ...any) logr.LogSink {
|
||||
l.Formatter.AddValues(kvList)
|
||||
l.AddValues(kvList) // via Formatter
|
||||
return &l
|
||||
}
|
||||
|
||||
func (l fnlogger) WithCallDepth(depth int) logr.LogSink {
|
||||
l.Formatter.AddCallDepth(depth)
|
||||
l.AddCallDepth(depth) // via Formatter
|
||||
return &l
|
||||
}
|
||||
|
||||
|
||||
-180
@@ -1,180 +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.
|
||||
|
||||
package ptypes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
|
||||
anypb "github.com/golang/protobuf/ptypes/any"
|
||||
)
|
||||
|
||||
const urlPrefix = "type.googleapis.com/"
|
||||
|
||||
// AnyMessageName returns the message name contained in an anypb.Any message.
|
||||
// Most type assertions should use the Is function instead.
|
||||
//
|
||||
// Deprecated: Call the any.MessageName method instead.
|
||||
func AnyMessageName(any *anypb.Any) (string, error) {
|
||||
name, err := anyMessageName(any)
|
||||
return string(name), err
|
||||
}
|
||||
func anyMessageName(any *anypb.Any) (protoreflect.FullName, error) {
|
||||
if any == nil {
|
||||
return "", fmt.Errorf("message is nil")
|
||||
}
|
||||
name := protoreflect.FullName(any.TypeUrl)
|
||||
if i := strings.LastIndex(any.TypeUrl, "/"); i >= 0 {
|
||||
name = name[i+len("/"):]
|
||||
}
|
||||
if !name.IsValid() {
|
||||
return "", fmt.Errorf("message type url %q is invalid", any.TypeUrl)
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
|
||||
// MarshalAny marshals the given message m into an anypb.Any message.
|
||||
//
|
||||
// Deprecated: Call the anypb.New function instead.
|
||||
func MarshalAny(m proto.Message) (*anypb.Any, error) {
|
||||
switch dm := m.(type) {
|
||||
case DynamicAny:
|
||||
m = dm.Message
|
||||
case *DynamicAny:
|
||||
if dm == nil {
|
||||
return nil, proto.ErrNil
|
||||
}
|
||||
m = dm.Message
|
||||
}
|
||||
b, err := proto.Marshal(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &anypb.Any{TypeUrl: urlPrefix + proto.MessageName(m), Value: b}, nil
|
||||
}
|
||||
|
||||
// Empty returns a new message of the type specified in an anypb.Any message.
|
||||
// It returns protoregistry.NotFound if the corresponding message type could not
|
||||
// be resolved in the global registry.
|
||||
//
|
||||
// Deprecated: Use protoregistry.GlobalTypes.FindMessageByName instead
|
||||
// to resolve the message name and create a new instance of it.
|
||||
func Empty(any *anypb.Any) (proto.Message, error) {
|
||||
name, err := anyMessageName(any)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mt, err := protoregistry.GlobalTypes.FindMessageByName(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return proto.MessageV1(mt.New().Interface()), nil
|
||||
}
|
||||
|
||||
// UnmarshalAny unmarshals the encoded value contained in the anypb.Any message
|
||||
// into the provided message m. It returns an error if the target message
|
||||
// does not match the type in the Any message or if an unmarshal error occurs.
|
||||
//
|
||||
// The target message m may be a *DynamicAny message. If the underlying message
|
||||
// type could not be resolved, then this returns protoregistry.NotFound.
|
||||
//
|
||||
// Deprecated: Call the any.UnmarshalTo method instead.
|
||||
func UnmarshalAny(any *anypb.Any, m proto.Message) error {
|
||||
if dm, ok := m.(*DynamicAny); ok {
|
||||
if dm.Message == nil {
|
||||
var err error
|
||||
dm.Message, err = Empty(any)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
m = dm.Message
|
||||
}
|
||||
|
||||
anyName, err := AnyMessageName(any)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msgName := proto.MessageName(m)
|
||||
if anyName != msgName {
|
||||
return fmt.Errorf("mismatched message type: got %q want %q", anyName, msgName)
|
||||
}
|
||||
return proto.Unmarshal(any.Value, m)
|
||||
}
|
||||
|
||||
// Is reports whether the Any message contains a message of the specified type.
|
||||
//
|
||||
// Deprecated: Call the any.MessageIs method instead.
|
||||
func Is(any *anypb.Any, m proto.Message) bool {
|
||||
if any == nil || m == nil {
|
||||
return false
|
||||
}
|
||||
name := proto.MessageName(m)
|
||||
if !strings.HasSuffix(any.TypeUrl, name) {
|
||||
return false
|
||||
}
|
||||
return len(any.TypeUrl) == len(name) || any.TypeUrl[len(any.TypeUrl)-len(name)-1] == '/'
|
||||
}
|
||||
|
||||
// DynamicAny is a value that can be passed to UnmarshalAny to automatically
|
||||
// allocate a proto.Message for the type specified in an anypb.Any message.
|
||||
// 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)
|
||||
//
|
||||
// Deprecated: Use the any.UnmarshalNew method instead to unmarshal
|
||||
// the any message contents into a new instance of the underlying message.
|
||||
type DynamicAny struct{ proto.Message }
|
||||
|
||||
func (m DynamicAny) String() string {
|
||||
if m.Message == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return m.Message.String()
|
||||
}
|
||||
func (m DynamicAny) Reset() {
|
||||
if m.Message == nil {
|
||||
return
|
||||
}
|
||||
m.Message.Reset()
|
||||
}
|
||||
func (m DynamicAny) ProtoMessage() {
|
||||
return
|
||||
}
|
||||
func (m DynamicAny) ProtoReflect() protoreflect.Message {
|
||||
if m.Message == nil {
|
||||
return nil
|
||||
}
|
||||
return dynamicAny{proto.MessageReflect(m.Message)}
|
||||
}
|
||||
|
||||
type dynamicAny struct{ protoreflect.Message }
|
||||
|
||||
func (m dynamicAny) Type() protoreflect.MessageType {
|
||||
return dynamicAnyType{m.Message.Type()}
|
||||
}
|
||||
func (m dynamicAny) New() protoreflect.Message {
|
||||
return dynamicAnyType{m.Message.Type()}.New()
|
||||
}
|
||||
func (m dynamicAny) Interface() protoreflect.ProtoMessage {
|
||||
return DynamicAny{proto.MessageV1(m.Message.Interface())}
|
||||
}
|
||||
|
||||
type dynamicAnyType struct{ protoreflect.MessageType }
|
||||
|
||||
func (t dynamicAnyType) New() protoreflect.Message {
|
||||
return dynamicAny{t.MessageType.New()}
|
||||
}
|
||||
func (t dynamicAnyType) Zero() protoreflect.Message {
|
||||
return dynamicAny{t.MessageType.Zero()}
|
||||
}
|
||||
-10
@@ -1,10 +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.
|
||||
|
||||
// Package ptypes provides functionality for interacting with well-known types.
|
||||
//
|
||||
// Deprecated: Well-known types have specialized functionality directly
|
||||
// injected into the generated packages for each message type.
|
||||
// See the deprecation notice for each function for the suggested alternative.
|
||||
package ptypes
|
||||
-76
@@ -1,76 +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.
|
||||
|
||||
package ptypes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
durationpb "github.com/golang/protobuf/ptypes/duration"
|
||||
)
|
||||
|
||||
// Range of google.protobuf.Duration as specified in duration.proto.
|
||||
// This is about 10,000 years in seconds.
|
||||
const (
|
||||
maxSeconds = int64(10000 * 365.25 * 24 * 60 * 60)
|
||||
minSeconds = -maxSeconds
|
||||
)
|
||||
|
||||
// Duration converts a durationpb.Duration to a time.Duration.
|
||||
// Duration returns an error if dur is invalid or overflows a time.Duration.
|
||||
//
|
||||
// Deprecated: Call the dur.AsDuration and dur.CheckValid methods instead.
|
||||
func Duration(dur *durationpb.Duration) (time.Duration, error) {
|
||||
if err := validateDuration(dur); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d := time.Duration(dur.Seconds) * time.Second
|
||||
if int64(d/time.Second) != dur.Seconds {
|
||||
return 0, fmt.Errorf("duration: %v is out of range for time.Duration", dur)
|
||||
}
|
||||
if dur.Nanos != 0 {
|
||||
d += time.Duration(dur.Nanos) * time.Nanosecond
|
||||
if (d < 0) != (dur.Nanos < 0) {
|
||||
return 0, fmt.Errorf("duration: %v is out of range for time.Duration", dur)
|
||||
}
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// DurationProto converts a time.Duration to a durationpb.Duration.
|
||||
//
|
||||
// Deprecated: Call the durationpb.New function instead.
|
||||
func DurationProto(d time.Duration) *durationpb.Duration {
|
||||
nanos := d.Nanoseconds()
|
||||
secs := nanos / 1e9
|
||||
nanos -= secs * 1e9
|
||||
return &durationpb.Duration{
|
||||
Seconds: int64(secs),
|
||||
Nanos: int32(nanos),
|
||||
}
|
||||
}
|
||||
|
||||
// validateDuration determines whether the durationpb.Duration is valid
|
||||
// according to the definition in google/protobuf/duration.proto.
|
||||
// A valid durpb.Duration may still be too large to fit into a time.Duration
|
||||
// Note that the range of durationpb.Duration is about 10,000 years,
|
||||
// while the range of time.Duration is about 290 years.
|
||||
func validateDuration(dur *durationpb.Duration) error {
|
||||
if dur == nil {
|
||||
return errors.New("duration: nil Duration")
|
||||
}
|
||||
if dur.Seconds < minSeconds || dur.Seconds > maxSeconds {
|
||||
return fmt.Errorf("duration: %v: seconds out of range", dur)
|
||||
}
|
||||
if dur.Nanos <= -1e9 || dur.Nanos >= 1e9 {
|
||||
return fmt.Errorf("duration: %v: nanos out of range", dur)
|
||||
}
|
||||
// Seconds and Nanos must have the same sign, unless d.Nanos is zero.
|
||||
if (dur.Seconds < 0 && dur.Nanos > 0) || (dur.Seconds > 0 && dur.Nanos < 0) {
|
||||
return fmt.Errorf("duration: %v: seconds and nanos have different signs", dur)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: github.com/golang/protobuf/ptypes/duration/duration.proto
|
||||
|
||||
package duration
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
durationpb "google.golang.org/protobuf/types/known/durationpb"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// Symbols defined in public import of google/protobuf/duration.proto.
|
||||
|
||||
type Duration = durationpb.Duration
|
||||
|
||||
var File_github_com_golang_protobuf_ptypes_duration_duration_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_github_com_golang_protobuf_ptypes_duration_duration_proto_rawDesc = []byte{
|
||||
0x0a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
|
||||
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
|
||||
0x70, 0x65, 0x73, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x64, 0x75, 0x72,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x35, 0x5a, 0x33, 0x67,
|
||||
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67,
|
||||
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x73,
|
||||
0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var file_github_com_golang_protobuf_ptypes_duration_duration_proto_goTypes = []interface{}{}
|
||||
var file_github_com_golang_protobuf_ptypes_duration_duration_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_github_com_golang_protobuf_ptypes_duration_duration_proto_init() }
|
||||
func file_github_com_golang_protobuf_ptypes_duration_duration_proto_init() {
|
||||
if File_github_com_golang_protobuf_ptypes_duration_duration_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_github_com_golang_protobuf_ptypes_duration_duration_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 0,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_github_com_golang_protobuf_ptypes_duration_duration_proto_goTypes,
|
||||
DependencyIndexes: file_github_com_golang_protobuf_ptypes_duration_duration_proto_depIdxs,
|
||||
}.Build()
|
||||
File_github_com_golang_protobuf_ptypes_duration_duration_proto = out.File
|
||||
file_github_com_golang_protobuf_ptypes_duration_duration_proto_rawDesc = nil
|
||||
file_github_com_golang_protobuf_ptypes_duration_duration_proto_goTypes = nil
|
||||
file_github_com_golang_protobuf_ptypes_duration_duration_proto_depIdxs = nil
|
||||
}
|
||||
-112
@@ -1,112 +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.
|
||||
|
||||
package ptypes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
timestamppb "github.com/golang/protobuf/ptypes/timestamp"
|
||||
)
|
||||
|
||||
// Range of google.protobuf.Duration as specified in timestamp.proto.
|
||||
const (
|
||||
// Seconds field of the earliest valid Timestamp.
|
||||
// This is time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC).Unix().
|
||||
minValidSeconds = -62135596800
|
||||
// Seconds field just after the latest valid Timestamp.
|
||||
// This is time.Date(10000, 1, 1, 0, 0, 0, 0, time.UTC).Unix().
|
||||
maxValidSeconds = 253402300800
|
||||
)
|
||||
|
||||
// Timestamp converts a timestamppb.Timestamp to a time.Time.
|
||||
// It returns an error if the argument is invalid.
|
||||
//
|
||||
// Unlike most Go functions, if Timestamp returns an error, the first return
|
||||
// value is not the zero time.Time. Instead, it is the value obtained from the
|
||||
// time.Unix function when passed the contents of the Timestamp, in the UTC
|
||||
// locale. This may or may not be a meaningful time; many invalid Timestamps
|
||||
// do map to valid time.Times.
|
||||
//
|
||||
// A nil Timestamp returns an error. The first return value in that case is
|
||||
// undefined.
|
||||
//
|
||||
// Deprecated: Call the ts.AsTime and ts.CheckValid methods instead.
|
||||
func Timestamp(ts *timestamppb.Timestamp) (time.Time, error) {
|
||||
// Don't return the zero value on error, because corresponds to a valid
|
||||
// timestamp. Instead return whatever time.Unix gives us.
|
||||
var t time.Time
|
||||
if ts == nil {
|
||||
t = time.Unix(0, 0).UTC() // treat nil like the empty Timestamp
|
||||
} else {
|
||||
t = time.Unix(ts.Seconds, int64(ts.Nanos)).UTC()
|
||||
}
|
||||
return t, validateTimestamp(ts)
|
||||
}
|
||||
|
||||
// TimestampNow returns a google.protobuf.Timestamp for the current time.
|
||||
//
|
||||
// Deprecated: Call the timestamppb.Now function instead.
|
||||
func TimestampNow() *timestamppb.Timestamp {
|
||||
ts, err := TimestampProto(time.Now())
|
||||
if err != nil {
|
||||
panic("ptypes: time.Now() out of Timestamp range")
|
||||
}
|
||||
return ts
|
||||
}
|
||||
|
||||
// TimestampProto converts the time.Time to a google.protobuf.Timestamp proto.
|
||||
// It returns an error if the resulting Timestamp is invalid.
|
||||
//
|
||||
// Deprecated: Call the timestamppb.New function instead.
|
||||
func TimestampProto(t time.Time) (*timestamppb.Timestamp, error) {
|
||||
ts := ×tamppb.Timestamp{
|
||||
Seconds: t.Unix(),
|
||||
Nanos: int32(t.Nanosecond()),
|
||||
}
|
||||
if err := validateTimestamp(ts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ts, nil
|
||||
}
|
||||
|
||||
// TimestampString returns the RFC 3339 string for valid Timestamps.
|
||||
// For invalid Timestamps, it returns an error message in parentheses.
|
||||
//
|
||||
// Deprecated: Call the ts.AsTime method instead,
|
||||
// followed by a call to the Format method on the time.Time value.
|
||||
func TimestampString(ts *timestamppb.Timestamp) string {
|
||||
t, err := Timestamp(ts)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("(%v)", err)
|
||||
}
|
||||
return t.Format(time.RFC3339Nano)
|
||||
}
|
||||
|
||||
// validateTimestamp determines whether a Timestamp is valid.
|
||||
// A valid timestamp represents a time in the range [0001-01-01, 10000-01-01)
|
||||
// and has a Nanos field in the range [0, 1e9).
|
||||
//
|
||||
// If the Timestamp is valid, validateTimestamp returns nil.
|
||||
// Otherwise, it returns an error that describes the problem.
|
||||
//
|
||||
// Every valid Timestamp can be represented by a time.Time,
|
||||
// but the converse is not true.
|
||||
func validateTimestamp(ts *timestamppb.Timestamp) error {
|
||||
if ts == nil {
|
||||
return errors.New("timestamp: nil Timestamp")
|
||||
}
|
||||
if ts.Seconds < minValidSeconds {
|
||||
return fmt.Errorf("timestamp: %v before 0001-01-01", ts)
|
||||
}
|
||||
if ts.Seconds >= maxValidSeconds {
|
||||
return fmt.Errorf("timestamp: %v after 10000-01-01", ts)
|
||||
}
|
||||
if ts.Nanos < 0 || ts.Nanos >= 1e9 {
|
||||
return fmt.Errorf("timestamp: %v: nanos not in range [0, 1e9)", ts)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
yaml "go.yaml.in/yaml/v3"
|
||||
)
|
||||
|
||||
// Context contains state of the compiler as it traverses a document.
|
||||
|
||||
+5
-5
@@ -20,9 +20,9 @@ import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes/any"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
yaml "go.yaml.in/yaml/v3"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
extensions "github.com/google/gnostic-models/extensions"
|
||||
)
|
||||
@@ -33,7 +33,7 @@ type ExtensionHandler struct {
|
||||
}
|
||||
|
||||
// CallExtension calls a binary extension handler.
|
||||
func CallExtension(context *Context, in *yaml.Node, extensionName string) (handled bool, response *any.Any, err error) {
|
||||
func CallExtension(context *Context, in *yaml.Node, extensionName string) (handled bool, response *anypb.Any, err error) {
|
||||
if context == nil || context.ExtensionHandlers == nil {
|
||||
return false, nil, nil
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func CallExtension(context *Context, in *yaml.Node, extensionName string) (handl
|
||||
return handled, response, err
|
||||
}
|
||||
|
||||
func (extensionHandlers *ExtensionHandler) handle(in *yaml.Node, extensionName string) (*any.Any, error) {
|
||||
func (extensionHandlers *ExtensionHandler) handle(in *yaml.Node, extensionName string) (*anypb.Any, error) {
|
||||
if extensionHandlers.Name != "" {
|
||||
yamlData, _ := yaml.Marshal(in)
|
||||
request := &extensions.ExtensionHandlerRequest{
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
yaml "go.yaml.in/yaml/v3"
|
||||
|
||||
"github.com/google/gnostic-models/jsonschema"
|
||||
)
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
yaml "go.yaml.in/yaml/v3"
|
||||
)
|
||||
|
||||
var verboseReader = false
|
||||
|
||||
+19
-77
@@ -14,8 +14,8 @@
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.19.3
|
||||
// protoc-gen-go v1.35.1
|
||||
// protoc v4.23.4
|
||||
// source: extensions/extension.proto
|
||||
|
||||
package gnostic_extension_v1
|
||||
@@ -51,11 +51,9 @@ type Version struct {
|
||||
|
||||
func (x *Version) Reset() {
|
||||
*x = Version{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_extensions_extension_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
mi := &file_extensions_extension_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Version) String() string {
|
||||
@@ -66,7 +64,7 @@ func (*Version) ProtoMessage() {}
|
||||
|
||||
func (x *Version) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_extensions_extension_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
@@ -123,11 +121,9 @@ type ExtensionHandlerRequest struct {
|
||||
|
||||
func (x *ExtensionHandlerRequest) Reset() {
|
||||
*x = ExtensionHandlerRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_extensions_extension_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
mi := &file_extensions_extension_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ExtensionHandlerRequest) String() string {
|
||||
@@ -138,7 +134,7 @@ func (*ExtensionHandlerRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ExtensionHandlerRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_extensions_extension_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
@@ -191,11 +187,9 @@ type ExtensionHandlerResponse struct {
|
||||
|
||||
func (x *ExtensionHandlerResponse) Reset() {
|
||||
*x = ExtensionHandlerResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_extensions_extension_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
mi := &file_extensions_extension_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ExtensionHandlerResponse) String() string {
|
||||
@@ -206,7 +200,7 @@ func (*ExtensionHandlerResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ExtensionHandlerResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_extensions_extension_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
@@ -257,11 +251,9 @@ type Wrapper struct {
|
||||
|
||||
func (x *Wrapper) Reset() {
|
||||
*x = Wrapper{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_extensions_extension_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
mi := &file_extensions_extension_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Wrapper) String() string {
|
||||
@@ -272,7 +264,7 @@ func (*Wrapper) ProtoMessage() {}
|
||||
|
||||
func (x *Wrapper) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_extensions_extension_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
@@ -367,7 +359,7 @@ func file_extensions_extension_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_extensions_extension_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_extensions_extension_proto_goTypes = []interface{}{
|
||||
var file_extensions_extension_proto_goTypes = []any{
|
||||
(*Version)(nil), // 0: gnostic.extension.v1.Version
|
||||
(*ExtensionHandlerRequest)(nil), // 1: gnostic.extension.v1.ExtensionHandlerRequest
|
||||
(*ExtensionHandlerResponse)(nil), // 2: gnostic.extension.v1.ExtensionHandlerResponse
|
||||
@@ -390,56 +382,6 @@ func file_extensions_extension_proto_init() {
|
||||
if File_extensions_extension_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_extensions_extension_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Version); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_extensions_extension_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ExtensionHandlerRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_extensions_extension_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ExtensionHandlerResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_extensions_extension_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Wrapper); 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{
|
||||
|
||||
+3
-3
@@ -19,8 +19,8 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
type extensionHandler func(name string, yamlInput string) (bool, proto.Message, error)
|
||||
@@ -54,7 +54,7 @@ func Main(handler extensionHandler) {
|
||||
response.Errors = append(response.Errors, err.Error())
|
||||
} else if handled {
|
||||
response.Handled = true
|
||||
response.Value, err = ptypes.MarshalAny(output)
|
||||
response.Value, err = anypb.New(output)
|
||||
if err != nil {
|
||||
response.Errors = append(response.Errors, err.Error())
|
||||
}
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
// of JSON Schemas.
|
||||
package jsonschema
|
||||
|
||||
import "gopkg.in/yaml.v3"
|
||||
import "go.yaml.in/yaml/v3"
|
||||
|
||||
// The Schema struct models a JSON Schema and, because schemas are
|
||||
// defined hierarchically, contains many references to itself.
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ import (
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
yaml "go.yaml.in/yaml/v3"
|
||||
)
|
||||
|
||||
// This is a global map of all known Schemas.
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ package jsonschema
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
yaml "go.yaml.in/yaml/v3"
|
||||
)
|
||||
|
||||
const indentation = " "
|
||||
|
||||
+40
-40
@@ -21,7 +21,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
yaml "go.yaml.in/yaml/v3"
|
||||
|
||||
"github.com/google/gnostic-models/compiler"
|
||||
)
|
||||
@@ -60,7 +60,7 @@ func NewAdditionalPropertiesItem(in *yaml.Node, context *compiler.Context) (*Add
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid AdditionalPropertiesItem")
|
||||
message := "contains an invalid AdditionalPropertiesItem"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -2543,7 +2543,7 @@ func NewNonBodyParameter(in *yaml.Node, context *compiler.Context) (*NonBodyPara
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid NonBodyParameter")
|
||||
message := "contains an invalid NonBodyParameter"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -3271,7 +3271,7 @@ func NewParameter(in *yaml.Node, context *compiler.Context) (*Parameter, error)
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid Parameter")
|
||||
message := "contains an invalid Parameter"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -3345,7 +3345,7 @@ func NewParametersItem(in *yaml.Node, context *compiler.Context) (*ParametersIte
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid ParametersItem")
|
||||
message := "contains an invalid ParametersItem"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -4561,7 +4561,7 @@ func NewResponseValue(in *yaml.Node, context *compiler.Context) (*ResponseValue,
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid ResponseValue")
|
||||
message := "contains an invalid ResponseValue"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -5030,7 +5030,7 @@ func NewSchemaItem(in *yaml.Node, context *compiler.Context) (*SchemaItem, error
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid SchemaItem")
|
||||
message := "contains an invalid SchemaItem"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -5160,7 +5160,7 @@ func NewSecurityDefinitionsItem(in *yaml.Node, context *compiler.Context) (*Secu
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid SecurityDefinitionsItem")
|
||||
message := "contains an invalid SecurityDefinitionsItem"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -6930,7 +6930,7 @@ func (m *BodyParameter) ToRawInfo() *yaml.Node {
|
||||
// always include this required field.
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("in"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString(m.In))
|
||||
if m.Required != false {
|
||||
if m.Required {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("required"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Required))
|
||||
}
|
||||
@@ -7149,7 +7149,7 @@ func (m *FileSchema) ToRawInfo() *yaml.Node {
|
||||
// always include this required field.
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("type"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type))
|
||||
if m.ReadOnly != false {
|
||||
if m.ReadOnly {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("readOnly"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ReadOnly))
|
||||
}
|
||||
@@ -7176,7 +7176,7 @@ func (m *FormDataParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Required != false {
|
||||
if m.Required {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("required"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Required))
|
||||
}
|
||||
@@ -7192,7 +7192,7 @@ func (m *FormDataParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("name"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name))
|
||||
}
|
||||
if m.AllowEmptyValue != false {
|
||||
if m.AllowEmptyValue {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("allowEmptyValue"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.AllowEmptyValue))
|
||||
}
|
||||
@@ -7220,7 +7220,7 @@ func (m *FormDataParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("maximum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Maximum))
|
||||
}
|
||||
if m.ExclusiveMaximum != false {
|
||||
if m.ExclusiveMaximum {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMaximum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMaximum))
|
||||
}
|
||||
@@ -7228,7 +7228,7 @@ func (m *FormDataParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("minimum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Minimum))
|
||||
}
|
||||
if m.ExclusiveMinimum != false {
|
||||
if m.ExclusiveMinimum {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMinimum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMinimum))
|
||||
}
|
||||
@@ -7252,7 +7252,7 @@ func (m *FormDataParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("minItems"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinItems))
|
||||
}
|
||||
if m.UniqueItems != false {
|
||||
if m.UniqueItems {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("uniqueItems"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.UniqueItems))
|
||||
}
|
||||
@@ -7306,7 +7306,7 @@ func (m *Header) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("maximum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Maximum))
|
||||
}
|
||||
if m.ExclusiveMaximum != false {
|
||||
if m.ExclusiveMaximum {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMaximum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMaximum))
|
||||
}
|
||||
@@ -7314,7 +7314,7 @@ func (m *Header) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("minimum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Minimum))
|
||||
}
|
||||
if m.ExclusiveMinimum != false {
|
||||
if m.ExclusiveMinimum {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMinimum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMinimum))
|
||||
}
|
||||
@@ -7338,7 +7338,7 @@ func (m *Header) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("minItems"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinItems))
|
||||
}
|
||||
if m.UniqueItems != false {
|
||||
if m.UniqueItems {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("uniqueItems"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.UniqueItems))
|
||||
}
|
||||
@@ -7373,7 +7373,7 @@ func (m *HeaderParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Required != false {
|
||||
if m.Required {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("required"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Required))
|
||||
}
|
||||
@@ -7413,7 +7413,7 @@ func (m *HeaderParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("maximum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Maximum))
|
||||
}
|
||||
if m.ExclusiveMaximum != false {
|
||||
if m.ExclusiveMaximum {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMaximum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMaximum))
|
||||
}
|
||||
@@ -7421,7 +7421,7 @@ func (m *HeaderParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("minimum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Minimum))
|
||||
}
|
||||
if m.ExclusiveMinimum != false {
|
||||
if m.ExclusiveMinimum {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMinimum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMinimum))
|
||||
}
|
||||
@@ -7445,7 +7445,7 @@ func (m *HeaderParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("minItems"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinItems))
|
||||
}
|
||||
if m.UniqueItems != false {
|
||||
if m.UniqueItems {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("uniqueItems"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.UniqueItems))
|
||||
}
|
||||
@@ -7940,7 +7940,7 @@ func (m *Operation) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("schemes"))
|
||||
info.Content = append(info.Content, compiler.NewSequenceNodeForStringArray(m.Schemes))
|
||||
}
|
||||
if m.Deprecated != false {
|
||||
if m.Deprecated {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("deprecated"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Deprecated))
|
||||
}
|
||||
@@ -8110,7 +8110,7 @@ func (m *PathParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("maximum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Maximum))
|
||||
}
|
||||
if m.ExclusiveMaximum != false {
|
||||
if m.ExclusiveMaximum {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMaximum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMaximum))
|
||||
}
|
||||
@@ -8118,7 +8118,7 @@ func (m *PathParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("minimum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Minimum))
|
||||
}
|
||||
if m.ExclusiveMinimum != false {
|
||||
if m.ExclusiveMinimum {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMinimum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMinimum))
|
||||
}
|
||||
@@ -8142,7 +8142,7 @@ func (m *PathParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("minItems"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinItems))
|
||||
}
|
||||
if m.UniqueItems != false {
|
||||
if m.UniqueItems {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("uniqueItems"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.UniqueItems))
|
||||
}
|
||||
@@ -8218,7 +8218,7 @@ func (m *PrimitivesItems) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("maximum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Maximum))
|
||||
}
|
||||
if m.ExclusiveMaximum != false {
|
||||
if m.ExclusiveMaximum {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMaximum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMaximum))
|
||||
}
|
||||
@@ -8226,7 +8226,7 @@ func (m *PrimitivesItems) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("minimum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Minimum))
|
||||
}
|
||||
if m.ExclusiveMinimum != false {
|
||||
if m.ExclusiveMinimum {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMinimum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMinimum))
|
||||
}
|
||||
@@ -8250,7 +8250,7 @@ func (m *PrimitivesItems) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("minItems"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinItems))
|
||||
}
|
||||
if m.UniqueItems != false {
|
||||
if m.UniqueItems {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("uniqueItems"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.UniqueItems))
|
||||
}
|
||||
@@ -8296,7 +8296,7 @@ func (m *QueryParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Required != false {
|
||||
if m.Required {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("required"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Required))
|
||||
}
|
||||
@@ -8312,7 +8312,7 @@ func (m *QueryParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("name"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name))
|
||||
}
|
||||
if m.AllowEmptyValue != false {
|
||||
if m.AllowEmptyValue {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("allowEmptyValue"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.AllowEmptyValue))
|
||||
}
|
||||
@@ -8340,7 +8340,7 @@ func (m *QueryParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("maximum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Maximum))
|
||||
}
|
||||
if m.ExclusiveMaximum != false {
|
||||
if m.ExclusiveMaximum {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMaximum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMaximum))
|
||||
}
|
||||
@@ -8348,7 +8348,7 @@ func (m *QueryParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("minimum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Minimum))
|
||||
}
|
||||
if m.ExclusiveMinimum != false {
|
||||
if m.ExclusiveMinimum {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMinimum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMinimum))
|
||||
}
|
||||
@@ -8372,7 +8372,7 @@ func (m *QueryParameterSubSchema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("minItems"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinItems))
|
||||
}
|
||||
if m.UniqueItems != false {
|
||||
if m.UniqueItems {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("uniqueItems"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.UniqueItems))
|
||||
}
|
||||
@@ -8514,7 +8514,7 @@ func (m *Schema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("maximum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Maximum))
|
||||
}
|
||||
if m.ExclusiveMaximum != false {
|
||||
if m.ExclusiveMaximum {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMaximum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMaximum))
|
||||
}
|
||||
@@ -8522,7 +8522,7 @@ func (m *Schema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("minimum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Minimum))
|
||||
}
|
||||
if m.ExclusiveMinimum != false {
|
||||
if m.ExclusiveMinimum {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMinimum"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMinimum))
|
||||
}
|
||||
@@ -8546,7 +8546,7 @@ func (m *Schema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("minItems"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinItems))
|
||||
}
|
||||
if m.UniqueItems != false {
|
||||
if m.UniqueItems {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("uniqueItems"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.UniqueItems))
|
||||
}
|
||||
@@ -8610,7 +8610,7 @@ func (m *Schema) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("discriminator"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Discriminator))
|
||||
}
|
||||
if m.ReadOnly != false {
|
||||
if m.ReadOnly {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("readOnly"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ReadOnly))
|
||||
}
|
||||
@@ -8796,11 +8796,11 @@ func (m *Xml) ToRawInfo() *yaml.Node {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("prefix"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Prefix))
|
||||
}
|
||||
if m.Attribute != false {
|
||||
if m.Attribute {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("attribute"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Attribute))
|
||||
}
|
||||
if m.Wrapped != false {
|
||||
if m.Wrapped {
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForString("wrapped"))
|
||||
info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Wrapped))
|
||||
}
|
||||
|
||||
+257
-1092
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -15,7 +15,7 @@
|
||||
package openapi_v2
|
||||
|
||||
import (
|
||||
"gopkg.in/yaml.v3"
|
||||
yaml "go.yaml.in/yaml/v3"
|
||||
|
||||
"github.com/google/gnostic-models/compiler"
|
||||
)
|
||||
|
||||
+12
-12
@@ -21,7 +21,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
yaml "go.yaml.in/yaml/v3"
|
||||
|
||||
"github.com/google/gnostic-models/compiler"
|
||||
)
|
||||
@@ -60,7 +60,7 @@ func NewAdditionalPropertiesItem(in *yaml.Node, context *compiler.Context) (*Add
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid AdditionalPropertiesItem")
|
||||
message := "contains an invalid AdditionalPropertiesItem"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ func NewAnyOrExpression(in *yaml.Node, context *compiler.Context) (*AnyOrExpress
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid AnyOrExpression")
|
||||
message := "contains an invalid AnyOrExpression"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -227,7 +227,7 @@ func NewCallbackOrReference(in *yaml.Node, context *compiler.Context) (*Callback
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid CallbackOrReference")
|
||||
message := "contains an invalid CallbackOrReference"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -979,7 +979,7 @@ func NewExampleOrReference(in *yaml.Node, context *compiler.Context) (*ExampleOr
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid ExampleOrReference")
|
||||
message := "contains an invalid ExampleOrReference"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -1320,7 +1320,7 @@ func NewHeaderOrReference(in *yaml.Node, context *compiler.Context) (*HeaderOrRe
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid HeaderOrReference")
|
||||
message := "contains an invalid HeaderOrReference"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -1713,7 +1713,7 @@ func NewLinkOrReference(in *yaml.Node, context *compiler.Context) (*LinkOrRefere
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid LinkOrReference")
|
||||
message := "contains an invalid LinkOrReference"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -3090,7 +3090,7 @@ func NewParameterOrReference(in *yaml.Node, context *compiler.Context) (*Paramet
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid ParameterOrReference")
|
||||
message := "contains an invalid ParameterOrReference"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -3606,7 +3606,7 @@ func NewRequestBodyOrReference(in *yaml.Node, context *compiler.Context) (*Reque
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid RequestBodyOrReference")
|
||||
message := "contains an invalid RequestBodyOrReference"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -3743,7 +3743,7 @@ func NewResponseOrReference(in *yaml.Node, context *compiler.Context) (*Response
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid ResponseOrReference")
|
||||
message := "contains an invalid ResponseOrReference"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -4310,7 +4310,7 @@ func NewSchemaOrReference(in *yaml.Node, context *compiler.Context) (*SchemaOrRe
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid SchemaOrReference")
|
||||
message := "contains an invalid SchemaOrReference"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
@@ -4543,7 +4543,7 @@ func NewSecuritySchemeOrReference(in *yaml.Node, context *compiler.Context) (*Se
|
||||
// since the oneof matched one of its possibilities, discard any matching errors
|
||||
errors = make([]error, 0)
|
||||
} else {
|
||||
message := fmt.Sprintf("contains an invalid SecuritySchemeOrReference")
|
||||
message := "contains an invalid SecuritySchemeOrReference"
|
||||
err := compiler.NewError(context, message)
|
||||
errors = []error{err}
|
||||
}
|
||||
|
||||
+341
-1422
File diff suppressed because it is too large
Load Diff
+182
@@ -0,0 +1,182 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.35.1
|
||||
// protoc v4.23.4
|
||||
// source: openapiv3/annotations.proto
|
||||
|
||||
package openapi_v3
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
descriptorpb "google.golang.org/protobuf/types/descriptorpb"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
var file_openapiv3_annotations_proto_extTypes = []protoimpl.ExtensionInfo{
|
||||
{
|
||||
ExtendedType: (*descriptorpb.FileOptions)(nil),
|
||||
ExtensionType: (*Document)(nil),
|
||||
Field: 1143,
|
||||
Name: "openapi.v3.document",
|
||||
Tag: "bytes,1143,opt,name=document",
|
||||
Filename: "openapiv3/annotations.proto",
|
||||
},
|
||||
{
|
||||
ExtendedType: (*descriptorpb.MethodOptions)(nil),
|
||||
ExtensionType: (*Operation)(nil),
|
||||
Field: 1143,
|
||||
Name: "openapi.v3.operation",
|
||||
Tag: "bytes,1143,opt,name=operation",
|
||||
Filename: "openapiv3/annotations.proto",
|
||||
},
|
||||
{
|
||||
ExtendedType: (*descriptorpb.MessageOptions)(nil),
|
||||
ExtensionType: (*Schema)(nil),
|
||||
Field: 1143,
|
||||
Name: "openapi.v3.schema",
|
||||
Tag: "bytes,1143,opt,name=schema",
|
||||
Filename: "openapiv3/annotations.proto",
|
||||
},
|
||||
{
|
||||
ExtendedType: (*descriptorpb.FieldOptions)(nil),
|
||||
ExtensionType: (*Schema)(nil),
|
||||
Field: 1143,
|
||||
Name: "openapi.v3.property",
|
||||
Tag: "bytes,1143,opt,name=property",
|
||||
Filename: "openapiv3/annotations.proto",
|
||||
},
|
||||
}
|
||||
|
||||
// Extension fields to descriptorpb.FileOptions.
|
||||
var (
|
||||
// optional openapi.v3.Document document = 1143;
|
||||
E_Document = &file_openapiv3_annotations_proto_extTypes[0]
|
||||
)
|
||||
|
||||
// Extension fields to descriptorpb.MethodOptions.
|
||||
var (
|
||||
// optional openapi.v3.Operation operation = 1143;
|
||||
E_Operation = &file_openapiv3_annotations_proto_extTypes[1]
|
||||
)
|
||||
|
||||
// Extension fields to descriptorpb.MessageOptions.
|
||||
var (
|
||||
// optional openapi.v3.Schema schema = 1143;
|
||||
E_Schema = &file_openapiv3_annotations_proto_extTypes[2]
|
||||
)
|
||||
|
||||
// Extension fields to descriptorpb.FieldOptions.
|
||||
var (
|
||||
// optional openapi.v3.Schema property = 1143;
|
||||
E_Property = &file_openapiv3_annotations_proto_extTypes[3]
|
||||
)
|
||||
|
||||
var File_openapiv3_annotations_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_openapiv3_annotations_proto_rawDesc = []byte{
|
||||
0x0a, 0x1b, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2f, 0x61, 0x6e, 0x6e, 0x6f,
|
||||
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x6f,
|
||||
0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 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, 0x1a, 0x19, 0x6f, 0x70, 0x65,
|
||||
0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x41, 0x50, 0x49, 0x76, 0x33,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3a, 0x4f, 0x0a, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65,
|
||||
0x6e, 0x74, 0x12, 0x1c, 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,
|
||||
0x18, 0xf7, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70,
|
||||
0x69, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x64,
|
||||
0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x3a, 0x54, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 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, 0xf7, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70,
|
||||
0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x4c, 0x0a,
|
||||
0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 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, 0x18, 0xf7, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x12, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x68,
|
||||
0x65, 0x6d, 0x61, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x3a, 0x4e, 0x0a, 0x08, 0x70,
|
||||
0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 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, 0xf7, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e,
|
||||
0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d,
|
||||
0x61, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x42, 0x42, 0x0a, 0x0e, 0x6f,
|
||||
0x72, 0x67, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x33, 0x42, 0x10, 0x41,
|
||||
0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50,
|
||||
0x01, 0x5a, 0x16, 0x2e, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x3b, 0x6f,
|
||||
0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x33, 0xa2, 0x02, 0x03, 0x4f, 0x41, 0x53, 0x62,
|
||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var file_openapiv3_annotations_proto_goTypes = []any{
|
||||
(*descriptorpb.FileOptions)(nil), // 0: google.protobuf.FileOptions
|
||||
(*descriptorpb.MethodOptions)(nil), // 1: google.protobuf.MethodOptions
|
||||
(*descriptorpb.MessageOptions)(nil), // 2: google.protobuf.MessageOptions
|
||||
(*descriptorpb.FieldOptions)(nil), // 3: google.protobuf.FieldOptions
|
||||
(*Document)(nil), // 4: openapi.v3.Document
|
||||
(*Operation)(nil), // 5: openapi.v3.Operation
|
||||
(*Schema)(nil), // 6: openapi.v3.Schema
|
||||
}
|
||||
var file_openapiv3_annotations_proto_depIdxs = []int32{
|
||||
0, // 0: openapi.v3.document:extendee -> google.protobuf.FileOptions
|
||||
1, // 1: openapi.v3.operation:extendee -> google.protobuf.MethodOptions
|
||||
2, // 2: openapi.v3.schema:extendee -> google.protobuf.MessageOptions
|
||||
3, // 3: openapi.v3.property:extendee -> google.protobuf.FieldOptions
|
||||
4, // 4: openapi.v3.document:type_name -> openapi.v3.Document
|
||||
5, // 5: openapi.v3.operation:type_name -> openapi.v3.Operation
|
||||
6, // 6: openapi.v3.schema:type_name -> openapi.v3.Schema
|
||||
6, // 7: openapi.v3.property:type_name -> openapi.v3.Schema
|
||||
8, // [8:8] is the sub-list for method output_type
|
||||
8, // [8:8] is the sub-list for method input_type
|
||||
4, // [4:8] is the sub-list for extension type_name
|
||||
0, // [0:4] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_openapiv3_annotations_proto_init() }
|
||||
func file_openapiv3_annotations_proto_init() {
|
||||
if File_openapiv3_annotations_proto != nil {
|
||||
return
|
||||
}
|
||||
file_openapiv3_OpenAPIv3_proto_init()
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_openapiv3_annotations_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 0,
|
||||
NumExtensions: 4,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_openapiv3_annotations_proto_goTypes,
|
||||
DependencyIndexes: file_openapiv3_annotations_proto_depIdxs,
|
||||
ExtensionInfos: file_openapiv3_annotations_proto_extTypes,
|
||||
}.Build()
|
||||
File_openapiv3_annotations_proto = out.File
|
||||
file_openapiv3_annotations_proto_rawDesc = nil
|
||||
file_openapiv3_annotations_proto_goTypes = nil
|
||||
file_openapiv3_annotations_proto_depIdxs = nil
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package openapi.v3;
|
||||
|
||||
import "google/protobuf/descriptor.proto";
|
||||
import "openapiv3/OpenAPIv3.proto";
|
||||
|
||||
// The Go package name.
|
||||
option go_package = "./openapiv3;openapi_v3";
|
||||
// This option lets the proto compiler generate Java code inside the package
|
||||
// name (see below) instead of inside an outer class. It creates a simpler
|
||||
// developer experience by reducing one-level of name nesting and be
|
||||
// consistent with most programming languages that don't support outer classes.
|
||||
option java_multiple_files = true;
|
||||
// The Java outer classname should be the filename in UpperCamelCase. This
|
||||
// class is only used to hold proto descriptor, so developers don't need to
|
||||
// work with it directly.
|
||||
option java_outer_classname = "AnnotationsProto";
|
||||
// The Java package name must be proto package name with proper prefix.
|
||||
option java_package = "org.openapi_v3";
|
||||
// A reasonable prefix for the Objective-C symbols generated from the package.
|
||||
// It should at a minimum be 3 characters long, all uppercase, and convention
|
||||
// is to use an abbreviation of the package name. Something short, but
|
||||
// hopefully unique enough to not conflict with things that may come along in
|
||||
// the future. 'GPB' is reserved for the protocol buffer implementation itself.
|
||||
option objc_class_prefix = "OAS";
|
||||
|
||||
extend google.protobuf.FileOptions {
|
||||
Document document = 1143;
|
||||
}
|
||||
|
||||
extend google.protobuf.MethodOptions {
|
||||
Operation operation = 1143;
|
||||
}
|
||||
|
||||
extend google.protobuf.MessageOptions {
|
||||
Schema schema = 1143;
|
||||
}
|
||||
|
||||
extend google.protobuf.FieldOptions {
|
||||
Schema property = 1143;
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
package openapi_v3
|
||||
|
||||
import (
|
||||
"gopkg.in/yaml.v3"
|
||||
yaml "go.yaml.in/yaml/v3"
|
||||
|
||||
"github.com/google/gnostic-models/compiler"
|
||||
)
|
||||
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- master
|
||||
|
||||
script:
|
||||
- go test -cover
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
# How to contribute #
|
||||
|
||||
We'd love to accept your patches and contributions to this project. There are
|
||||
just a few small guidelines you need to follow.
|
||||
|
||||
|
||||
## Contributor License Agreement ##
|
||||
|
||||
Contributions to any Google project must be accompanied by a Contributor
|
||||
License Agreement. This is not a copyright **assignment**, it simply gives
|
||||
Google permission to use and redistribute your contributions as part of the
|
||||
project.
|
||||
|
||||
* If you are an individual writing original source code and you're sure you
|
||||
own the intellectual property, then you'll need to sign an [individual
|
||||
CLA][].
|
||||
|
||||
* If you work for a company that wants to allow you to contribute your work,
|
||||
then you'll need to sign a [corporate CLA][].
|
||||
|
||||
You generally only need to submit a CLA once, so if you've already submitted
|
||||
one (even if it was for a different project), you probably don't need to do it
|
||||
again.
|
||||
|
||||
[individual CLA]: https://developers.google.com/open-source/cla/individual
|
||||
[corporate CLA]: https://developers.google.com/open-source/cla/corporate
|
||||
|
||||
|
||||
## Submitting a patch ##
|
||||
|
||||
1. It's generally best to start by opening a new issue describing the bug or
|
||||
feature you're intending to fix. Even if you think it's relatively minor,
|
||||
it's helpful to know what people are working on. Mention in the initial
|
||||
issue that you are planning to work on that bug or feature so that it can
|
||||
be assigned to you.
|
||||
|
||||
1. Follow the normal process of [forking][] the project, and setup a new
|
||||
branch to work in. It's important that each group of changes be done in
|
||||
separate branches in order to ensure that a pull request only includes the
|
||||
commits related to that bug or feature.
|
||||
|
||||
1. Go makes it very simple to ensure properly formatted code, so always run
|
||||
`go fmt` on your code before committing it. You should also run
|
||||
[golint][] over your code. As noted in the [golint readme][], it's not
|
||||
strictly necessary that your code be completely "lint-free", but this will
|
||||
help you find common style issues.
|
||||
|
||||
1. Any significant changes should almost always be accompanied by tests. The
|
||||
project already has good test coverage, so look at some of the existing
|
||||
tests if you're unsure how to go about it. [gocov][] and [gocov-html][]
|
||||
are invaluable tools for seeing which parts of your code aren't being
|
||||
exercised by your tests.
|
||||
|
||||
1. Do your best to have [well-formed commit messages][] for each change.
|
||||
This provides consistency throughout the project, and ensures that commit
|
||||
messages are able to be formatted properly by various git tools.
|
||||
|
||||
1. Finally, push the commits to your fork and submit a [pull request][].
|
||||
|
||||
[forking]: https://help.github.com/articles/fork-a-repo
|
||||
[golint]: https://github.com/golang/lint
|
||||
[golint readme]: https://github.com/golang/lint/blob/master/README
|
||||
[gocov]: https://github.com/axw/gocov
|
||||
[gocov-html]: https://github.com/matm/gocov-html
|
||||
[well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
||||
[squash]: http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits
|
||||
[pull request]: https://help.github.com/articles/creating-a-pull-request
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
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 fuzz is a library for populating go objects with random values.
|
||||
package fuzz
|
||||
-605
@@ -1,605 +0,0 @@
|
||||
/*
|
||||
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 fuzz
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/google/gofuzz/bytesource"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// fuzzFuncMap is a map from a type to a fuzzFunc that handles that type.
|
||||
type fuzzFuncMap map[reflect.Type]reflect.Value
|
||||
|
||||
// Fuzzer knows how to fill any object with random fields.
|
||||
type Fuzzer struct {
|
||||
fuzzFuncs fuzzFuncMap
|
||||
defaultFuzzFuncs fuzzFuncMap
|
||||
r *rand.Rand
|
||||
nilChance float64
|
||||
minElements int
|
||||
maxElements int
|
||||
maxDepth int
|
||||
skipFieldPatterns []*regexp.Regexp
|
||||
}
|
||||
|
||||
// New returns a new Fuzzer. Customize your Fuzzer further by calling Funcs,
|
||||
// RandSource, NilChance, or NumElements in any order.
|
||||
func New() *Fuzzer {
|
||||
return NewWithSeed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
func NewWithSeed(seed int64) *Fuzzer {
|
||||
f := &Fuzzer{
|
||||
defaultFuzzFuncs: fuzzFuncMap{
|
||||
reflect.TypeOf(&time.Time{}): reflect.ValueOf(fuzzTime),
|
||||
},
|
||||
|
||||
fuzzFuncs: fuzzFuncMap{},
|
||||
r: rand.New(rand.NewSource(seed)),
|
||||
nilChance: .2,
|
||||
minElements: 1,
|
||||
maxElements: 10,
|
||||
maxDepth: 100,
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// NewFromGoFuzz is a helper function that enables using gofuzz (this
|
||||
// project) with go-fuzz (https://github.com/dvyukov/go-fuzz) for continuous
|
||||
// fuzzing. Essentially, it enables translating the fuzzing bytes from
|
||||
// go-fuzz to any Go object using this library.
|
||||
//
|
||||
// This implementation promises a constant translation from a given slice of
|
||||
// bytes to the fuzzed objects. This promise will remain over future
|
||||
// versions of Go and of this library.
|
||||
//
|
||||
// Note: the returned Fuzzer should not be shared between multiple goroutines,
|
||||
// as its deterministic output will no longer be available.
|
||||
//
|
||||
// Example: use go-fuzz to test the function `MyFunc(int)` in the package
|
||||
// `mypackage`. Add the file: "mypacakge_fuzz.go" with the content:
|
||||
//
|
||||
// // +build gofuzz
|
||||
// package mypacakge
|
||||
// import fuzz "github.com/google/gofuzz"
|
||||
// func Fuzz(data []byte) int {
|
||||
// var i int
|
||||
// fuzz.NewFromGoFuzz(data).Fuzz(&i)
|
||||
// MyFunc(i)
|
||||
// return 0
|
||||
// }
|
||||
func NewFromGoFuzz(data []byte) *Fuzzer {
|
||||
return New().RandSource(bytesource.New(data))
|
||||
}
|
||||
|
||||
// Funcs adds each entry in fuzzFuncs as a custom fuzzing function.
|
||||
//
|
||||
// Each entry in fuzzFuncs must be a function taking two parameters.
|
||||
// The first parameter must be a pointer or map. It is the variable that
|
||||
// function will fill with random data. The second parameter must be a
|
||||
// fuzz.Continue, which will provide a source of randomness and a way
|
||||
// to automatically continue fuzzing smaller pieces of the first parameter.
|
||||
//
|
||||
// These functions are called sensibly, e.g., if you wanted custom string
|
||||
// fuzzing, the function `func(s *string, c fuzz.Continue)` would get
|
||||
// called and passed the address of strings. Maps and pointers will always
|
||||
// be made/new'd for you, ignoring the NilChange option. For slices, it
|
||||
// doesn't make much sense to pre-create them--Fuzzer doesn't know how
|
||||
// long you want your slice--so take a pointer to a slice, and make it
|
||||
// yourself. (If you don't want your map/pointer type pre-made, take a
|
||||
// pointer to it, and make it yourself.) See the examples for a range of
|
||||
// custom functions.
|
||||
func (f *Fuzzer) Funcs(fuzzFuncs ...interface{}) *Fuzzer {
|
||||
for i := range fuzzFuncs {
|
||||
v := reflect.ValueOf(fuzzFuncs[i])
|
||||
if v.Kind() != reflect.Func {
|
||||
panic("Need only funcs!")
|
||||
}
|
||||
t := v.Type()
|
||||
if t.NumIn() != 2 || t.NumOut() != 0 {
|
||||
panic("Need 2 in and 0 out params!")
|
||||
}
|
||||
argT := t.In(0)
|
||||
switch argT.Kind() {
|
||||
case reflect.Ptr, reflect.Map:
|
||||
default:
|
||||
panic("fuzzFunc must take pointer or map type")
|
||||
}
|
||||
if t.In(1) != reflect.TypeOf(Continue{}) {
|
||||
panic("fuzzFunc's second parameter must be type fuzz.Continue")
|
||||
}
|
||||
f.fuzzFuncs[argT] = v
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// RandSource causes f to get values from the given source of randomness.
|
||||
// Use if you want deterministic fuzzing.
|
||||
func (f *Fuzzer) RandSource(s rand.Source) *Fuzzer {
|
||||
f.r = rand.New(s)
|
||||
return f
|
||||
}
|
||||
|
||||
// NilChance sets the probability of creating a nil pointer, map, or slice to
|
||||
// 'p'. 'p' should be between 0 (no nils) and 1 (all nils), inclusive.
|
||||
func (f *Fuzzer) NilChance(p float64) *Fuzzer {
|
||||
if p < 0 || p > 1 {
|
||||
panic("p should be between 0 and 1, inclusive.")
|
||||
}
|
||||
f.nilChance = p
|
||||
return f
|
||||
}
|
||||
|
||||
// NumElements sets the minimum and maximum number of elements that will be
|
||||
// added to a non-nil map or slice.
|
||||
func (f *Fuzzer) NumElements(atLeast, atMost int) *Fuzzer {
|
||||
if atLeast > atMost {
|
||||
panic("atLeast must be <= atMost")
|
||||
}
|
||||
if atLeast < 0 {
|
||||
panic("atLeast must be >= 0")
|
||||
}
|
||||
f.minElements = atLeast
|
||||
f.maxElements = atMost
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Fuzzer) genElementCount() int {
|
||||
if f.minElements == f.maxElements {
|
||||
return f.minElements
|
||||
}
|
||||
return f.minElements + f.r.Intn(f.maxElements-f.minElements+1)
|
||||
}
|
||||
|
||||
func (f *Fuzzer) genShouldFill() bool {
|
||||
return f.r.Float64() >= f.nilChance
|
||||
}
|
||||
|
||||
// MaxDepth sets the maximum number of recursive fuzz calls that will be made
|
||||
// before stopping. This includes struct members, pointers, and map and slice
|
||||
// elements.
|
||||
func (f *Fuzzer) MaxDepth(d int) *Fuzzer {
|
||||
f.maxDepth = d
|
||||
return f
|
||||
}
|
||||
|
||||
// Skip fields which match the supplied pattern. Call this multiple times if needed
|
||||
// This is useful to skip XXX_ fields generated by protobuf
|
||||
func (f *Fuzzer) SkipFieldsWithPattern(pattern *regexp.Regexp) *Fuzzer {
|
||||
f.skipFieldPatterns = append(f.skipFieldPatterns, pattern)
|
||||
return f
|
||||
}
|
||||
|
||||
// Fuzz recursively fills all of obj's fields with something random. First
|
||||
// this tries to find a custom fuzz function (see Funcs). If there is no
|
||||
// custom function this tests whether the object implements fuzz.Interface and,
|
||||
// if so, calls Fuzz on it to fuzz itself. If that fails, this will see if
|
||||
// there is a default fuzz function provided by this package. If all of that
|
||||
// fails, this will generate random values for all primitive fields and then
|
||||
// recurse for all non-primitives.
|
||||
//
|
||||
// This is safe for cyclic or tree-like structs, up to a limit. Use the
|
||||
// MaxDepth method to adjust how deep you need it to recurse.
|
||||
//
|
||||
// obj must be a pointer. Only exported (public) fields can be set (thanks,
|
||||
// golang :/ ) Intended for tests, so will panic on bad input or unimplemented
|
||||
// fields.
|
||||
func (f *Fuzzer) Fuzz(obj interface{}) {
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
panic("needed ptr!")
|
||||
}
|
||||
v = v.Elem()
|
||||
f.fuzzWithContext(v, 0)
|
||||
}
|
||||
|
||||
// FuzzNoCustom is just like Fuzz, except that any custom fuzz function for
|
||||
// obj's type will not be called and obj will not be tested for fuzz.Interface
|
||||
// conformance. This applies only to obj and not other instances of obj's
|
||||
// type.
|
||||
// Not safe for cyclic or tree-like structs!
|
||||
// obj must be a pointer. Only exported (public) fields can be set (thanks, golang :/ )
|
||||
// Intended for tests, so will panic on bad input or unimplemented fields.
|
||||
func (f *Fuzzer) FuzzNoCustom(obj interface{}) {
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
panic("needed ptr!")
|
||||
}
|
||||
v = v.Elem()
|
||||
f.fuzzWithContext(v, flagNoCustomFuzz)
|
||||
}
|
||||
|
||||
const (
|
||||
// Do not try to find a custom fuzz function. Does not apply recursively.
|
||||
flagNoCustomFuzz uint64 = 1 << iota
|
||||
)
|
||||
|
||||
func (f *Fuzzer) fuzzWithContext(v reflect.Value, flags uint64) {
|
||||
fc := &fuzzerContext{fuzzer: f}
|
||||
fc.doFuzz(v, flags)
|
||||
}
|
||||
|
||||
// fuzzerContext carries context about a single fuzzing run, which lets Fuzzer
|
||||
// be thread-safe.
|
||||
type fuzzerContext struct {
|
||||
fuzzer *Fuzzer
|
||||
curDepth int
|
||||
}
|
||||
|
||||
func (fc *fuzzerContext) doFuzz(v reflect.Value, flags uint64) {
|
||||
if fc.curDepth >= fc.fuzzer.maxDepth {
|
||||
return
|
||||
}
|
||||
fc.curDepth++
|
||||
defer func() { fc.curDepth-- }()
|
||||
|
||||
if !v.CanSet() {
|
||||
return
|
||||
}
|
||||
|
||||
if flags&flagNoCustomFuzz == 0 {
|
||||
// Check for both pointer and non-pointer custom functions.
|
||||
if v.CanAddr() && fc.tryCustom(v.Addr()) {
|
||||
return
|
||||
}
|
||||
if fc.tryCustom(v) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if fn, ok := fillFuncMap[v.Kind()]; ok {
|
||||
fn(v, fc.fuzzer.r)
|
||||
return
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Map:
|
||||
if fc.fuzzer.genShouldFill() {
|
||||
v.Set(reflect.MakeMap(v.Type()))
|
||||
n := fc.fuzzer.genElementCount()
|
||||
for i := 0; i < n; i++ {
|
||||
key := reflect.New(v.Type().Key()).Elem()
|
||||
fc.doFuzz(key, 0)
|
||||
val := reflect.New(v.Type().Elem()).Elem()
|
||||
fc.doFuzz(val, 0)
|
||||
v.SetMapIndex(key, val)
|
||||
}
|
||||
return
|
||||
}
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
case reflect.Ptr:
|
||||
if fc.fuzzer.genShouldFill() {
|
||||
v.Set(reflect.New(v.Type().Elem()))
|
||||
fc.doFuzz(v.Elem(), 0)
|
||||
return
|
||||
}
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
case reflect.Slice:
|
||||
if fc.fuzzer.genShouldFill() {
|
||||
n := fc.fuzzer.genElementCount()
|
||||
v.Set(reflect.MakeSlice(v.Type(), n, n))
|
||||
for i := 0; i < n; i++ {
|
||||
fc.doFuzz(v.Index(i), 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
case reflect.Array:
|
||||
if fc.fuzzer.genShouldFill() {
|
||||
n := v.Len()
|
||||
for i := 0; i < n; i++ {
|
||||
fc.doFuzz(v.Index(i), 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
case reflect.Struct:
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
skipField := false
|
||||
fieldName := v.Type().Field(i).Name
|
||||
for _, pattern := range fc.fuzzer.skipFieldPatterns {
|
||||
if pattern.MatchString(fieldName) {
|
||||
skipField = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !skipField {
|
||||
fc.doFuzz(v.Field(i), 0)
|
||||
}
|
||||
}
|
||||
case reflect.Chan:
|
||||
fallthrough
|
||||
case reflect.Func:
|
||||
fallthrough
|
||||
case reflect.Interface:
|
||||
fallthrough
|
||||
default:
|
||||
panic(fmt.Sprintf("Can't handle %#v", v.Interface()))
|
||||
}
|
||||
}
|
||||
|
||||
// tryCustom searches for custom handlers, and returns true iff it finds a match
|
||||
// and successfully randomizes v.
|
||||
func (fc *fuzzerContext) tryCustom(v reflect.Value) bool {
|
||||
// First: see if we have a fuzz function for it.
|
||||
doCustom, ok := fc.fuzzer.fuzzFuncs[v.Type()]
|
||||
if !ok {
|
||||
// Second: see if it can fuzz itself.
|
||||
if v.CanInterface() {
|
||||
intf := v.Interface()
|
||||
if fuzzable, ok := intf.(Interface); ok {
|
||||
fuzzable.Fuzz(Continue{fc: fc, Rand: fc.fuzzer.r})
|
||||
return true
|
||||
}
|
||||
}
|
||||
// Finally: see if there is a default fuzz function.
|
||||
doCustom, ok = fc.fuzzer.defaultFuzzFuncs[v.Type()]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Ptr:
|
||||
if v.IsNil() {
|
||||
if !v.CanSet() {
|
||||
return false
|
||||
}
|
||||
v.Set(reflect.New(v.Type().Elem()))
|
||||
}
|
||||
case reflect.Map:
|
||||
if v.IsNil() {
|
||||
if !v.CanSet() {
|
||||
return false
|
||||
}
|
||||
v.Set(reflect.MakeMap(v.Type()))
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
doCustom.Call([]reflect.Value{v, reflect.ValueOf(Continue{
|
||||
fc: fc,
|
||||
Rand: fc.fuzzer.r,
|
||||
})})
|
||||
return true
|
||||
}
|
||||
|
||||
// Interface represents an object that knows how to fuzz itself. Any time we
|
||||
// find a type that implements this interface we will delegate the act of
|
||||
// fuzzing itself.
|
||||
type Interface interface {
|
||||
Fuzz(c Continue)
|
||||
}
|
||||
|
||||
// Continue can be passed to custom fuzzing functions to allow them to use
|
||||
// the correct source of randomness and to continue fuzzing their members.
|
||||
type Continue struct {
|
||||
fc *fuzzerContext
|
||||
|
||||
// For convenience, Continue implements rand.Rand via embedding.
|
||||
// Use this for generating any randomness if you want your fuzzing
|
||||
// to be repeatable for a given seed.
|
||||
*rand.Rand
|
||||
}
|
||||
|
||||
// Fuzz continues fuzzing obj. obj must be a pointer.
|
||||
func (c Continue) Fuzz(obj interface{}) {
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
panic("needed ptr!")
|
||||
}
|
||||
v = v.Elem()
|
||||
c.fc.doFuzz(v, 0)
|
||||
}
|
||||
|
||||
// FuzzNoCustom continues fuzzing obj, except that any custom fuzz function for
|
||||
// obj's type will not be called and obj will not be tested for fuzz.Interface
|
||||
// conformance. This applies only to obj and not other instances of obj's
|
||||
// type.
|
||||
func (c Continue) FuzzNoCustom(obj interface{}) {
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
panic("needed ptr!")
|
||||
}
|
||||
v = v.Elem()
|
||||
c.fc.doFuzz(v, flagNoCustomFuzz)
|
||||
}
|
||||
|
||||
// RandString makes a random string up to 20 characters long. The returned string
|
||||
// may include a variety of (valid) UTF-8 encodings.
|
||||
func (c Continue) RandString() string {
|
||||
return randString(c.Rand)
|
||||
}
|
||||
|
||||
// RandUint64 makes random 64 bit numbers.
|
||||
// Weirdly, rand doesn't have a function that gives you 64 random bits.
|
||||
func (c Continue) RandUint64() uint64 {
|
||||
return randUint64(c.Rand)
|
||||
}
|
||||
|
||||
// RandBool returns true or false randomly.
|
||||
func (c Continue) RandBool() bool {
|
||||
return randBool(c.Rand)
|
||||
}
|
||||
|
||||
func fuzzInt(v reflect.Value, r *rand.Rand) {
|
||||
v.SetInt(int64(randUint64(r)))
|
||||
}
|
||||
|
||||
func fuzzUint(v reflect.Value, r *rand.Rand) {
|
||||
v.SetUint(randUint64(r))
|
||||
}
|
||||
|
||||
func fuzzTime(t *time.Time, c Continue) {
|
||||
var sec, nsec int64
|
||||
// Allow for about 1000 years of random time values, which keeps things
|
||||
// like JSON parsing reasonably happy.
|
||||
sec = c.Rand.Int63n(1000 * 365 * 24 * 60 * 60)
|
||||
c.Fuzz(&nsec)
|
||||
*t = time.Unix(sec, nsec)
|
||||
}
|
||||
|
||||
var fillFuncMap = map[reflect.Kind]func(reflect.Value, *rand.Rand){
|
||||
reflect.Bool: func(v reflect.Value, r *rand.Rand) {
|
||||
v.SetBool(randBool(r))
|
||||
},
|
||||
reflect.Int: fuzzInt,
|
||||
reflect.Int8: fuzzInt,
|
||||
reflect.Int16: fuzzInt,
|
||||
reflect.Int32: fuzzInt,
|
||||
reflect.Int64: fuzzInt,
|
||||
reflect.Uint: fuzzUint,
|
||||
reflect.Uint8: fuzzUint,
|
||||
reflect.Uint16: fuzzUint,
|
||||
reflect.Uint32: fuzzUint,
|
||||
reflect.Uint64: fuzzUint,
|
||||
reflect.Uintptr: fuzzUint,
|
||||
reflect.Float32: func(v reflect.Value, r *rand.Rand) {
|
||||
v.SetFloat(float64(r.Float32()))
|
||||
},
|
||||
reflect.Float64: func(v reflect.Value, r *rand.Rand) {
|
||||
v.SetFloat(r.Float64())
|
||||
},
|
||||
reflect.Complex64: func(v reflect.Value, r *rand.Rand) {
|
||||
v.SetComplex(complex128(complex(r.Float32(), r.Float32())))
|
||||
},
|
||||
reflect.Complex128: func(v reflect.Value, r *rand.Rand) {
|
||||
v.SetComplex(complex(r.Float64(), r.Float64()))
|
||||
},
|
||||
reflect.String: func(v reflect.Value, r *rand.Rand) {
|
||||
v.SetString(randString(r))
|
||||
},
|
||||
reflect.UnsafePointer: func(v reflect.Value, r *rand.Rand) {
|
||||
panic("unimplemented")
|
||||
},
|
||||
}
|
||||
|
||||
// randBool returns true or false randomly.
|
||||
func randBool(r *rand.Rand) bool {
|
||||
return r.Int31()&(1<<30) == 0
|
||||
}
|
||||
|
||||
type int63nPicker interface {
|
||||
Int63n(int64) int64
|
||||
}
|
||||
|
||||
// UnicodeRange describes a sequential range of unicode characters.
|
||||
// Last must be numerically greater than First.
|
||||
type UnicodeRange struct {
|
||||
First, Last rune
|
||||
}
|
||||
|
||||
// UnicodeRanges describes an arbitrary number of sequential ranges of unicode characters.
|
||||
// To be useful, each range must have at least one character (First <= Last) and
|
||||
// there must be at least one range.
|
||||
type UnicodeRanges []UnicodeRange
|
||||
|
||||
// choose returns a random unicode character from the given range, using the
|
||||
// given randomness source.
|
||||
func (ur UnicodeRange) choose(r int63nPicker) rune {
|
||||
count := int64(ur.Last - ur.First + 1)
|
||||
return ur.First + rune(r.Int63n(count))
|
||||
}
|
||||
|
||||
// CustomStringFuzzFunc constructs a FuzzFunc which produces random strings.
|
||||
// Each character is selected from the range ur. If there are no characters
|
||||
// in the range (cr.Last < cr.First), this will panic.
|
||||
func (ur UnicodeRange) CustomStringFuzzFunc() func(s *string, c Continue) {
|
||||
ur.check()
|
||||
return func(s *string, c Continue) {
|
||||
*s = ur.randString(c.Rand)
|
||||
}
|
||||
}
|
||||
|
||||
// check is a function that used to check whether the first of ur(UnicodeRange)
|
||||
// is greater than the last one.
|
||||
func (ur UnicodeRange) check() {
|
||||
if ur.Last < ur.First {
|
||||
panic("The last encoding must be greater than the first one.")
|
||||
}
|
||||
}
|
||||
|
||||
// randString of UnicodeRange makes a random string up to 20 characters long.
|
||||
// Each character is selected form ur(UnicodeRange).
|
||||
func (ur UnicodeRange) randString(r *rand.Rand) string {
|
||||
n := r.Intn(20)
|
||||
sb := strings.Builder{}
|
||||
sb.Grow(n)
|
||||
for i := 0; i < n; i++ {
|
||||
sb.WriteRune(ur.choose(r))
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// defaultUnicodeRanges sets a default unicode range when user do not set
|
||||
// CustomStringFuzzFunc() but wants fuzz string.
|
||||
var defaultUnicodeRanges = UnicodeRanges{
|
||||
{' ', '~'}, // ASCII characters
|
||||
{'\u00a0', '\u02af'}, // Multi-byte encoded characters
|
||||
{'\u4e00', '\u9fff'}, // Common CJK (even longer encodings)
|
||||
}
|
||||
|
||||
// CustomStringFuzzFunc constructs a FuzzFunc which produces random strings.
|
||||
// Each character is selected from one of the ranges of ur(UnicodeRanges).
|
||||
// Each range has an equal probability of being chosen. If there are no ranges,
|
||||
// or a selected range has no characters (.Last < .First), this will panic.
|
||||
// Do not modify any of the ranges in ur after calling this function.
|
||||
func (ur UnicodeRanges) CustomStringFuzzFunc() func(s *string, c Continue) {
|
||||
// Check unicode ranges slice is empty.
|
||||
if len(ur) == 0 {
|
||||
panic("UnicodeRanges is empty.")
|
||||
}
|
||||
// if not empty, each range should be checked.
|
||||
for i := range ur {
|
||||
ur[i].check()
|
||||
}
|
||||
return func(s *string, c Continue) {
|
||||
*s = ur.randString(c.Rand)
|
||||
}
|
||||
}
|
||||
|
||||
// randString of UnicodeRanges makes a random string up to 20 characters long.
|
||||
// Each character is selected form one of the ranges of ur(UnicodeRanges),
|
||||
// and each range has an equal probability of being chosen.
|
||||
func (ur UnicodeRanges) randString(r *rand.Rand) string {
|
||||
n := r.Intn(20)
|
||||
sb := strings.Builder{}
|
||||
sb.Grow(n)
|
||||
for i := 0; i < n; i++ {
|
||||
sb.WriteRune(ur[r.Intn(len(ur))].choose(r))
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// randString makes a random string up to 20 characters long. The returned string
|
||||
// may include a variety of (valid) UTF-8 encodings.
|
||||
func randString(r *rand.Rand) string {
|
||||
return defaultUnicodeRanges.randString(r)
|
||||
}
|
||||
|
||||
// randUint64 makes random 64 bit numbers.
|
||||
// Weirdly, rand doesn't have a function that gives you 64 random bits.
|
||||
func randUint64(r *rand.Rand) uint64 {
|
||||
return uint64(r.Uint32())<<32 | uint64(r.Uint32())
|
||||
}
|
||||
+5
-12
@@ -7,19 +7,13 @@ Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
|
||||
[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
|
||||
|
||||
|
||||
---
|
||||
|
||||
⚠️ **[The Gorilla WebSocket Package is looking for a new maintainer](https://github.com/gorilla/websocket/issues/370)**
|
||||
|
||||
---
|
||||
|
||||
### Documentation
|
||||
|
||||
* [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc)
|
||||
* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat)
|
||||
* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)
|
||||
* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)
|
||||
* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch)
|
||||
* [Chat example](https://github.com/gorilla/websocket/tree/main/examples/chat)
|
||||
* [Command example](https://github.com/gorilla/websocket/tree/main/examples/command)
|
||||
* [Client and server example](https://github.com/gorilla/websocket/tree/main/examples/echo)
|
||||
* [File watch example](https://github.com/gorilla/websocket/tree/main/examples/filewatch)
|
||||
|
||||
### Status
|
||||
|
||||
@@ -35,5 +29,4 @@ package API is stable.
|
||||
|
||||
The Gorilla WebSocket package passes the server tests in the [Autobahn Test
|
||||
Suite](https://github.com/crossbario/autobahn-testsuite) using the application in the [examples/autobahn
|
||||
subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn).
|
||||
|
||||
subdirectory](https://github.com/gorilla/websocket/tree/main/examples/autobahn).
|
||||
|
||||
+170
-75
@@ -9,8 +9,8 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
@@ -51,18 +51,34 @@ func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufS
|
||||
//
|
||||
// It is safe to call Dialer's methods concurrently.
|
||||
type Dialer struct {
|
||||
// The following custom dial functions can be set to establish
|
||||
// connections to either the backend server or the proxy (if it
|
||||
// exists). The scheme of the dialed entity (either backend or
|
||||
// proxy) determines which custom dial function is selected:
|
||||
// either NetDialTLSContext for HTTPS or NetDialContext/NetDial
|
||||
// for HTTP. Since the "Proxy" function can determine the scheme
|
||||
// dynamically, it can make sense to set multiple custom dial
|
||||
// functions simultaneously.
|
||||
//
|
||||
// NetDial specifies the dial function for creating TCP connections. If
|
||||
// NetDial is nil, net.Dial is used.
|
||||
// NetDial is nil, net.Dialer DialContext is used.
|
||||
// If "Proxy" field is also set, this function dials the proxy--not
|
||||
// the backend server.
|
||||
NetDial func(network, addr string) (net.Conn, error)
|
||||
|
||||
// NetDialContext specifies the dial function for creating TCP connections. If
|
||||
// NetDialContext is nil, NetDial is used.
|
||||
// If "Proxy" field is also set, this function dials the proxy--not
|
||||
// the backend server.
|
||||
NetDialContext func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
|
||||
// NetDialTLSContext specifies the dial function for creating TLS/TCP connections. If
|
||||
// NetDialTLSContext is nil, NetDialContext is used.
|
||||
// If NetDialTLSContext is set, Dial assumes the TLS handshake is done there and
|
||||
// TLSClientConfig is ignored.
|
||||
// If "Proxy" field is also set, this function dials the proxy (and performs
|
||||
// the TLS handshake with the proxy, ignoring TLSClientConfig). In this TLS proxy
|
||||
// dialing case the TLSClientConfig could still be necessary for TLS to the backend server.
|
||||
NetDialTLSContext func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
|
||||
// Proxy specifies a function to return a proxy for a given
|
||||
@@ -73,7 +89,7 @@ type Dialer struct {
|
||||
|
||||
// TLSClientConfig specifies the TLS configuration to use with tls.Client.
|
||||
// If nil, the default configuration is used.
|
||||
// If either NetDialTLS or NetDialTLSContext are set, Dial assumes the TLS handshake
|
||||
// If NetDialTLSContext is set, Dial assumes the TLS handshake
|
||||
// is done there and TLSClientConfig is ignored.
|
||||
TLSClientConfig *tls.Config
|
||||
|
||||
@@ -244,71 +260,16 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
// Get network dial function.
|
||||
var netDial func(network, add string) (net.Conn, error)
|
||||
|
||||
switch u.Scheme {
|
||||
case "http":
|
||||
if d.NetDialContext != nil {
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return d.NetDialContext(ctx, network, addr)
|
||||
}
|
||||
} else if d.NetDial != nil {
|
||||
netDial = d.NetDial
|
||||
}
|
||||
case "https":
|
||||
if d.NetDialTLSContext != nil {
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return d.NetDialTLSContext(ctx, network, addr)
|
||||
}
|
||||
} else if d.NetDialContext != nil {
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return d.NetDialContext(ctx, network, addr)
|
||||
}
|
||||
} else if d.NetDial != nil {
|
||||
netDial = d.NetDial
|
||||
}
|
||||
default:
|
||||
return nil, nil, errMalformedURL
|
||||
}
|
||||
|
||||
if netDial == nil {
|
||||
netDialer := &net.Dialer{}
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return netDialer.DialContext(ctx, network, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// If needed, wrap the dial function to set the connection deadline.
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
forwardDial := netDial
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
c, err := forwardDial(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = c.SetDeadline(deadline)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If needed, wrap the dial function to connect through a proxy.
|
||||
var proxyURL *url.URL
|
||||
if d.Proxy != nil {
|
||||
proxyURL, err := d.Proxy(req)
|
||||
proxyURL, err = d.Proxy(req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if proxyURL != nil {
|
||||
dialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
netDial = dialer.Dial
|
||||
}
|
||||
}
|
||||
netDial, err := d.netDialFn(ctx, proxyURL, u)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
hostPort, hostNoPort := hostPortNoPort(u)
|
||||
@@ -317,24 +278,30 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
trace.GetConn(hostPort)
|
||||
}
|
||||
|
||||
netConn, err := netDial("tcp", hostPort)
|
||||
netConn, err := netDial(ctx, "tcp", hostPort)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if trace != nil && trace.GotConn != nil {
|
||||
trace.GotConn(httptrace.GotConnInfo{
|
||||
Conn: netConn,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Close the network connection when returning an error. The variable
|
||||
// netConn is set to nil before the success return at the end of the
|
||||
// function.
|
||||
defer func() {
|
||||
if netConn != nil {
|
||||
netConn.Close()
|
||||
// It's safe to ignore the error from Close() because this code is
|
||||
// only executed when returning a more important error to the
|
||||
// application.
|
||||
_ = netConn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if u.Scheme == "https" && d.NetDialTLSContext == nil {
|
||||
// If NetDialTLSContext is set, assume that the TLS handshake has already been done
|
||||
// Do TLS handshake over established connection if a proxy exists.
|
||||
if proxyURL != nil && u.Scheme == "https" {
|
||||
|
||||
cfg := cloneTLSConfig(d.TLSClientConfig)
|
||||
if cfg.ServerName == "" {
|
||||
@@ -370,6 +337,17 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
|
||||
resp, err := http.ReadResponse(conn.br, req)
|
||||
if err != nil {
|
||||
if d.TLSClientConfig != nil {
|
||||
for _, proto := range d.TLSClientConfig.NextProtos {
|
||||
if proto != "http/1.1" {
|
||||
return nil, nil, fmt.Errorf(
|
||||
"websocket: protocol %q was given but is not supported;"+
|
||||
"sharing tls.Config with net/http Transport can cause this error: %w",
|
||||
proto, err,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -388,7 +366,7 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
// debugging.
|
||||
buf := make([]byte, 1024)
|
||||
n, _ := io.ReadFull(resp.Body, buf)
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n]))
|
||||
resp.Body = io.NopCloser(bytes.NewReader(buf[:n]))
|
||||
return nil, resp, ErrBadHandshake
|
||||
}
|
||||
|
||||
@@ -406,17 +384,134 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
break
|
||||
}
|
||||
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
||||
resp.Body = io.NopCloser(bytes.NewReader([]byte{}))
|
||||
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
|
||||
|
||||
netConn.SetDeadline(time.Time{})
|
||||
netConn = nil // to avoid close in defer.
|
||||
if err := netConn.SetDeadline(time.Time{}); err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
// Success! Set netConn to nil to stop the deferred function above from
|
||||
// closing the network connection.
|
||||
netConn = nil
|
||||
|
||||
return conn, resp, nil
|
||||
}
|
||||
|
||||
// Returns the dial function to establish the connection to either the backend
|
||||
// server or the proxy (if it exists). If the dialed entity is HTTPS, then the
|
||||
// returned dial function *also* performs the TLS handshake to the dialed entity.
|
||||
// NOTE: If a proxy exists, it is possible for a second TLS handshake to be
|
||||
// necessary over the established connection.
|
||||
func (d *Dialer) netDialFn(ctx context.Context, proxyURL *url.URL, backendURL *url.URL) (netDialerFunc, error) {
|
||||
var netDial netDialerFunc
|
||||
if proxyURL != nil {
|
||||
netDial = d.netDialFromURL(proxyURL)
|
||||
} else {
|
||||
netDial = d.netDialFromURL(backendURL)
|
||||
}
|
||||
// If needed, wrap the dial function to set the connection deadline.
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
netDial = netDialWithDeadline(netDial, deadline)
|
||||
}
|
||||
// Proxy dialing is wrapped to implement CONNECT method and possibly proxy auth.
|
||||
if proxyURL != nil {
|
||||
return proxyFromURL(proxyURL, netDial)
|
||||
}
|
||||
return netDial, nil
|
||||
}
|
||||
|
||||
// Returns function to create the connection depending on the Dialer's
|
||||
// custom dialing functions and the passed URL of entity connecting to.
|
||||
func (d *Dialer) netDialFromURL(u *url.URL) netDialerFunc {
|
||||
var netDial netDialerFunc
|
||||
switch {
|
||||
case d.NetDialContext != nil:
|
||||
netDial = d.NetDialContext
|
||||
case d.NetDial != nil:
|
||||
netDial = func(ctx context.Context, net, addr string) (net.Conn, error) {
|
||||
return d.NetDial(net, addr)
|
||||
}
|
||||
default:
|
||||
netDial = (&net.Dialer{}).DialContext
|
||||
}
|
||||
// If dialed entity is HTTPS, then either use custom TLS dialing function (if exists)
|
||||
// or wrap the previously computed "netDial" to use TLS config for handshake.
|
||||
if u.Scheme == "https" {
|
||||
if d.NetDialTLSContext != nil {
|
||||
netDial = d.NetDialTLSContext
|
||||
} else {
|
||||
netDial = netDialWithTLSHandshake(netDial, d.TLSClientConfig, u)
|
||||
}
|
||||
}
|
||||
return netDial
|
||||
}
|
||||
|
||||
// Returns wrapped "netDial" function, performing TLS handshake after connecting.
|
||||
func netDialWithTLSHandshake(netDial netDialerFunc, tlsConfig *tls.Config, u *url.URL) netDialerFunc {
|
||||
return func(ctx context.Context, unused, addr string) (net.Conn, error) {
|
||||
hostPort, hostNoPort := hostPortNoPort(u)
|
||||
trace := httptrace.ContextClientTrace(ctx)
|
||||
if trace != nil && trace.GetConn != nil {
|
||||
trace.GetConn(hostPort)
|
||||
}
|
||||
// Creates TCP connection to addr using passed "netDial" function.
|
||||
conn, err := netDial(ctx, "tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg := cloneTLSConfig(tlsConfig)
|
||||
if cfg.ServerName == "" {
|
||||
cfg.ServerName = hostNoPort
|
||||
}
|
||||
tlsConn := tls.Client(conn, cfg)
|
||||
// Do the TLS handshake using TLSConfig over the wrapped connection.
|
||||
if trace != nil && trace.TLSHandshakeStart != nil {
|
||||
trace.TLSHandshakeStart()
|
||||
}
|
||||
err = doHandshake(ctx, tlsConn, cfg)
|
||||
if trace != nil && trace.TLSHandshakeDone != nil {
|
||||
trace.TLSHandshakeDone(tlsConn.ConnectionState(), err)
|
||||
}
|
||||
if err != nil {
|
||||
tlsConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return tlsConn, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Returns wrapped "netDial" function, setting passed deadline.
|
||||
func netDialWithDeadline(netDial netDialerFunc, deadline time.Time) netDialerFunc {
|
||||
return func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
c, err := netDial(ctx, network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = c.SetDeadline(deadline)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||
if cfg == nil {
|
||||
return &tls.Config{}
|
||||
}
|
||||
return cfg.Clone()
|
||||
}
|
||||
|
||||
func doHandshake(ctx context.Context, tlsConn *tls.Conn, cfg *tls.Config) error {
|
||||
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if !cfg.InsecureSkipVerify {
|
||||
if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+5
-1
@@ -33,7 +33,11 @@ func decompressNoContextTakeover(r io.Reader) io.ReadCloser {
|
||||
"\x01\x00\x00\xff\xff"
|
||||
|
||||
fr, _ := flateReaderPool.Get().(io.ReadCloser)
|
||||
fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)
|
||||
mr := io.MultiReader(r, strings.NewReader(tail))
|
||||
if err := fr.(flate.Resetter).Reset(mr, nil); err != nil {
|
||||
// Reset never fails, but handle error in case that changes.
|
||||
fr = flate.NewReader(mr)
|
||||
}
|
||||
return &flateReadWrapper{fr}
|
||||
}
|
||||
|
||||
|
||||
+64
-48
@@ -6,11 +6,10 @@ package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -181,16 +180,16 @@ var (
|
||||
errInvalidControlFrame = errors.New("websocket: invalid control frame")
|
||||
)
|
||||
|
||||
func newMaskKey() [4]byte {
|
||||
n := rand.Uint32()
|
||||
return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
|
||||
}
|
||||
// maskRand is an io.Reader for generating mask bytes. The reader is initialized
|
||||
// to crypto/rand Reader. Tests swap the reader to a math/rand reader for
|
||||
// reproducible results.
|
||||
var maskRand = rand.Reader
|
||||
|
||||
func hideTempErr(err error) error {
|
||||
if e, ok := err.(net.Error); ok && e.Temporary() {
|
||||
err = &netError{msg: e.Error(), timeout: e.Timeout()}
|
||||
}
|
||||
return err
|
||||
// newMaskKey returns a new 32 bit value for masking client frames.
|
||||
func newMaskKey() [4]byte {
|
||||
var k [4]byte
|
||||
_, _ = io.ReadFull(maskRand, k[:])
|
||||
return k
|
||||
}
|
||||
|
||||
func isControl(frameType int) bool {
|
||||
@@ -358,7 +357,6 @@ func (c *Conn) RemoteAddr() net.Addr {
|
||||
// Write methods
|
||||
|
||||
func (c *Conn) writeFatal(err error) error {
|
||||
err = hideTempErr(err)
|
||||
c.writeErrMu.Lock()
|
||||
if c.writeErr == nil {
|
||||
c.writeErr = err
|
||||
@@ -372,7 +370,9 @@ func (c *Conn) read(n int) ([]byte, error) {
|
||||
if err == io.EOF {
|
||||
err = errUnexpectedEOF
|
||||
}
|
||||
c.br.Discard(len(p))
|
||||
// Discard is guaranteed to succeed because the number of bytes to discard
|
||||
// is less than or equal to the number of bytes buffered.
|
||||
_, _ = c.br.Discard(len(p))
|
||||
return p, err
|
||||
}
|
||||
|
||||
@@ -387,7 +387,9 @@ func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error
|
||||
return err
|
||||
}
|
||||
|
||||
c.conn.SetWriteDeadline(deadline)
|
||||
if err := c.conn.SetWriteDeadline(deadline); err != nil {
|
||||
return c.writeFatal(err)
|
||||
}
|
||||
if len(buf1) == 0 {
|
||||
_, err = c.conn.Write(buf0)
|
||||
} else {
|
||||
@@ -397,7 +399,7 @@ func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error
|
||||
return c.writeFatal(err)
|
||||
}
|
||||
if frameType == CloseMessage {
|
||||
c.writeFatal(ErrCloseSent)
|
||||
_ = c.writeFatal(ErrCloseSent)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -436,21 +438,27 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er
|
||||
maskBytes(key, 0, buf[6:])
|
||||
}
|
||||
|
||||
d := 1000 * time.Hour
|
||||
if !deadline.IsZero() {
|
||||
d = deadline.Sub(time.Now())
|
||||
if deadline.IsZero() {
|
||||
// No timeout for zero time.
|
||||
<-c.mu
|
||||
} else {
|
||||
d := time.Until(deadline)
|
||||
if d < 0 {
|
||||
return errWriteTimeout
|
||||
}
|
||||
select {
|
||||
case <-c.mu:
|
||||
default:
|
||||
timer := time.NewTimer(d)
|
||||
select {
|
||||
case <-c.mu:
|
||||
timer.Stop()
|
||||
case <-timer.C:
|
||||
return errWriteTimeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timer := time.NewTimer(d)
|
||||
select {
|
||||
case <-c.mu:
|
||||
timer.Stop()
|
||||
case <-timer.C:
|
||||
return errWriteTimeout
|
||||
}
|
||||
defer func() { c.mu <- struct{}{} }()
|
||||
|
||||
c.writeErrMu.Lock()
|
||||
@@ -460,13 +468,14 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er
|
||||
return err
|
||||
}
|
||||
|
||||
c.conn.SetWriteDeadline(deadline)
|
||||
_, err = c.conn.Write(buf)
|
||||
if err != nil {
|
||||
if err := c.conn.SetWriteDeadline(deadline); err != nil {
|
||||
return c.writeFatal(err)
|
||||
}
|
||||
if _, err = c.conn.Write(buf); err != nil {
|
||||
return c.writeFatal(err)
|
||||
}
|
||||
if messageType == CloseMessage {
|
||||
c.writeFatal(ErrCloseSent)
|
||||
_ = c.writeFatal(ErrCloseSent)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -630,7 +639,7 @@ func (w *messageWriter) flushFrame(final bool, extra []byte) error {
|
||||
}
|
||||
|
||||
if final {
|
||||
w.endMessage(errWriteClosed)
|
||||
_ = w.endMessage(errWriteClosed)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -795,7 +804,7 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
// 1. Skip remainder of previous frame.
|
||||
|
||||
if c.readRemaining > 0 {
|
||||
if _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil {
|
||||
if _, err := io.CopyN(io.Discard, c.br, c.readRemaining); err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
}
|
||||
@@ -817,7 +826,7 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
rsv2 := p[0]&rsv2Bit != 0
|
||||
rsv3 := p[0]&rsv3Bit != 0
|
||||
mask := p[1]&maskBit != 0
|
||||
c.setReadRemaining(int64(p[1] & 0x7f))
|
||||
_ = c.setReadRemaining(int64(p[1] & 0x7f)) // will not fail because argument is >= 0
|
||||
|
||||
c.readDecompress = false
|
||||
if rsv1 {
|
||||
@@ -922,7 +931,8 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
}
|
||||
|
||||
if c.readLimit > 0 && c.readLength > c.readLimit {
|
||||
c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait))
|
||||
// Make a best effort to send a close message describing the problem.
|
||||
_ = c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait))
|
||||
return noFrame, ErrReadLimit
|
||||
}
|
||||
|
||||
@@ -934,7 +944,7 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
var payload []byte
|
||||
if c.readRemaining > 0 {
|
||||
payload, err = c.read(int(c.readRemaining))
|
||||
c.setReadRemaining(0)
|
||||
_ = c.setReadRemaining(0) // will not fail because argument is >= 0
|
||||
if err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
@@ -981,7 +991,8 @@ func (c *Conn) handleProtocolError(message string) error {
|
||||
if len(data) > maxControlFramePayloadSize {
|
||||
data = data[:maxControlFramePayloadSize]
|
||||
}
|
||||
c.WriteControl(CloseMessage, data, time.Now().Add(writeWait))
|
||||
// Make a best effor to send a close message describing the problem.
|
||||
_ = c.WriteControl(CloseMessage, data, time.Now().Add(writeWait))
|
||||
return errors.New("websocket: " + message)
|
||||
}
|
||||
|
||||
@@ -1008,7 +1019,7 @@ func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
||||
for c.readErr == nil {
|
||||
frameType, err := c.advanceFrame()
|
||||
if err != nil {
|
||||
c.readErr = hideTempErr(err)
|
||||
c.readErr = err
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1048,13 +1059,13 @@ func (r *messageReader) Read(b []byte) (int, error) {
|
||||
b = b[:c.readRemaining]
|
||||
}
|
||||
n, err := c.br.Read(b)
|
||||
c.readErr = hideTempErr(err)
|
||||
c.readErr = err
|
||||
if c.isServer {
|
||||
c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n])
|
||||
}
|
||||
rem := c.readRemaining
|
||||
rem -= int64(n)
|
||||
c.setReadRemaining(rem)
|
||||
_ = c.setReadRemaining(rem) // rem is guaranteed to be >= 0
|
||||
if c.readRemaining > 0 && c.readErr == io.EOF {
|
||||
c.readErr = errUnexpectedEOF
|
||||
}
|
||||
@@ -1069,7 +1080,7 @@ func (r *messageReader) Read(b []byte) (int, error) {
|
||||
frameType, err := c.advanceFrame()
|
||||
switch {
|
||||
case err != nil:
|
||||
c.readErr = hideTempErr(err)
|
||||
c.readErr = err
|
||||
case frameType == TextMessage || frameType == BinaryMessage:
|
||||
c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader")
|
||||
}
|
||||
@@ -1094,7 +1105,7 @@ func (c *Conn) ReadMessage() (messageType int, p []byte, err error) {
|
||||
if err != nil {
|
||||
return messageType, nil, err
|
||||
}
|
||||
p, err = ioutil.ReadAll(r)
|
||||
p, err = io.ReadAll(r)
|
||||
return messageType, p, err
|
||||
}
|
||||
|
||||
@@ -1136,7 +1147,8 @@ func (c *Conn) SetCloseHandler(h func(code int, text string) error) {
|
||||
if h == nil {
|
||||
h = func(code int, text string) error {
|
||||
message := FormatCloseMessage(code, "")
|
||||
c.WriteControl(CloseMessage, message, time.Now().Add(writeWait))
|
||||
// Make a best effor to send the close message.
|
||||
_ = c.WriteControl(CloseMessage, message, time.Now().Add(writeWait))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -1158,13 +1170,9 @@ func (c *Conn) PingHandler() func(appData string) error {
|
||||
func (c *Conn) SetPingHandler(h func(appData string) error) {
|
||||
if h == nil {
|
||||
h = func(message string) error {
|
||||
err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait))
|
||||
if err == ErrCloseSent {
|
||||
return nil
|
||||
} else if e, ok := err.(net.Error); ok && e.Temporary() {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
// Make a best effort to send the pong message.
|
||||
_ = c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
c.handlePing = h
|
||||
@@ -1189,8 +1197,16 @@ func (c *Conn) SetPongHandler(h func(appData string) error) {
|
||||
c.handlePong = h
|
||||
}
|
||||
|
||||
// NetConn returns the underlying connection that is wrapped by c.
|
||||
// Note that writing to or reading from this connection directly will corrupt the
|
||||
// WebSocket connection.
|
||||
func (c *Conn) NetConn() net.Conn {
|
||||
return c.conn
|
||||
}
|
||||
|
||||
// UnderlyingConn returns the internal net.Conn. This can be used to further
|
||||
// modifications to connection specific flags.
|
||||
// Deprecated: Use the NetConn method.
|
||||
func (c *Conn) UnderlyingConn() net.Conn {
|
||||
return c.conn
|
||||
}
|
||||
|
||||
+40
-13
@@ -6,34 +6,52 @@ package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
type netDialerFunc func(network, addr string) (net.Conn, error)
|
||||
type netDialerFunc func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
|
||||
func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) {
|
||||
return fn(network, addr)
|
||||
return fn(context.Background(), network, addr)
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) {
|
||||
return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil
|
||||
})
|
||||
func (fn netDialerFunc) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return fn(ctx, network, addr)
|
||||
}
|
||||
|
||||
func proxyFromURL(proxyURL *url.URL, forwardDial netDialerFunc) (netDialerFunc, error) {
|
||||
if proxyURL.Scheme == "http" || proxyURL.Scheme == "https" {
|
||||
return (&httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDial}).DialContext, nil
|
||||
}
|
||||
dialer, err := proxy.FromURL(proxyURL, forwardDial)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if d, ok := dialer.(proxy.ContextDialer); ok {
|
||||
return d.DialContext, nil
|
||||
}
|
||||
return func(ctx context.Context, net, addr string) (net.Conn, error) {
|
||||
return dialer.Dial(net, addr)
|
||||
}, nil
|
||||
}
|
||||
|
||||
type httpProxyDialer struct {
|
||||
proxyURL *url.URL
|
||||
forwardDial func(network, addr string) (net.Conn, error)
|
||||
forwardDial netDialerFunc
|
||||
}
|
||||
|
||||
func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) {
|
||||
func (hpd *httpProxyDialer) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) {
|
||||
hostPort, _ := hostPortNoPort(hpd.proxyURL)
|
||||
conn, err := hpd.forwardDial(network, hostPort)
|
||||
conn, err := hpd.forwardDial(ctx, network, hostPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -46,7 +64,6 @@ func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error)
|
||||
connectHeader.Set("Proxy-Authorization", "Basic "+credential)
|
||||
}
|
||||
}
|
||||
|
||||
connectReq := &http.Request{
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{Opaque: addr},
|
||||
@@ -59,7 +76,7 @@ func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read response. It's OK to use and discard buffered reader here becaue
|
||||
// Read response. It's OK to use and discard buffered reader here because
|
||||
// the remote server does not speak until spoken to.
|
||||
br := bufio.NewReader(conn)
|
||||
resp, err := http.ReadResponse(br, connectReq)
|
||||
@@ -68,8 +85,18 @@ func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
conn.Close()
|
||||
// Close the response body to silence false positives from linters. Reset
|
||||
// the buffered reader first to ensure that Close() does not read from
|
||||
// conn.
|
||||
// Note: Applications must call resp.Body.Close() on a response returned
|
||||
// http.ReadResponse to inspect trailers or read another response from the
|
||||
// buffered reader. The call to resp.Body.Close() does not release
|
||||
// resources.
|
||||
br.Reset(bytes.NewReader(nil))
|
||||
_ = resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
_ = conn.Close()
|
||||
f := strings.SplitN(resp.Status, " ", 2)
|
||||
return nil, errors.New(f[1])
|
||||
}
|
||||
|
||||
+66
-58
@@ -6,8 +6,7 @@ package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -101,8 +100,8 @@ func checkSameOrigin(r *http.Request) bool {
|
||||
func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {
|
||||
if u.Subprotocols != nil {
|
||||
clientProtocols := Subprotocols(r)
|
||||
for _, serverProtocol := range u.Subprotocols {
|
||||
for _, clientProtocol := range clientProtocols {
|
||||
for _, clientProtocol := range clientProtocols {
|
||||
for _, serverProtocol := range u.Subprotocols {
|
||||
if clientProtocol == serverProtocol {
|
||||
return clientProtocol
|
||||
}
|
||||
@@ -130,7 +129,8 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
||||
}
|
||||
|
||||
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
|
||||
return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'websocket' token not found in 'Upgrade' header")
|
||||
w.Header().Set("Upgrade", "websocket")
|
||||
return u.returnError(w, r, http.StatusUpgradeRequired, badHandshake+"'websocket' token not found in 'Upgrade' header")
|
||||
}
|
||||
|
||||
if r.Method != http.MethodGet {
|
||||
@@ -154,8 +154,8 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
||||
}
|
||||
|
||||
challengeKey := r.Header.Get("Sec-Websocket-Key")
|
||||
if challengeKey == "" {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header is missing or blank")
|
||||
if !isValidChallengeKey(challengeKey) {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header must be Base64 encoded value of 16-byte in length")
|
||||
}
|
||||
|
||||
subprotocol := u.selectSubprotocol(r, responseHeader)
|
||||
@@ -172,28 +172,37 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
||||
}
|
||||
}
|
||||
|
||||
h, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
|
||||
}
|
||||
var brw *bufio.ReadWriter
|
||||
netConn, brw, err := h.Hijack()
|
||||
netConn, brw, err := http.NewResponseController(w).Hijack()
|
||||
if err != nil {
|
||||
return u.returnError(w, r, http.StatusInternalServerError, err.Error())
|
||||
return u.returnError(w, r, http.StatusInternalServerError,
|
||||
"websocket: hijack: "+err.Error())
|
||||
}
|
||||
|
||||
if brw.Reader.Buffered() > 0 {
|
||||
netConn.Close()
|
||||
return nil, errors.New("websocket: client sent data before handshake is complete")
|
||||
}
|
||||
// Close the network connection when returning an error. The variable
|
||||
// netConn is set to nil before the success return at the end of the
|
||||
// function.
|
||||
defer func() {
|
||||
if netConn != nil {
|
||||
// It's safe to ignore the error from Close() because this code is
|
||||
// only executed when returning a more important error to the
|
||||
// application.
|
||||
_ = netConn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
var br *bufio.Reader
|
||||
if u.ReadBufferSize == 0 && bufioReaderSize(netConn, brw.Reader) > 256 {
|
||||
// Reuse hijacked buffered reader as connection reader.
|
||||
if u.ReadBufferSize == 0 && brw.Reader.Size() > 256 {
|
||||
// Use hijacked buffered reader as the connection reader.
|
||||
br = brw.Reader
|
||||
} else if brw.Reader.Buffered() > 0 {
|
||||
// Wrap the network connection to read buffered data in brw.Reader
|
||||
// before reading from the network connection. This should be rare
|
||||
// because a client must not send message data before receiving the
|
||||
// handshake response.
|
||||
netConn = &brNetConn{br: brw.Reader, Conn: netConn}
|
||||
}
|
||||
|
||||
buf := bufioWriterBuffer(netConn, brw.Writer)
|
||||
buf := brw.Writer.AvailableBuffer()
|
||||
|
||||
var writeBuf []byte
|
||||
if u.WriteBufferPool == nil && u.WriteBufferSize == 0 && len(buf) >= maxFrameHeaderSize+256 {
|
||||
@@ -247,20 +256,30 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
||||
}
|
||||
p = append(p, "\r\n"...)
|
||||
|
||||
// Clear deadlines set by HTTP server.
|
||||
netConn.SetDeadline(time.Time{})
|
||||
|
||||
if u.HandshakeTimeout > 0 {
|
||||
netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout))
|
||||
if err := netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Clear deadlines set by HTTP server.
|
||||
if err := netConn.SetDeadline(time.Time{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err = netConn.Write(p); err != nil {
|
||||
netConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
if u.HandshakeTimeout > 0 {
|
||||
netConn.SetWriteDeadline(time.Time{})
|
||||
if err := netConn.SetWriteDeadline(time.Time{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Success! Set netConn to nil to stop the deferred function above from
|
||||
// closing the network connection.
|
||||
netConn = nil
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
@@ -327,39 +346,28 @@ func IsWebSocketUpgrade(r *http.Request) bool {
|
||||
tokenListContainsValue(r.Header, "Upgrade", "websocket")
|
||||
}
|
||||
|
||||
// bufioReaderSize size returns the size of a bufio.Reader.
|
||||
func bufioReaderSize(originalReader io.Reader, br *bufio.Reader) int {
|
||||
// This code assumes that peek on a reset reader returns
|
||||
// bufio.Reader.buf[:0].
|
||||
// TODO: Use bufio.Reader.Size() after Go 1.10
|
||||
br.Reset(originalReader)
|
||||
if p, err := br.Peek(0); err == nil {
|
||||
return cap(p)
|
||||
type brNetConn struct {
|
||||
br *bufio.Reader
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (b *brNetConn) Read(p []byte) (n int, err error) {
|
||||
if b.br != nil {
|
||||
// Limit read to buferred data.
|
||||
if n := b.br.Buffered(); len(p) > n {
|
||||
p = p[:n]
|
||||
}
|
||||
n, err = b.br.Read(p)
|
||||
if b.br.Buffered() == 0 {
|
||||
b.br = nil
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
return 0
|
||||
return b.Conn.Read(p)
|
||||
}
|
||||
|
||||
// writeHook is an io.Writer that records the last slice passed to it vio
|
||||
// io.Writer.Write.
|
||||
type writeHook struct {
|
||||
p []byte
|
||||
// NetConn returns the underlying connection that is wrapped by b.
|
||||
func (b *brNetConn) NetConn() net.Conn {
|
||||
return b.Conn
|
||||
}
|
||||
|
||||
func (wh *writeHook) Write(p []byte) (int, error) {
|
||||
wh.p = p
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// bufioWriterBuffer grabs the buffer from a bufio.Writer.
|
||||
func bufioWriterBuffer(originalWriter io.Writer, bw *bufio.Writer) []byte {
|
||||
// This code assumes that bufio.Writer.buf[:1] is passed to the
|
||||
// bufio.Writer's underlying writer.
|
||||
var wh writeHook
|
||||
bw.Reset(&wh)
|
||||
bw.WriteByte(0)
|
||||
bw.Flush()
|
||||
|
||||
bw.Reset(originalWriter)
|
||||
|
||||
return wh.p[:cap(wh.p)]
|
||||
}
|
||||
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
//go:build go1.17
|
||||
// +build go1.17
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
func doHandshake(ctx context.Context, tlsConn *tls.Conn, cfg *tls.Config) error {
|
||||
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if !cfg.InsecureSkipVerify {
|
||||
if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
//go:build !go1.17
|
||||
// +build !go1.17
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
func doHandshake(ctx context.Context, tlsConn *tls.Conn, cfg *tls.Config) error {
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
if !cfg.InsecureSkipVerify {
|
||||
if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+15
@@ -281,3 +281,18 @@ headers:
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// isValidChallengeKey checks if the argument meets RFC6455 specification.
|
||||
func isValidChallengeKey(s string) bool {
|
||||
// From RFC6455:
|
||||
//
|
||||
// A |Sec-WebSocket-Key| header field with a base64-encoded (see
|
||||
// Section 4 of [RFC4648]) value that, when decoded, is 16 bytes in
|
||||
// length.
|
||||
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
decoded, err := base64.StdEncoding.DecodeString(s)
|
||||
return err == nil && len(decoded) == 16
|
||||
}
|
||||
|
||||
-473
@@ -1,473 +0,0 @@
|
||||
// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
|
||||
//go:generate bundle -o x_net_proxy.go golang.org/x/net/proxy
|
||||
|
||||
// Package proxy provides support for a variety of protocols to proxy network
|
||||
// data.
|
||||
//
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type proxy_direct struct{}
|
||||
|
||||
// Direct is a direct proxy: one that makes network connections directly.
|
||||
var proxy_Direct = proxy_direct{}
|
||||
|
||||
func (proxy_direct) Dial(network, addr string) (net.Conn, error) {
|
||||
return net.Dial(network, addr)
|
||||
}
|
||||
|
||||
// A PerHost directs connections to a default Dialer unless the host name
|
||||
// requested matches one of a number of exceptions.
|
||||
type proxy_PerHost struct {
|
||||
def, bypass proxy_Dialer
|
||||
|
||||
bypassNetworks []*net.IPNet
|
||||
bypassIPs []net.IP
|
||||
bypassZones []string
|
||||
bypassHosts []string
|
||||
}
|
||||
|
||||
// NewPerHost returns a PerHost Dialer that directs connections to either
|
||||
// defaultDialer or bypass, depending on whether the connection matches one of
|
||||
// the configured rules.
|
||||
func proxy_NewPerHost(defaultDialer, bypass proxy_Dialer) *proxy_PerHost {
|
||||
return &proxy_PerHost{
|
||||
def: defaultDialer,
|
||||
bypass: bypass,
|
||||
}
|
||||
}
|
||||
|
||||
// Dial connects to the address addr on the given network through either
|
||||
// defaultDialer or bypass.
|
||||
func (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) {
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.dialerForRequest(host).Dial(network, addr)
|
||||
}
|
||||
|
||||
func (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer {
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
for _, net := range p.bypassNetworks {
|
||||
if net.Contains(ip) {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
for _, bypassIP := range p.bypassIPs {
|
||||
if bypassIP.Equal(ip) {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
return p.def
|
||||
}
|
||||
|
||||
for _, zone := range p.bypassZones {
|
||||
if strings.HasSuffix(host, zone) {
|
||||
return p.bypass
|
||||
}
|
||||
if host == zone[1:] {
|
||||
// For a zone ".example.com", we match "example.com"
|
||||
// too.
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
for _, bypassHost := range p.bypassHosts {
|
||||
if bypassHost == host {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
return p.def
|
||||
}
|
||||
|
||||
// AddFromString parses a string that contains comma-separated values
|
||||
// specifying hosts that should use the bypass proxy. Each value is either an
|
||||
// IP address, a CIDR range, a zone (*.example.com) or a host name
|
||||
// (localhost). A best effort is made to parse the string and errors are
|
||||
// ignored.
|
||||
func (p *proxy_PerHost) AddFromString(s string) {
|
||||
hosts := strings.Split(s, ",")
|
||||
for _, host := range hosts {
|
||||
host = strings.TrimSpace(host)
|
||||
if len(host) == 0 {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(host, "/") {
|
||||
// We assume that it's a CIDR address like 127.0.0.0/8
|
||||
if _, net, err := net.ParseCIDR(host); err == nil {
|
||||
p.AddNetwork(net)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
p.AddIP(ip)
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(host, "*.") {
|
||||
p.AddZone(host[1:])
|
||||
continue
|
||||
}
|
||||
p.AddHost(host)
|
||||
}
|
||||
}
|
||||
|
||||
// AddIP specifies an IP address that will use the bypass proxy. Note that
|
||||
// this will only take effect if a literal IP address is dialed. A connection
|
||||
// to a named host will never match an IP.
|
||||
func (p *proxy_PerHost) AddIP(ip net.IP) {
|
||||
p.bypassIPs = append(p.bypassIPs, ip)
|
||||
}
|
||||
|
||||
// AddNetwork specifies an IP range that will use the bypass proxy. Note that
|
||||
// this will only take effect if a literal IP address is dialed. A connection
|
||||
// to a named host will never match.
|
||||
func (p *proxy_PerHost) AddNetwork(net *net.IPNet) {
|
||||
p.bypassNetworks = append(p.bypassNetworks, net)
|
||||
}
|
||||
|
||||
// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
|
||||
// "example.com" matches "example.com" and all of its subdomains.
|
||||
func (p *proxy_PerHost) AddZone(zone string) {
|
||||
if strings.HasSuffix(zone, ".") {
|
||||
zone = zone[:len(zone)-1]
|
||||
}
|
||||
if !strings.HasPrefix(zone, ".") {
|
||||
zone = "." + zone
|
||||
}
|
||||
p.bypassZones = append(p.bypassZones, zone)
|
||||
}
|
||||
|
||||
// AddHost specifies a host name that will use the bypass proxy.
|
||||
func (p *proxy_PerHost) AddHost(host string) {
|
||||
if strings.HasSuffix(host, ".") {
|
||||
host = host[:len(host)-1]
|
||||
}
|
||||
p.bypassHosts = append(p.bypassHosts, host)
|
||||
}
|
||||
|
||||
// A Dialer is a means to establish a connection.
|
||||
type proxy_Dialer interface {
|
||||
// Dial connects to the given address via the proxy.
|
||||
Dial(network, addr string) (c net.Conn, err error)
|
||||
}
|
||||
|
||||
// Auth contains authentication parameters that specific Dialers may require.
|
||||
type proxy_Auth struct {
|
||||
User, Password string
|
||||
}
|
||||
|
||||
// FromEnvironment returns the dialer specified by the proxy related variables in
|
||||
// the environment.
|
||||
func proxy_FromEnvironment() proxy_Dialer {
|
||||
allProxy := proxy_allProxyEnv.Get()
|
||||
if len(allProxy) == 0 {
|
||||
return proxy_Direct
|
||||
}
|
||||
|
||||
proxyURL, err := url.Parse(allProxy)
|
||||
if err != nil {
|
||||
return proxy_Direct
|
||||
}
|
||||
proxy, err := proxy_FromURL(proxyURL, proxy_Direct)
|
||||
if err != nil {
|
||||
return proxy_Direct
|
||||
}
|
||||
|
||||
noProxy := proxy_noProxyEnv.Get()
|
||||
if len(noProxy) == 0 {
|
||||
return proxy
|
||||
}
|
||||
|
||||
perHost := proxy_NewPerHost(proxy, proxy_Direct)
|
||||
perHost.AddFromString(noProxy)
|
||||
return perHost
|
||||
}
|
||||
|
||||
// proxySchemes is a map from URL schemes to a function that creates a Dialer
|
||||
// from a URL with such a scheme.
|
||||
var proxy_proxySchemes map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error)
|
||||
|
||||
// RegisterDialerType takes a URL scheme and a function to generate Dialers from
|
||||
// a URL with that scheme and a forwarding Dialer. Registered schemes are used
|
||||
// by FromURL.
|
||||
func proxy_RegisterDialerType(scheme string, f func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) {
|
||||
if proxy_proxySchemes == nil {
|
||||
proxy_proxySchemes = make(map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error))
|
||||
}
|
||||
proxy_proxySchemes[scheme] = f
|
||||
}
|
||||
|
||||
// FromURL returns a Dialer given a URL specification and an underlying
|
||||
// Dialer for it to make network requests.
|
||||
func proxy_FromURL(u *url.URL, forward proxy_Dialer) (proxy_Dialer, error) {
|
||||
var auth *proxy_Auth
|
||||
if u.User != nil {
|
||||
auth = new(proxy_Auth)
|
||||
auth.User = u.User.Username()
|
||||
if p, ok := u.User.Password(); ok {
|
||||
auth.Password = p
|
||||
}
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "socks5":
|
||||
return proxy_SOCKS5("tcp", u.Host, auth, forward)
|
||||
}
|
||||
|
||||
// If the scheme doesn't match any of the built-in schemes, see if it
|
||||
// was registered by another package.
|
||||
if proxy_proxySchemes != nil {
|
||||
if f, ok := proxy_proxySchemes[u.Scheme]; ok {
|
||||
return f(u, forward)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
|
||||
}
|
||||
|
||||
var (
|
||||
proxy_allProxyEnv = &proxy_envOnce{
|
||||
names: []string{"ALL_PROXY", "all_proxy"},
|
||||
}
|
||||
proxy_noProxyEnv = &proxy_envOnce{
|
||||
names: []string{"NO_PROXY", "no_proxy"},
|
||||
}
|
||||
)
|
||||
|
||||
// envOnce looks up an environment variable (optionally by multiple
|
||||
// names) once. It mitigates expensive lookups on some platforms
|
||||
// (e.g. Windows).
|
||||
// (Borrowed from net/http/transport.go)
|
||||
type proxy_envOnce struct {
|
||||
names []string
|
||||
once sync.Once
|
||||
val string
|
||||
}
|
||||
|
||||
func (e *proxy_envOnce) Get() string {
|
||||
e.once.Do(e.init)
|
||||
return e.val
|
||||
}
|
||||
|
||||
func (e *proxy_envOnce) init() {
|
||||
for _, n := range e.names {
|
||||
e.val = os.Getenv(n)
|
||||
if e.val != "" {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
|
||||
// with an optional username and password. See RFC 1928 and RFC 1929.
|
||||
func proxy_SOCKS5(network, addr string, auth *proxy_Auth, forward proxy_Dialer) (proxy_Dialer, error) {
|
||||
s := &proxy_socks5{
|
||||
network: network,
|
||||
addr: addr,
|
||||
forward: forward,
|
||||
}
|
||||
if auth != nil {
|
||||
s.user = auth.User
|
||||
s.password = auth.Password
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
type proxy_socks5 struct {
|
||||
user, password string
|
||||
network, addr string
|
||||
forward proxy_Dialer
|
||||
}
|
||||
|
||||
const proxy_socks5Version = 5
|
||||
|
||||
const (
|
||||
proxy_socks5AuthNone = 0
|
||||
proxy_socks5AuthPassword = 2
|
||||
)
|
||||
|
||||
const proxy_socks5Connect = 1
|
||||
|
||||
const (
|
||||
proxy_socks5IP4 = 1
|
||||
proxy_socks5Domain = 3
|
||||
proxy_socks5IP6 = 4
|
||||
)
|
||||
|
||||
var proxy_socks5Errors = []string{
|
||||
"",
|
||||
"general failure",
|
||||
"connection forbidden",
|
||||
"network unreachable",
|
||||
"host unreachable",
|
||||
"connection refused",
|
||||
"TTL expired",
|
||||
"command not supported",
|
||||
"address type not supported",
|
||||
}
|
||||
|
||||
// Dial connects to the address addr on the given network via the SOCKS5 proxy.
|
||||
func (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) {
|
||||
switch network {
|
||||
case "tcp", "tcp6", "tcp4":
|
||||
default:
|
||||
return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
|
||||
}
|
||||
|
||||
conn, err := s.forward.Dial(s.network, s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.connect(conn, addr); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// connect takes an existing connection to a socks5 proxy server,
|
||||
// and commands the server to extend that connection to target,
|
||||
// which must be a canonical address with a host and port.
|
||||
func (s *proxy_socks5) connect(conn net.Conn, target string) error {
|
||||
host, portStr, err := net.SplitHostPort(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to parse port number: " + portStr)
|
||||
}
|
||||
if port < 1 || port > 0xffff {
|
||||
return errors.New("proxy: port number out of range: " + portStr)
|
||||
}
|
||||
|
||||
// the size here is just an estimate
|
||||
buf := make([]byte, 0, 6+len(host))
|
||||
|
||||
buf = append(buf, proxy_socks5Version)
|
||||
if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
|
||||
buf = append(buf, 2 /* num auth methods */, proxy_socks5AuthNone, proxy_socks5AuthPassword)
|
||||
} else {
|
||||
buf = append(buf, 1 /* num auth methods */, proxy_socks5AuthNone)
|
||||
}
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
if buf[0] != 5 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
if buf[1] == 0xff {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
|
||||
}
|
||||
|
||||
// See RFC 1929
|
||||
if buf[1] == proxy_socks5AuthPassword {
|
||||
buf = buf[:0]
|
||||
buf = append(buf, 1 /* password protocol version */)
|
||||
buf = append(buf, uint8(len(s.user)))
|
||||
buf = append(buf, s.user...)
|
||||
buf = append(buf, uint8(len(s.password)))
|
||||
buf = append(buf, s.password...)
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if buf[1] != 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
|
||||
}
|
||||
}
|
||||
|
||||
buf = buf[:0]
|
||||
buf = append(buf, proxy_socks5Version, proxy_socks5Connect, 0 /* reserved */)
|
||||
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
buf = append(buf, proxy_socks5IP4)
|
||||
ip = ip4
|
||||
} else {
|
||||
buf = append(buf, proxy_socks5IP6)
|
||||
}
|
||||
buf = append(buf, ip...)
|
||||
} else {
|
||||
if len(host) > 255 {
|
||||
return errors.New("proxy: destination host name too long: " + host)
|
||||
}
|
||||
buf = append(buf, proxy_socks5Domain)
|
||||
buf = append(buf, byte(len(host)))
|
||||
buf = append(buf, host...)
|
||||
}
|
||||
buf = append(buf, byte(port>>8), byte(port))
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:4]); err != nil {
|
||||
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
failure := "unknown error"
|
||||
if int(buf[1]) < len(proxy_socks5Errors) {
|
||||
failure = proxy_socks5Errors[buf[1]]
|
||||
}
|
||||
|
||||
if len(failure) > 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
|
||||
}
|
||||
|
||||
bytesToDiscard := 0
|
||||
switch buf[3] {
|
||||
case proxy_socks5IP4:
|
||||
bytesToDiscard = net.IPv4len
|
||||
case proxy_socks5IP6:
|
||||
bytesToDiscard = net.IPv6len
|
||||
case proxy_socks5Domain:
|
||||
_, err := io.ReadFull(conn, buf[:1])
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
bytesToDiscard = int(buf[0])
|
||||
default:
|
||||
return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
|
||||
}
|
||||
|
||||
if cap(buf) < bytesToDiscard {
|
||||
buf = make([]byte, bytesToDiscard)
|
||||
} else {
|
||||
buf = buf[:bytesToDiscard]
|
||||
}
|
||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
||||
return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
// Also need to discard the port number
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+1
-1
@@ -143,7 +143,7 @@ func (b *bitWriter) flush32() {
|
||||
// flushAlign will flush remaining full bytes and align to next byte boundary.
|
||||
func (b *bitWriter) flushAlign() {
|
||||
nbBytes := (b.nBits + 7) >> 3
|
||||
for i := uint8(0); i < nbBytes; i++ {
|
||||
for i := range nbBytes {
|
||||
b.out = append(b.out, byte(b.bitContainer>>(i*8)))
|
||||
}
|
||||
b.nBits = 0
|
||||
|
||||
+1
-1
@@ -396,7 +396,7 @@ func (s *Scratch) buildCTable() error {
|
||||
if v > largeLimit {
|
||||
s.zeroBits = true
|
||||
}
|
||||
for nbOccurrences := int16(0); nbOccurrences < v; nbOccurrences++ {
|
||||
for range v {
|
||||
tableSymbol[position] = symbol
|
||||
position = (position + step) & tableMask
|
||||
for position > highThreshold {
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ func (b *bitWriter) flush32() {
|
||||
// flushAlign will flush remaining full bytes and align to next byte boundary.
|
||||
func (b *bitWriter) flushAlign() {
|
||||
nbBytes := (b.nBits + 7) >> 3
|
||||
for i := uint8(0); i < nbBytes; i++ {
|
||||
for i := range nbBytes {
|
||||
b.out = append(b.out, byte(b.bitContainer>>(i*8)))
|
||||
}
|
||||
b.nBits = 0
|
||||
|
||||
+3
-3
@@ -276,7 +276,7 @@ func (s *Scratch) compress4X(src []byte) ([]byte, error) {
|
||||
offsetIdx := len(s.Out)
|
||||
s.Out = append(s.Out, sixZeros[:]...)
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
for i := range 4 {
|
||||
toDo := src
|
||||
if len(toDo) > segmentSize {
|
||||
toDo = toDo[:segmentSize]
|
||||
@@ -312,7 +312,7 @@ func (s *Scratch) compress4Xp(src []byte) ([]byte, error) {
|
||||
segmentSize := (len(src) + 3) / 4
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(4)
|
||||
for i := 0; i < 4; i++ {
|
||||
for i := range 4 {
|
||||
toDo := src
|
||||
if len(toDo) > segmentSize {
|
||||
toDo = toDo[:segmentSize]
|
||||
@@ -326,7 +326,7 @@ func (s *Scratch) compress4Xp(src []byte) ([]byte, error) {
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
for i := 0; i < 4; i++ {
|
||||
for i := range 4 {
|
||||
o := s.tmpOut[i]
|
||||
if len(o) > math.MaxUint16 {
|
||||
// We cannot store the size in the jump table
|
||||
|
||||
+4
-10
@@ -626,7 +626,7 @@ func (d *Decoder) decompress4X8bit(dst, src []byte) ([]byte, error) {
|
||||
|
||||
var br [4]bitReaderBytes
|
||||
start := 6
|
||||
for i := 0; i < 3; i++ {
|
||||
for i := range 3 {
|
||||
length := int(src[i*2]) | (int(src[i*2+1]) << 8)
|
||||
if start+length >= len(src) {
|
||||
return nil, errors.New("truncated input (or invalid offset)")
|
||||
@@ -798,10 +798,7 @@ func (d *Decoder) decompress4X8bit(dst, src []byte) ([]byte, error) {
|
||||
remainBytes := dstEvery - (decoded / 4)
|
||||
for i := range br {
|
||||
offset := dstEvery * i
|
||||
endsAt := offset + remainBytes
|
||||
if endsAt > len(out) {
|
||||
endsAt = len(out)
|
||||
}
|
||||
endsAt := min(offset+remainBytes, len(out))
|
||||
br := &br[i]
|
||||
bitsLeft := br.remaining()
|
||||
for bitsLeft > 0 {
|
||||
@@ -864,7 +861,7 @@ func (d *Decoder) decompress4X8bit(dst, src []byte) ([]byte, error) {
|
||||
func (d *Decoder) decompress4X8bitExactly(dst, src []byte) ([]byte, error) {
|
||||
var br [4]bitReaderBytes
|
||||
start := 6
|
||||
for i := 0; i < 3; i++ {
|
||||
for i := range 3 {
|
||||
length := int(src[i*2]) | (int(src[i*2+1]) << 8)
|
||||
if start+length >= len(src) {
|
||||
return nil, errors.New("truncated input (or invalid offset)")
|
||||
@@ -1035,10 +1032,7 @@ func (d *Decoder) decompress4X8bitExactly(dst, src []byte) ([]byte, error) {
|
||||
remainBytes := dstEvery - (decoded / 4)
|
||||
for i := range br {
|
||||
offset := dstEvery * i
|
||||
endsAt := offset + remainBytes
|
||||
if endsAt > len(out) {
|
||||
endsAt = len(out)
|
||||
}
|
||||
endsAt := min(offset+remainBytes, len(out))
|
||||
br := &br[i]
|
||||
bitsLeft := br.remaining()
|
||||
for bitsLeft > 0 {
|
||||
|
||||
+2
-5
@@ -58,7 +58,7 @@ func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) {
|
||||
var br [4]bitReaderShifted
|
||||
// Decode "jump table"
|
||||
start := 6
|
||||
for i := 0; i < 3; i++ {
|
||||
for i := range 3 {
|
||||
length := int(src[i*2]) | (int(src[i*2+1]) << 8)
|
||||
if start+length >= len(src) {
|
||||
return nil, errors.New("truncated input (or invalid offset)")
|
||||
@@ -109,10 +109,7 @@ func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) {
|
||||
remainBytes := dstEvery - (decoded / 4)
|
||||
for i := range br {
|
||||
offset := dstEvery * i
|
||||
endsAt := offset + remainBytes
|
||||
if endsAt > len(out) {
|
||||
endsAt = len(out)
|
||||
}
|
||||
endsAt := min(offset+remainBytes, len(out))
|
||||
br := &br[i]
|
||||
bitsLeft := br.remaining()
|
||||
for bitsLeft > 0 {
|
||||
|
||||
+2
-2
@@ -201,7 +201,7 @@ func (c cTable) write(s *Scratch) error {
|
||||
for i := range hist[:16] {
|
||||
hist[i] = 0
|
||||
}
|
||||
for n := uint8(0); n < maxSymbolValue; n++ {
|
||||
for n := range maxSymbolValue {
|
||||
v := bitsToWeight[c[n].nBits] & 15
|
||||
huffWeight[n] = v
|
||||
hist[v]++
|
||||
@@ -271,7 +271,7 @@ func (c cTable) estTableSize(s *Scratch) (sz int, err error) {
|
||||
for i := range hist[:16] {
|
||||
hist[i] = 0
|
||||
}
|
||||
for n := uint8(0); n < maxSymbolValue; n++ {
|
||||
for n := range maxSymbolValue {
|
||||
v := bitsToWeight[c[n].nBits] & 15
|
||||
huffWeight[n] = v
|
||||
hist[v]++
|
||||
|
||||
+2
-2
@@ -37,6 +37,6 @@ func Store32(b []byte, v uint32) {
|
||||
}
|
||||
|
||||
// Store64 will store v at b.
|
||||
func Store64(b []byte, v uint64) {
|
||||
binary.LittleEndian.PutUint64(b, v)
|
||||
func Store64[I Indexer](b []byte, i I, v uint64) {
|
||||
binary.LittleEndian.PutUint64(b[i:], v)
|
||||
}
|
||||
|
||||
+3
-6
@@ -38,18 +38,15 @@ func Load64[I Indexer](b []byte, i I) uint64 {
|
||||
|
||||
// Store16 will store v at b.
|
||||
func Store16(b []byte, v uint16) {
|
||||
//binary.LittleEndian.PutUint16(b, v)
|
||||
*(*uint16)(unsafe.Pointer(unsafe.SliceData(b))) = v
|
||||
}
|
||||
|
||||
// Store32 will store v at b.
|
||||
func Store32(b []byte, v uint32) {
|
||||
//binary.LittleEndian.PutUint32(b, v)
|
||||
*(*uint32)(unsafe.Pointer(unsafe.SliceData(b))) = v
|
||||
}
|
||||
|
||||
// Store64 will store v at b.
|
||||
func Store64(b []byte, v uint64) {
|
||||
//binary.LittleEndian.PutUint64(b, v)
|
||||
*(*uint64)(unsafe.Pointer(unsafe.SliceData(b))) = v
|
||||
// Store64 will store v at b[i:].
|
||||
func Store64[I Indexer](b []byte, i I, v uint64) {
|
||||
*(*uint64)(unsafe.Add(unsafe.Pointer(unsafe.SliceData(b)), i)) = v
|
||||
}
|
||||
|
||||
+1
-1
@@ -209,7 +209,7 @@ func (r *Reader) fill() error {
|
||||
if !r.readFull(r.buf[:len(magicBody)], false) {
|
||||
return r.err
|
||||
}
|
||||
for i := 0; i < len(magicBody); i++ {
|
||||
for i := range len(magicBody) {
|
||||
if r.buf[i] != magicBody[i] {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
|
||||
+3
-1
@@ -20,8 +20,10 @@ import (
|
||||
func Encode(dst, src []byte) []byte {
|
||||
if n := MaxEncodedLen(len(src)); n < 0 {
|
||||
panic(ErrTooLarge)
|
||||
} else if len(dst) < n {
|
||||
} else if cap(dst) < n {
|
||||
dst = make([]byte, n)
|
||||
} else {
|
||||
dst = dst[:n]
|
||||
}
|
||||
|
||||
// The block starts with the varint-encoded length of the decompressed bytes.
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ func (b *bitWriter) flush32() {
|
||||
// flushAlign will flush remaining full bytes and align to next byte boundary.
|
||||
func (b *bitWriter) flushAlign() {
|
||||
nbBytes := (b.nBits + 7) >> 3
|
||||
for i := uint8(0); i < nbBytes; i++ {
|
||||
for i := range nbBytes {
|
||||
b.out = append(b.out, byte(b.bitContainer>>(i*8)))
|
||||
}
|
||||
b.nBits = 0
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user