From 9d64444110303e638845b4122d23ba95e56a8242 Mon Sep 17 00:00:00 2001 From: ucwong Date: Mon, 4 May 2026 11:11:20 +0000 Subject: [PATCH 1/5] download found state --- ctxc/downloader/queue.go | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/ctxc/downloader/queue.go b/ctxc/downloader/queue.go index 117e8de7c9..4f9401d588 100644 --- a/ctxc/downloader/queue.go +++ b/ctxc/downloader/queue.go @@ -21,7 +21,6 @@ package downloader import ( "errors" - "fmt" "sync" "sync/atomic" "time" @@ -849,10 +848,10 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, } // Assemble each of the results with their headers and retrieved data parts var ( - accepted int - failure error - i int - hashes []common.Hash + accepted int + failure error + i int + foundStale bool ) for _, header := range request.Headers { // Short circuit assembly if no more fetch results are found @@ -864,40 +863,39 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, failure = err break } - hashes = append(hashes, header.Hash()) i++ } for _, header := range request.Headers[:i] { if res, stale, err := q.resultCache.GetDeliverySlot(header.Number.Uint64()); err == nil && !stale { reconstruct(accepted, res) + accepted++ } else { // else: betweeen here and above, some other peer filled this result, // or it was indeed a no-op. This should not happen, but if it does it's // not something to panic about log.Error("Delivery stale", "stale", stale, "number", header.Number.Uint64(), "err", err) - failure = errStaleDelivery + foundStale = true } // Clean up a successful fetch - delete(taskPool, hashes[accepted]) - accepted++ + delete(taskPool, header.Hash()) } // Return all failed or missing fetches to the queue - for _, header := range request.Headers[accepted:] { + for _, header := range request.Headers[i:] { taskQueue.Push(header, -int64(header.Number.Uint64())) } // Wake up WaitResults if accepted > 0 { q.active.Signal() } - if failure == nil { - return accepted, nil + if failure != nil { + return accepted, failure } // If none of the data was good, it's a stale delivery - if accepted > 0 { - return accepted, fmt.Errorf("partial failure: %v", failure) + if foundStale { + return accepted, errStaleDelivery } - return accepted, fmt.Errorf("%w: %v", failure, errStaleDelivery) + return accepted, nil } // Prepare configures the result cache to allow accepting and caching inbound From 1e5ead08fafe6799de10e987d890f6889774e010 Mon Sep 17 00:00:00 2001 From: ucwong Date: Mon, 4 May 2026 14:07:25 +0000 Subject: [PATCH 2/5] deps --- go.mod | 65 +- go.sum | 140 +- .../azure-sdk-for-go/sdk/azcore/CHANGELOG.md | 13 + .../internal/exported/response_error.go | 2 +- .../sdk/azcore/internal/shared/constants.go | 2 +- .../sdk/azcore/runtime/policy_http_trace.go | 11 +- .../sdk/azcore/runtime/policy_logging.go | 23 +- .../sdk/azcore/runtime/request.go | 8 +- .../aws-sdk-go-v2/aws/go_module_metadata.go | 2 +- .../aws/aws-sdk-go-v2/config/CHANGELOG.md | 14 + .../config/go_module_metadata.go | 2 +- .../{ => config}/internal/ini/errors.go | 0 .../{ => config}/internal/ini/ini.go | 0 .../{ => config}/internal/ini/parse.go | 0 .../{ => config}/internal/ini/sections.go | 0 .../{ => config}/internal/ini/strings.go | 0 .../{ => config}/internal/ini/token.go | 0 .../{ => config}/internal/ini/tokenize.go | 0 .../{ => config}/internal/ini/value.go | 0 .../aws/aws-sdk-go-v2/config/shared_config.go | 2 +- .../aws-sdk-go-v2/credentials/CHANGELOG.md | 10 + .../credentials/go_module_metadata.go | 2 +- .../feature/ec2/imds/CHANGELOG.md | 10 + .../feature/ec2/imds/go_module_metadata.go | 2 +- .../internal/configsources/CHANGELOG.md | 10 + .../configsources/go_module_metadata.go | 2 +- .../internal/endpoints/v2/CHANGELOG.md | 10 + .../endpoints/v2/go_module_metadata.go | 2 +- .../aws-sdk-go-v2/internal/ini/CHANGELOG.md | 296 - .../aws-sdk-go-v2/internal/v4a/CHANGELOG.md | 460 + .../internal/{ini => v4a}/LICENSE.txt | 0 .../aws-sdk-go-v2/internal/v4a/credentials.go | 141 + .../aws/aws-sdk-go-v2/internal/v4a/error.go | 17 + .../{ini => v4a}/go_module_metadata.go | 4 +- .../internal/v4a/internal/crypto/compare.go | 30 + .../internal/v4a/internal/crypto/ecc.go | 113 + .../internal/v4a/internal/v4/const.go | 36 + .../internal/v4a/internal/v4/header_rules.go | 82 + .../internal/v4a/internal/v4/headers.go | 68 + .../internal/v4a/internal/v4/hmac.go | 13 + .../internal/v4a/internal/v4/host.go | 75 + .../internal/v4a/internal/v4/time.go | 36 + .../internal/v4a/internal/v4/util.go | 64 + .../aws-sdk-go-v2/internal/v4a/middleware.go | 118 + .../internal/v4a/presign_middleware.go | 117 + .../aws/aws-sdk-go-v2/internal/v4a/smithy.go | 92 + .../aws/aws-sdk-go-v2/internal/v4a/v4a.go | 520 + .../internal/accept-encoding/CHANGELOG.md | 8 + .../accept-encoding/go_module_metadata.go | 2 +- .../internal/presigned-url/CHANGELOG.md | 10 + .../presigned-url/go_module_metadata.go | 2 +- .../service/route53/CHANGELOG.md | 10 + .../service/route53/go_module_metadata.go | 2 +- .../aws-sdk-go-v2/service/signin/CHANGELOG.md | 10 + .../service/signin/go_module_metadata.go | 2 +- .../aws-sdk-go-v2/service/sso/CHANGELOG.md | 10 + .../service/sso/go_module_metadata.go | 2 +- .../service/ssooidc/CHANGELOG.md | 10 + .../service/ssooidc/go_module_metadata.go | 2 +- .../aws-sdk-go-v2/service/sts/CHANGELOG.md | 11 + .../aws-sdk-go-v2/service/sts/api_client.go | 28 + .../aws/aws-sdk-go-v2/service/sts/auth.go | 10 + .../aws-sdk-go-v2/service/sts/generated.json | 1 + .../service/sts/go_module_metadata.go | 2 +- .../aws/aws-sdk-go-v2/service/sts/options.go | 48 + vendor/github.com/aws/smithy-go/CHANGELOG.md | 9 + .../aws/smithy-go/go_module_metadata.go | 2 +- .../cockroachdb/errors/errbase/encode.go | 28 +- .../cockroachdb/errors/markers/markers.go | 114 +- .../cockroachdb/errors/report/report.go | 5 +- .../deckarep/golang-set/v2/README.md | 7 + .../github.com/deckarep/golang-set/v2/set.go | 17 +- .../deckarep/golang-set/v2/threadsafe.go | 35 +- .../deckarep/golang-set/v2/threadunsafe.go | 67 +- vendor/github.com/dlclark/regexp2/README.md | 2 +- vendor/github.com/dlclark/regexp2/runner.go | 27 +- .../github.com/fsnotify/fsnotify/.cirrus.yml | 14 - .../github.com/fsnotify/fsnotify/CHANGELOG.md | 49 + .../fsnotify/fsnotify/CONTRIBUTING.md | 2 + vendor/github.com/fsnotify/fsnotify/README.md | 56 +- .../fsnotify/fsnotify/backend_fen.go | 4 +- .../fsnotify/fsnotify/backend_inotify.go | 84 +- .../fsnotify/fsnotify/backend_kqueue.go | 68 +- .../fsnotify/fsnotify/backend_windows.go | 78 +- .../github.com/fsnotify/fsnotify/fsnotify.go | 72 +- .../fsnotify/fsnotify/internal/darwin.go | 19 - .../fsnotify/internal/debug_darwin.go | 42 - .../fsnotify/internal/debug_dragonfly.go | 18 - .../fsnotify/internal/debug_freebsd.go | 34 +- .../fsnotify/internal/debug_kqueue.go | 2 +- .../fsnotify/internal/debug_netbsd.go | 10 - .../fsnotify/internal/debug_openbsd.go | 12 - .../fsnotify/fsnotify/internal/freebsd.go | 11 - .../fsnotify/fsnotify/internal/unix.go | 11 - .../fsnotify/fsnotify/internal/unix2.go | 18 + .../fsnotify/fsnotify/internal/windows.go | 1 - .../getsentry/sentry-go/.codecov.yml | 7 - .../github.com/getsentry/sentry-go/.craft.yml | 3 + .../getsentry/sentry-go/.golangci.yml | 6 +- .../github.com/getsentry/sentry-go/AGENTS.md | 11 +- .../getsentry/sentry-go/CHANGELOG.md | 47 + .../github.com/getsentry/sentry-go/Makefile | 136 +- .../github.com/getsentry/sentry-go/README.md | 5 +- .../github.com/getsentry/sentry-go/baggage.go | 52 + .../getsentry/sentry-go/batch_processor.go | 2 +- .../github.com/getsentry/sentry-go/client.go | 80 +- vendor/github.com/getsentry/sentry-go/go.work | 19 + .../getsentry/sentry-go/go.work.sum | 189 + vendor/github.com/getsentry/sentry-go/hub.go | 25 +- .../getsentry/sentry-go/interfaces.go | 138 +- .../sentry-go/internal/http/transport.go | 40 +- .../sentry-go/internal/protocol/envelope.go | 2 +- .../sentry-go/internal/protocol/interfaces.go | 8 + .../sentry-go/internal/telemetry/processor.go | 2 +- .../sentry-go/internal/telemetry/scheduler.go | 60 +- .../getsentry/sentry-go/internal/util/util.go | 2 +- .../github.com/getsentry/sentry-go/scope.go | 62 +- .../github.com/getsentry/sentry-go/sentry.go | 5 +- .../github.com/getsentry/sentry-go/tracing.go | 105 +- .../getsentry/sentry-go/transport.go | 74 +- .../jedib0t/go-pretty/v6/text/align.go | 7 + .../klauspost/compress/.gitattributes | 1 + .../github.com/klauspost/compress/README.md | 1400 +- .../klauspost/compress/fse/README.md | 156 +- .../klauspost/compress/huff0/README.md | 178 +- .../klauspost/compress/s2/decode_amd64.s | 2 +- .../klauspost/compress/s2/encode_best.go | 28 +- .../klauspost/compress/s2/encode_better.go | 42 +- .../klauspost/compress/s2/hashtable_pool.go | 65 + .../mattn/go-isatty/isatty_windows.go | 9 +- vendor/github.com/pion/ice/v4/agent.go | 150 +- .../github.com/pion/ice/v4/candidate_base.go | 24 +- .../github.com/pion/ice/v4/candidate_host.go | 19 +- .../pion/ice/v4/candidate_peer_reflexive.go | 1 - .../github.com/pion/ice/v4/candidate_relay.go | 3 +- .../pion/ice/v4/candidate_server_reflexive.go | 1 - vendor/github.com/pion/ice/v4/gather.go | 2 +- .../github.com/pion/interceptor/.golangci.yml | 4 + vendor/github.com/pion/interceptor/README.md | 4 +- .../internal/rtpbuffer/rtpbuffer.go | 13 +- .../pkg/flexfec/flexfec_coverage.go | 6 +- .../pkg/flexfec/flexfec_decoder_03.go | 4 +- .../pkg/flexfec/flexfec_encoder.go | 4 +- .../pkg/flexfec/flexfec_encoder_03.go | 2 +- .../pkg/nack/responder_interceptor.go | 23 + vendor/github.com/pion/rtp/.golangci.yml | 4 + .../pion/rtp/codecs/av1/obu/leb128.go | 2 +- .../github.com/pion/rtp/codecs/vp8_packet.go | 7 +- .../github.com/pion/rtp/codecs/vp9_packet.go | 4 +- vendor/github.com/pion/rtp/packet.go | 14 + .../pion/rtp/playoutdelayextension.go | 8 +- vendor/github.com/pion/rtp/vlaextension.go | 15 +- vendor/github.com/pion/sctp/.golangci.yml | 3 + vendor/github.com/pion/sctp/association.go | 26 +- .../transport/v4/reuseport/control_default.go | 14 + .../transport/v4/reuseport/control_unix.go | 22 + .../transport/v4/reuseport/control_windows.go | 18 + .../pion/transport/v4/reuseport/reuseport.go | 5 + .../github.com/pion/turn/v4/.goreleaser.yml | 5 - .../internal/allocation/allocation_manager.go | 246 - .../turn/v4/internal/allocation/errors.go | 22 - .../pion/turn/v4/internal/server/errors.go | 29 - .../turn/v4/relay_address_generator_none.go | 59 - .../turn/v4/relay_address_generator_range.go | 116 - .../turn/v4/relay_address_generator_static.go | 73 - .../pion/turn/{v4 => v5}/.gitignore | 2 +- .../pion/turn/{v4 => v5}/.golangci.yml | 2 +- .../github.com/pion/turn/v5/.goreleaser.yml | 5 + .../github.com/pion/turn/{v4 => v5}/LICENSE | 2 +- .../github.com/pion/turn/{v4 => v5}/README.md | 15 +- vendor/github.com/pion/turn/v5/addr_family.go | 16 + .../github.com/pion/turn/{v4 => v5}/client.go | 266 +- .../pion/turn/{v4 => v5}/codecov.yml | 2 +- .../github.com/pion/turn/{v4 => v5}/errors.go | 3 +- .../internal/allocation/allocation.go | 183 +- .../internal/allocation/allocation_manager.go | 497 + .../internal/allocation/channel_bind.go | 4 +- .../turn/v5/internal/allocation/errors.go | 36 + .../internal/allocation/event_handler.go | 21 +- .../internal/allocation/five_tuple.go | 2 +- .../internal/allocation/permission.go | 12 +- .../pion/turn/v5/internal/auth/auth.go | 26 + .../{v4 => v5}/internal/client/allocation.go | 27 +- .../{v4 => v5}/internal/client/binding.go | 2 +- .../turn/{v4 => v5}/internal/client/client.go | 2 +- .../turn/{v4 => v5}/internal/client/errors.go | 3 +- .../internal/client/periodic_timer.go | 2 +- .../{v4 => v5}/internal/client/permission.go | 4 +- .../{v4 => v5}/internal/client/tcp_alloc.go | 45 +- .../{v4 => v5}/internal/client/tcp_conn.go | 4 +- .../{v4 => v5}/internal/client/transaction.go | 2 +- .../{v4 => v5}/internal/client/trylock.go | 2 +- .../{v4 => v5}/internal/client/udp_conn.go | 59 +- .../turn/{v4 => v5}/internal/ipnet/util.go | 25 +- .../turn/{v4 => v5}/internal/proto/addr.go | 5 +- .../{v4 => v5}/internal/proto/chandata.go | 2 +- .../turn/{v4 => v5}/internal/proto/chann.go | 2 +- .../internal/proto/connection_id.go | 2 +- .../turn/{v4 => v5}/internal/proto/data.go | 2 +- .../{v4 => v5}/internal/proto/dontfrag.go | 3 +- .../{v4 => v5}/internal/proto/evenport.go | 2 +- .../{v4 => v5}/internal/proto/lifetime.go | 2 +- .../{v4 => v5}/internal/proto/peeraddr.go | 2 +- .../turn/{v4 => v5}/internal/proto/proto.go | 2 +- .../{v4 => v5}/internal/proto/relayedaddr.go | 2 +- .../{v4 => v5}/internal/proto/reqfamily.go | 2 +- .../{v4 => v5}/internal/proto/reqtrans.go | 2 +- .../{v4 => v5}/internal/proto/rsrvtoken.go | 2 +- .../{v4 => v5/internal/proto}/stun_conn.go | 20 +- .../turn/{v4 => v5}/internal/server/base36.go | 2 +- .../pion/turn/v5/internal/server/errors.go | 32 + .../turn/{v4 => v5}/internal/server/nonce.go | 2 +- .../turn/{v4 => v5}/internal/server/server.go | 49 +- .../{v4 => v5}/internal/server/short_nonce.go | 2 +- .../turn/{v4 => v5}/internal/server/stun.go | 4 +- .../turn/{v4 => v5}/internal/server/turn.go | 381 +- .../turn/{v4 => v5}/internal/server/util.go | 44 +- .../pion/turn/{v4 => v5}/lt_cred.go | 53 +- .../turn/v5/relay_address_generator_none.go | 97 + .../turn/v5/relay_address_generator_range.go | 185 + .../turn/v5/relay_address_generator_static.go | 120 + .../pion/turn/{v4 => v5}/renovate.json | 0 .../github.com/pion/turn/{v4 => v5}/server.go | 57 +- .../pion/turn/{v4 => v5}/server_config.go | 34 +- vendor/github.com/pion/turn/v5/stun_conn.go | 17 + .../github.com/pion/webrtc/v4/.golangci.yml | 3 + vendor/github.com/pion/webrtc/v4/DESIGN.md | 2 +- vendor/github.com/pion/webrtc/v4/README.md | 12 +- .../pion/webrtc/v4/rtptransceiver.go | 8 +- vendor/go.mongodb.org/mongo-driver/LICENSE | 201 + .../go.mongodb.org/mongo-driver/bson/bson.go | 50 + .../bson/bsoncodec/array_codec.go | 55 + .../mongo-driver/bson/bsoncodec/bsoncodec.go | 382 + .../bson/bsoncodec/byte_slice_codec.go | 138 + .../bson/bsoncodec/codec_cache.go | 166 + .../bson/bsoncodec/cond_addr_codec.go | 63 + .../bson/bsoncodec/default_value_decoders.go | 1819 + .../bson/bsoncodec/default_value_encoders.go | 856 + .../mongo-driver/bson/bsoncodec/doc.go | 95 + .../bson/bsoncodec/empty_interface_codec.go | 173 + .../mongo-driver/bson/bsoncodec/map_codec.go | 343 + .../mongo-driver/bson/bsoncodec/mode.go | 65 + .../bson/bsoncodec/pointer_codec.go | 108 + .../mongo-driver/bson/bsoncodec/proxy.go | 14 + .../mongo-driver/bson/bsoncodec/registry.go | 524 + .../bson/bsoncodec/slice_codec.go | 214 + .../bson/bsoncodec/string_codec.go | 140 + .../bson/bsoncodec/struct_codec.go | 736 + .../bson/bsoncodec/struct_tag_parser.go | 148 + .../mongo-driver/bson/bsoncodec/time_codec.go | 151 + .../mongo-driver/bson/bsoncodec/types.go | 58 + .../mongo-driver/bson/bsoncodec/uint_codec.go | 202 + .../bsonoptions/byte_slice_codec_options.go | 49 + .../mongo-driver/bson/bsonoptions/doc.go | 8 + .../empty_interface_codec_options.go | 49 + .../bson/bsonoptions/map_codec_options.go | 82 + .../bson/bsonoptions/slice_codec_options.go | 49 + .../bson/bsonoptions/string_codec_options.go | 52 + .../bson/bsonoptions/struct_codec_options.go | 107 + .../bson/bsonoptions/time_codec_options.go | 49 + .../bson/bsonoptions/uint_codec_options.go | 49 + .../mongo-driver/bson/bsonrw/copier.go | 489 + .../mongo-driver/bson/bsonrw/doc.go | 9 + .../bson/bsonrw/extjson_parser.go | 806 + .../bson/bsonrw/extjson_reader.go | 653 + .../bson/bsonrw/extjson_tables.go | 223 + .../bson/bsonrw/extjson_wrappers.go | 492 + .../bson/bsonrw/extjson_writer.go | 751 + .../mongo-driver/bson/bsonrw/json_scanner.go | 533 + .../mongo-driver/bson/bsonrw/mode.go | 108 + .../mongo-driver/bson/bsonrw/reader.go | 65 + .../mongo-driver/bson/bsonrw/value_reader.go | 888 + .../mongo-driver/bson/bsonrw/value_writer.go | 640 + .../mongo-driver/bson/bsonrw/writer.go | 87 + .../mongo-driver/bson/bsontype/bsontype.go | 116 + .../mongo-driver/bson/decoder.go | 208 + .../go.mongodb.org/mongo-driver/bson/doc.go | 142 + .../mongo-driver/bson/encoder.go | 199 + .../mongo-driver/bson/marshal.go | 453 + .../mongo-driver/bson/primitive/decimal.go | 432 + .../mongo-driver/bson/primitive/objectid.go | 206 + .../mongo-driver/bson/primitive/primitive.go | 231 + .../mongo-driver/bson/primitive_codecs.go | 122 + .../go.mongodb.org/mongo-driver/bson/raw.go | 101 + .../mongo-driver/bson/raw_element.go | 48 + .../mongo-driver/bson/raw_value.go | 324 + .../mongo-driver/bson/registry.go | 47 + .../go.mongodb.org/mongo-driver/bson/types.go | 50 + .../mongo-driver/bson/unmarshal.go | 180 + .../mongo-driver/x/bsonx/bsoncore/array.go | 164 + .../x/bsonx/bsoncore/bson_arraybuilder.go | 201 + .../x/bsonx/bsoncore/bson_documentbuilder.go | 189 + .../mongo-driver/x/bsonx/bsoncore/bsoncore.go | 842 + .../mongo-driver/x/bsonx/bsoncore/doc.go | 34 + .../mongo-driver/x/bsonx/bsoncore/document.go | 386 + .../x/bsonx/bsoncore/document_sequence.go | 189 + .../mongo-driver/x/bsonx/bsoncore/element.go | 152 + .../mongo-driver/x/bsonx/bsoncore/tables.go | 223 + .../mongo-driver/x/bsonx/bsoncore/value.go | 964 + vendor/modernc.org/libc/libc_darwin.go | 4 + vendor/modernc.org/libc/libc_freebsd.go | 4 + vendor/modernc.org/libc/libc_windows.go | 8 +- .../sqlite/lib/libsqlite3_freebsd.go | 17 + .../sqlite/lib/libsqlite3_windows.go | 16 + .../sqlite/lib/sqlite_darwin_amd64.go | 29690 ++++++++------- .../sqlite/lib/sqlite_darwin_arm64.go | 29697 ++++++++------- .../sqlite/lib/sqlite_freebsd_amd64.go | 29895 ++++++++------- .../sqlite/lib/sqlite_freebsd_arm64.go | 29887 ++++++++------- .../sqlite/lib/sqlite_linux_386.go | 29631 ++++++++------- .../sqlite/lib/sqlite_linux_amd64.go | 29483 ++++++++------- .../sqlite/lib/sqlite_linux_arm.go | 29518 ++++++++------- .../sqlite/lib/sqlite_linux_arm64.go | 29475 ++++++++------- .../sqlite/lib/sqlite_linux_loong64.go | 29483 ++++++++------- .../sqlite/lib/sqlite_linux_ppc64le.go | 29475 ++++++++------- .../sqlite/lib/sqlite_linux_riscv64.go | 29475 ++++++++------- .../sqlite/lib/sqlite_linux_s390x.go | 29475 ++++++++------- .../sqlite/lib/sqlite_openbsd_amd64.go | 29708 ++++++++------- .../sqlite/lib/sqlite_openbsd_arm64.go | 29699 ++++++++------- .../modernc.org/sqlite/lib/sqlite_windows.go | 29931 ++++++++------- .../sqlite/lib/sqlite_windows_386.go | 29981 +++++++++------- vendor/modules.txt | 106 +- 321 files changed, 291533 insertions(+), 212316 deletions(-) rename vendor/github.com/aws/aws-sdk-go-v2/{ => config}/internal/ini/errors.go (100%) rename vendor/github.com/aws/aws-sdk-go-v2/{ => config}/internal/ini/ini.go (100%) rename vendor/github.com/aws/aws-sdk-go-v2/{ => config}/internal/ini/parse.go (100%) rename vendor/github.com/aws/aws-sdk-go-v2/{ => config}/internal/ini/sections.go (100%) rename vendor/github.com/aws/aws-sdk-go-v2/{ => config}/internal/ini/strings.go (100%) rename vendor/github.com/aws/aws-sdk-go-v2/{ => config}/internal/ini/token.go (100%) rename vendor/github.com/aws/aws-sdk-go-v2/{ => config}/internal/ini/tokenize.go (100%) rename vendor/github.com/aws/aws-sdk-go-v2/{ => config}/internal/ini/value.go (100%) delete mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/ini/CHANGELOG.md create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/CHANGELOG.md rename vendor/github.com/aws/aws-sdk-go-v2/internal/{ini => v4a}/LICENSE.txt (100%) create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/credentials.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/error.go rename vendor/github.com/aws/aws-sdk-go-v2/internal/{ini => v4a}/go_module_metadata.go (74%) create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/crypto/compare.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/crypto/ecc.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/const.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/header_rules.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/headers.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/hmac.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/host.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/time.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/util.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/middleware.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/presign_middleware.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/smithy.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/v4a.go delete mode 100644 vendor/github.com/fsnotify/fsnotify/.cirrus.yml create mode 100644 vendor/github.com/getsentry/sentry-go/baggage.go create mode 100644 vendor/github.com/getsentry/sentry-go/go.work create mode 100644 vendor/github.com/getsentry/sentry-go/go.work.sum create mode 100644 vendor/github.com/klauspost/compress/s2/hashtable_pool.go create mode 100644 vendor/github.com/pion/transport/v4/reuseport/control_default.go create mode 100644 vendor/github.com/pion/transport/v4/reuseport/control_unix.go create mode 100644 vendor/github.com/pion/transport/v4/reuseport/control_windows.go create mode 100644 vendor/github.com/pion/transport/v4/reuseport/reuseport.go delete mode 100644 vendor/github.com/pion/turn/v4/.goreleaser.yml delete mode 100644 vendor/github.com/pion/turn/v4/internal/allocation/allocation_manager.go delete mode 100644 vendor/github.com/pion/turn/v4/internal/allocation/errors.go delete mode 100644 vendor/github.com/pion/turn/v4/internal/server/errors.go delete mode 100644 vendor/github.com/pion/turn/v4/relay_address_generator_none.go delete mode 100644 vendor/github.com/pion/turn/v4/relay_address_generator_range.go delete mode 100644 vendor/github.com/pion/turn/v4/relay_address_generator_static.go rename vendor/github.com/pion/turn/{v4 => v5}/.gitignore (84%) rename vendor/github.com/pion/turn/{v4 => v5}/.golangci.yml (99%) create mode 100644 vendor/github.com/pion/turn/v5/.goreleaser.yml rename vendor/github.com/pion/turn/{v4 => v5}/LICENSE (94%) rename vendor/github.com/pion/turn/{v4 => v5}/README.md (86%) create mode 100644 vendor/github.com/pion/turn/v5/addr_family.go rename vendor/github.com/pion/turn/{v4 => v5}/client.go (66%) rename vendor/github.com/pion/turn/{v4 => v5}/codecov.yml (86%) rename vendor/github.com/pion/turn/{v4 => v5}/errors.go (94%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/allocation/allocation.go (66%) create mode 100644 vendor/github.com/pion/turn/v5/internal/allocation/allocation_manager.go rename vendor/github.com/pion/turn/{v4 => v5}/internal/allocation/channel_bind.go (91%) create mode 100644 vendor/github.com/pion/turn/v5/internal/allocation/errors.go rename vendor/github.com/pion/turn/{v4 => v5}/internal/allocation/event_handler.go (84%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/allocation/five_tuple.go (96%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/allocation/permission.go (73%) create mode 100644 vendor/github.com/pion/turn/v5/internal/auth/auth.go rename vendor/github.com/pion/turn/{v4 => v5}/internal/client/allocation.go (87%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/client/binding.go (98%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/client/client.go (87%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/client/errors.go (91%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/client/periodic_timer.go (96%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/client/permission.go (93%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/client/tcp_alloc.go (91%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/client/tcp_conn.go (91%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/client/transaction.go (98%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/client/trylock.go (88%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/client/udp_conn.go (87%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/ipnet/util.go (65%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/proto/addr.go (88%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/proto/chandata.go (98%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/proto/chann.go (96%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/proto/connection_id.go (93%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/proto/data.go (92%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/proto/dontfrag.go (94%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/proto/evenport.go (95%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/proto/lifetime.go (95%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/proto/peeraddr.go (94%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/proto/proto.go (94%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/proto/relayedaddr.go (94%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/proto/reqfamily.go (96%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/proto/reqtrans.go (96%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/proto/rsrvtoken.go (95%) rename vendor/github.com/pion/turn/{v4 => v5/internal/proto}/stun_conn.go (89%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/server/base36.go (95%) create mode 100644 vendor/github.com/pion/turn/v5/internal/server/errors.go rename vendor/github.com/pion/turn/{v4 => v5}/internal/server/nonce.go (96%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/server/server.go (71%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/server/short_nonce.go (98%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/server/stun.go (82%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/server/turn.go (51%) rename vendor/github.com/pion/turn/{v4 => v5}/internal/server/util.go (74%) rename vendor/github.com/pion/turn/{v4 => v5}/lt_cred.go (67%) create mode 100644 vendor/github.com/pion/turn/v5/relay_address_generator_none.go create mode 100644 vendor/github.com/pion/turn/v5/relay_address_generator_range.go create mode 100644 vendor/github.com/pion/turn/v5/relay_address_generator_static.go rename vendor/github.com/pion/turn/{v4 => v5}/renovate.json (100%) rename vendor/github.com/pion/turn/{v4 => v5}/server.go (75%) rename vendor/github.com/pion/turn/{v4 => v5}/server_config.go (80%) create mode 100644 vendor/github.com/pion/turn/v5/stun_conn.go create mode 100644 vendor/go.mongodb.org/mongo-driver/LICENSE create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bson.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/array_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/bsoncodec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/byte_slice_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/codec_cache.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/cond_addr_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_decoders.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_encoders.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/doc.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/empty_interface_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/map_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/mode.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/pointer_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/proxy.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/registry.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/slice_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/string_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/struct_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/struct_tag_parser.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/time_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/types.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/uint_codec.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/byte_slice_codec_options.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/doc.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/empty_interface_codec_options.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/map_codec_options.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/slice_codec_options.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/string_codec_options.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/struct_codec_options.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/time_codec_options.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/uint_codec_options.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/copier.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/doc.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_parser.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_reader.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_tables.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_wrappers.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_writer.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/json_scanner.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/mode.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/reader.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/value_reader.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/value_writer.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsonrw/writer.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/bsontype/bsontype.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/decoder.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/doc.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/encoder.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/marshal.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/primitive/decimal.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/primitive/objectid.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/primitive/primitive.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/primitive_codecs.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/raw.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/raw_element.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/raw_value.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/registry.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/types.go create mode 100644 vendor/go.mongodb.org/mongo-driver/bson/unmarshal.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/array.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/bson_arraybuilder.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/bson_documentbuilder.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/bsoncore.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/doc.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/document.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/document_sequence.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/element.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/tables.go create mode 100644 vendor/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/value.go create mode 100644 vendor/modernc.org/sqlite/lib/libsqlite3_freebsd.go create mode 100644 vendor/modernc.org/sqlite/lib/libsqlite3_windows.go diff --git a/go.mod b/go.mod index 07d6f551d2..df81a7b430 100644 --- a/go.mod +++ b/go.mod @@ -11,10 +11,10 @@ require ( github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260416073033-7c2071eaa8d4 github.com/VictoriaMetrics/fastcache v1.13.3 github.com/arsham/figurine v1.3.0 - github.com/aws/aws-sdk-go-v2 v1.41.5 - github.com/aws/aws-sdk-go-v2/config v1.32.14 - github.com/aws/aws-sdk-go-v2/credentials v1.19.14 - github.com/aws/aws-sdk-go-v2/service/route53 v1.62.5 + github.com/aws/aws-sdk-go-v2 v1.41.7 + github.com/aws/aws-sdk-go-v2/config v1.32.17 + github.com/aws/aws-sdk-go-v2/credentials v1.19.16 + github.com/aws/aws-sdk-go-v2/service/route53 v1.62.7 github.com/cespare/cp v1.1.1 github.com/charmbracelet/bubbletea v1.3.10 github.com/cloudflare/cloudflare-go v0.116.0 @@ -23,7 +23,7 @@ require ( github.com/crate-crypto/go-eth-kzg v1.5.0 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/dchest/siphash v1.2.3 - github.com/deckarep/golang-set/v2 v2.8.0 + github.com/deckarep/golang-set/v2 v2.9.0 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 github.com/dop251/goja v0.0.0-20260311135729-065cd970411c github.com/ethereum/c-kzg-4844 v1.0.3 @@ -32,7 +32,7 @@ require ( github.com/ferranbt/fastssz v1.0.0 github.com/fjl/gencodec v0.1.0 github.com/fogleman/ease v0.0.0-20170301025033-8da417bf1776 - github.com/fsnotify/fsnotify v1.9.0 + github.com/fsnotify/fsnotify v1.10.1 github.com/gballet/go-libpcsclite v0.0.0-20250918194357-1ec6f2e601c6 github.com/gofrs/flock v0.13.0 github.com/golang/snappy v1.0.0 @@ -49,7 +49,7 @@ require ( github.com/jedisct1/go-minisign v0.0.0-20241212093149-d2f9f49435c7 github.com/lucasb-eyer/go-colorful v1.4.0 github.com/mattn/go-colorable v0.1.14 - github.com/mattn/go-isatty v0.0.21 + github.com/mattn/go-isatty v0.0.22 github.com/muesli/reflow v0.3.0 github.com/muesli/termenv v0.16.0 github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 @@ -77,7 +77,7 @@ require ( ) require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 // indirect github.com/CortexFoundation/compress v0.0.0-20240218153512-9074bdc2397c // indirect github.com/CortexFoundation/cvm-runtime v0.0.0-20221117094012-b5a251885572 // indirect @@ -108,17 +108,17 @@ require ( github.com/antlabs/timer v0.1.4 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/arsham/rainbow v1.2.1 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect - github.com/aws/smithy-go v1.25.0 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 // indirect + github.com/aws/smithy-go v1.25.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/benbjohnson/immutable v0.4.3 // indirect @@ -135,7 +135,7 @@ require ( github.com/charmbracelet/x/term v0.2.2 // indirect github.com/clipperhouse/displaywidth v0.11.0 // indirect github.com/clipperhouse/uax29/v2 v2.7.0 // indirect - github.com/cockroachdb/errors v1.12.0 // indirect + github.com/cockroachdb/errors v1.13.0 // indirect github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 // indirect github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect github.com/cockroachdb/redact v1.1.8 // indirect @@ -145,7 +145,7 @@ require ( github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect github.com/dgraph-io/badger/v4 v4.9.1 // indirect github.com/dgraph-io/ristretto/v2 v2.4.0 // indirect - github.com/dlclark/regexp2 v1.11.5 // indirect + github.com/dlclark/regexp2 v1.12.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/edsrzf/mmap-go v1.2.0 // indirect github.com/elliotchance/orderedmap v1.8.0 // indirect @@ -153,7 +153,7 @@ require ( github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/felixge/fgprof v0.9.5 // indirect github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect - github.com/getsentry/sentry-go v0.45.1 // indirect + github.com/getsentry/sentry-go v0.46.2 // indirect github.com/go-llsqlite/adapter v0.2.0 // indirect github.com/go-llsqlite/crawshaw v0.6.0 // indirect github.com/go-logr/logr v1.4.3 // indirect @@ -171,8 +171,8 @@ require ( github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // indirect - github.com/jedib0t/go-pretty/v6 v6.7.9 // indirect - github.com/klauspost/compress v1.18.5 // indirect + github.com/jedib0t/go-pretty/v6 v6.7.10 // indirect + github.com/klauspost/compress v1.18.6 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect @@ -196,22 +196,22 @@ require ( github.com/pion/datachannel v1.6.0 // indirect github.com/pion/dtls/v2 v2.2.12 // indirect github.com/pion/dtls/v3 v3.1.2 // indirect - github.com/pion/ice/v4 v4.2.4 // indirect - github.com/pion/interceptor v0.1.44 // indirect + github.com/pion/ice/v4 v4.2.5 // indirect + github.com/pion/interceptor v0.1.45 // indirect github.com/pion/logging v0.2.4 // indirect github.com/pion/mdns/v2 v2.1.0 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/rtcp v1.2.16 // indirect - github.com/pion/rtp v1.10.1 // indirect - github.com/pion/sctp v1.9.4 // indirect + github.com/pion/rtp v1.10.2 // indirect + github.com/pion/sctp v1.9.5 // indirect github.com/pion/sdp/v3 v3.0.18 // indirect github.com/pion/srtp/v3 v3.0.10 // indirect github.com/pion/stun/v3 v3.1.2 // indirect github.com/pion/transport/v2 v2.2.10 // indirect github.com/pion/transport/v3 v3.1.1 // indirect github.com/pion/transport/v4 v4.0.1 // indirect - github.com/pion/turn/v4 v4.1.4 // indirect - github.com/pion/webrtc/v4 v4.2.11 // indirect + github.com/pion/turn/v5 v5.0.3 // indirect + github.com/pion/webrtc/v4 v4.2.12 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.23.2 // indirect @@ -243,6 +243,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/zeebo/xxh3 v1.1.0 // indirect go.etcd.io/bbolt v1.4.3 // indirect + go.mongodb.org/mongo-driver v1.17.9 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/otel v1.43.0 // indirect go.opentelemetry.io/otel/metric v1.43.0 // indirect @@ -255,9 +256,9 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.4.1 // indirect - modernc.org/libc v1.72.0 // indirect + modernc.org/libc v1.72.2 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect - modernc.org/sqlite v1.48.2 // indirect + modernc.org/sqlite v1.50.0 // indirect zombiezen.com/go/sqlite v1.4.2 // indirect ) diff --git a/go.sum b/go.sum index 0f709e7fea..b8d3d88e57 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,8 @@ filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7 git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.1 h1:jHb/wfvRikGdxMXYV3QG/SzUOPYN9KEUUuC0Yd0/vC0= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.1/go.mod h1:pzBXCYn05zvYIrwLgtK8Ap8QcjRg+0i76tMQdWN6wOk= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 h1:fhqpLE3UEXi9lPaBRpQ6XuRW0nU7hgg4zlmZZa+a9q4= @@ -264,36 +264,36 @@ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.30.24/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.31.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY= -github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= -github.com/aws/aws-sdk-go-v2/config v1.32.14 h1:opVIRo/ZbbI8OIqSOKmpFaY7IwfFUOCCXBsUpJOwDdI= -github.com/aws/aws-sdk-go-v2/config v1.32.14/go.mod h1:U4/V0uKxh0Tl5sxmCBZ3AecYny4UNlVmObYjKuuaiOo= -github.com/aws/aws-sdk-go-v2/credentials v1.19.14 h1:n+UcGWAIZHkXzYt87uMFBv/l8THYELoX6gVcUvgl6fI= -github.com/aws/aws-sdk-go-v2/credentials v1.19.14/go.mod h1:cJKuyWB59Mqi0jM3nFYQRmnHVQIcgoxjEMAbLkpr62w= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 h1:NUS3K4BTDArQqNu2ih7yeDLaS3bmHD0YndtA6UP884g= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21/go.mod h1:YWNWJQNjKigKY1RHVJCuupeWDrrHjRqHm0N9rdrWzYI= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA= -github.com/aws/aws-sdk-go-v2/service/route53 v1.62.5 h1:Z+/OLsb85Kpq7TVLCspskqePaf68Tdv6GfmJP4kH6i0= -github.com/aws/aws-sdk-go-v2/service/route53 v1.62.5/go.mod h1:TmxGowuBYwjmHFOsEDxaZdsQE62JJzOmtiWafTi/czg= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.9/go.mod h1:7yuQJoT+OoH8aqIxw9vwF+8KpvLZ8AWmvmUWHsGQZvI= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 h1:lFd1+ZSEYJZYvv9d6kXzhkZu07si3f+GQ1AaYwa2LUM= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.15/go.mod h1:WSvS1NLr7JaPunCXqpJnWk1Bjo7IxzZXrZi1QQCkuqM= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 h1:dzztQ1YmfPrxdrOiuZRMF6fuOwWlWpD2StNLTceKpys= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBUdErbMnAFFp12Lm/U= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw= -github.com/aws/smithy-go v1.25.0 h1:Sz/XJ64rwuiKtB6j98nDIPyYrV1nVNJ4YU74gttcl5U= -github.com/aws/smithy-go v1.25.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/aws-sdk-go-v2 v1.41.7 h1:DWpAJt66FmnnaRIOT/8ASTucrvuDPZASqhhLey6tLY8= +github.com/aws/aws-sdk-go-v2 v1.41.7/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc= +github.com/aws/aws-sdk-go-v2/config v1.32.17 h1:FpL4/758/diKwqbytU0prpuiu60fgXKUWCpDJtApclU= +github.com/aws/aws-sdk-go-v2/config v1.32.17/go.mod h1:OXqUMzgXytfoF9JaKkhrOYsyh72t9G+MJH8mMRaexOE= +github.com/aws/aws-sdk-go-v2/credentials v1.19.16 h1:r3RJBuU7X9ibt8RHbMjWE6y60QbKBiII6wSrXnapxSU= +github.com/aws/aws-sdk-go-v2/credentials v1.19.16/go.mod h1:6cx7zqDENJDbBIIWX6P8s0h6hqHC8Avbjh9Dseo27ug= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 h1:UuSfcORqNSz/ey3VPRS8TcVH2Ikf0/sC+Hdj400QI6U= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23/go.mod h1:+G/OSGiOFnSOkYloKj/9M35s74LgVAdJBSD5lsFfqKg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 h1:GpT/TrnBYuE5gan2cZbTtvP+JlHsutdmlV2YfEyNde0= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23/go.mod h1:xYWD6BS9ywC5bS3sz9Xh04whO/hzK2plt2Zkyrp4JuA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 h1:bpd8vxhlQi2r1hiueOw02f/duEPTMK59Q4QMAoTTtTo= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23/go.mod h1:15DfR2nw+CRHIk0tqNyifu3G1YdAOy68RftkhMDDwYk= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 h1:OQqn11BtaYv1WLUowvcA30MpzIu8Ti4pcLPIIyoKZrA= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24/go.mod h1:X5ZJyfwVrWA96GzPmUCWFQaEARPR7gCrpq2E92PJwAE= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 h1:FLudkZLt5ci0ozzgkVo8BJGwvqNaZbTWb3UcucAateA= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9/go.mod h1:w7wZ/s9qK7c8g4al+UyoF1Sp/Z45UwMGcqIzLWVQHWk= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 h1:pbrxO/kuIwgEsOPLkaHu0O+m4fNgLU8B3vxQ+72jTPw= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23/go.mod h1:/CMNUqoj46HpS3MNRDEDIwcgEnrtZlKRaHNaHxIFpNA= +github.com/aws/aws-sdk-go-v2/service/route53 v1.62.7 h1:twRRMmtSITnt/rrp+D7UDLzE5pKMZe759aalkUdN+OY= +github.com/aws/aws-sdk-go-v2/service/route53 v1.62.7/go.mod h1:ztM1lr+sRoCAI8336ZUvlRPbToue0d3gE/wd6jomSJ8= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 h1:TdJ+HdzOBhU8+iVAOGUTU63VXopcumCOF1paFulHWZc= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.11/go.mod h1:R82ZRExE/nheo0N+T8zHPcLRTcH8MGsnR3BiVGX0TwI= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 h1:7byT8HUWrgoRp6sXjxtZwgOKfhss5fW6SkLBtqzgRoE= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.17/go.mod h1:xNWknVi4Ezm1vg1QsB/5EWpAJURq22uqd38U8qKvOJc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 h1:+1Kl1zx6bWi4X7cKi3VYh29h8BvsCoHQEQ6ST9X8w7w= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21/go.mod h1:4vIRDq+CJB2xFAXZ+YgGUTiEft7oAQlhIs71xcSeuVg= +github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 h1:F/M5Y9I3nwr2IEpshZgh1GeHpOItExNM9L1euNuh/fk= +github.com/aws/aws-sdk-go-v2/service/sts v1.42.1/go.mod h1:mTNxImtovCOEEuD65mKW7DCsL+2gjEH+RPEAexAzAio= +github.com/aws/smithy-go v1.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI= +github.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= @@ -374,8 +374,8 @@ github.com/cloudflare/cloudflare-go v0.116.0/go.mod h1:Ds6urDwn/TF2uIU24mu7H91xk github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= -github.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo= -github.com/cockroachdb/errors v1.12.0/go.mod h1:SvzfYNNBshAVbZ8wzNc/UPK3w1vf0dKDUP41ucAIf7g= +github.com/cockroachdb/errors v1.13.0 h1:BoCcJeiP9hpBJDETkX19qi8Tb8So37srSsp3stTaDMQ= +github.com/cockroachdb/errors v1.13.0/go.mod h1:bjxt/4E5+OyuAnacpTIU9rn2mzPu1VlthvHP+xpROq0= github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 h1:pU88SPhIFid6/k0egdR5V6eALQYq2qbSmukrkgIh/0A= github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 h1:ASDL+UJcILMqgNeV5jiqR4j+sTuvQNHdf2chuKj1M5k= @@ -419,8 +419,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= -github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ= -github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/deckarep/golang-set/v2 v2.9.0 h1:prva4eP9UysWagLyKrtn074ughi0NnkIf0A4M5yOCKI= +github.com/deckarep/golang-set/v2 v2.9.0/go.mod h1:EWknQXbs0mcFpat2QOoXV0Ee57cD+w6ZEN76BR2JVrM= github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo= @@ -435,8 +435,8 @@ github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa5 github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= -github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8= +github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= @@ -501,16 +501,16 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= +github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgxrzK5E1fW7RQGeDwE8F/ZZnUYc= github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/gballet/go-libpcsclite v0.0.0-20250918194357-1ec6f2e601c6 h1:ko+DlyhLqUHpgrvwqs5ybydoGAqjpJQTXpAS7vUqVlM= github.com/gballet/go-libpcsclite v0.0.0-20250918194357-1ec6f2e601c6/go.mod h1:3IVE7v4II2gS2V5amIH7F7NeYQtbbORtQtjdflgS1vk= -github.com/getsentry/sentry-go v0.45.1 h1:9rfzJtGiJG+MGIaWZXidDGHcH5GU1Z5y0WVJGf9nysw= -github.com/getsentry/sentry-go v0.45.1/go.mod h1:XDotiNZbgf5U8bPDUAfvcFmOnMQQceESxyKaObSssW0= +github.com/getsentry/sentry-go v0.46.2 h1:1jhYwrKGa3sIpo/y5iDNXS5wDoT7I1KNzMHrnK6ojns= +github.com/getsentry/sentry-go v0.46.2/go.mod h1:evVbw2qotNUdYG8KxXbAdjOQWWvWIwKxpjdZZIvcIPw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= @@ -743,8 +743,8 @@ github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1: github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= -github.com/jedib0t/go-pretty/v6 v6.7.9 h1:frarzQWmkZd97syT81+TH8INKPpzoxQnk+Mk5EIHSrM= -github.com/jedib0t/go-pretty/v6 v6.7.9/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= +github.com/jedib0t/go-pretty/v6 v6.7.10 h1:B/2qW2Bkv2L6n14PP8o1kx75kWzHOQ3YTluWzg9icac= +github.com/jedib0t/go-pretty/v6 v6.7.10/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= github.com/jedisct1/go-minisign v0.0.0-20241212093149-d2f9f49435c7 h1:FWpSWRD8FbVkKQu8M1DM9jF5oXFLyE+XpisIYfdzbic= github.com/jedisct1/go-minisign v0.0.0-20241212093149-d2f9f49435c7/go.mod h1:BMxO138bOokdgt4UaxZiEfypcSHX0t6SIFimVP1oRfk= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= @@ -776,8 +776,8 @@ github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6 github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= -github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= +github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= @@ -835,8 +835,8 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.13-0.20200128103942-cb30d6282491/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs= -github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= +github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= +github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= @@ -985,10 +985,10 @@ github.com/pion/dtls/v3 v3.1.2 h1:gqEdOUXLtCGW+afsBLO0LtDD8GnuBBjEy6HRtyofZTc= github.com/pion/dtls/v3 v3.1.2/go.mod h1:Hw/igcX4pdY69z1Hgv5x7wJFrUkdgHwAn/Q/uo7YHRo= github.com/pion/ice v0.7.13/go.mod h1:U3ERMkJgkPMlBjzMe2XxIQPl6ZrfRHyENwGCBoFrWWk= github.com/pion/ice v0.7.14/go.mod h1:/Lz6jAUhsvXed7kNJImXtvVSgjtcdGKoZAZIYb9WEm0= -github.com/pion/ice/v4 v4.2.4 h1:6IU9X19HNcgjieVlp0qntG9jca/RTgo1770c/t5JrV4= -github.com/pion/ice/v4 v4.2.4/go.mod h1:ELQIH5Xmcs/nsJnKyzLJWij8v9Z/xHOzdPTfpFZMh2o= -github.com/pion/interceptor v0.1.44 h1:sNlZwM8dWXU9JQAkJh8xrarC0Etn8Oolcniukmuy0/I= -github.com/pion/interceptor v0.1.44/go.mod h1:4atVlBkcgXuUP+ykQF0qOCGU2j7pQzX2ofvPRFsY5RY= +github.com/pion/ice/v4 v4.2.5 h1:5umUQy4hX6HwMsCnJ0SX337YYCeTWDgC9JWyvUqHIHs= +github.com/pion/ice/v4 v4.2.5/go.mod h1:aaABRaykEYnNjccjbiimuYxViaASeuv5mk9BpplUxK0= +github.com/pion/interceptor v0.1.45 h1:6PUo/5829bIfRFIPPJQzuDn8EjxRTSB/CSD7QVCOaqo= +github.com/pion/interceptor v0.1.45/go.mod h1:gNDYM/uFKcLe/B3gS2/7+aw6z+RDiMy2qKTnF1LO31w= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8= github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so= @@ -1003,11 +1003,11 @@ github.com/pion/rtcp v1.2.16 h1:fk1B1dNW4hsI78XUCljZJlC4kZOPk67mNRuQ0fcEkSo= github.com/pion/rtcp v1.2.16/go.mod h1:/as7VKfYbs5NIb4h6muQ35kQF/J0ZVNz2Z3xKoCBYOo= github.com/pion/rtp v1.3.2/go.mod h1:q9wPnA96pu2urCcW/sK/RiDn597bhGoAQQ+y2fDwHuY= github.com/pion/rtp v1.4.0/go.mod h1:/l4cvcKd0D3u9JLs2xSVI95YkfXW87a3br3nqmVtSlE= -github.com/pion/rtp v1.10.1 h1:xP1prZcCTUuhO2c83XtxyOHJteISg6o8iPsE2acaMtA= -github.com/pion/rtp v1.10.1/go.mod h1:rF5nS1GqbR7H/TCpKwylzeq6yDM+MM6k+On5EgeThEM= +github.com/pion/rtp v1.10.2 h1:l+f6tTDcAH6xwepaAoW791ddhuYsJlqRATOzirO04Mo= +github.com/pion/rtp v1.10.2/go.mod h1:Au8fc6cEByy8RLTwKTQTEeQqDB/SJDxwL4mZuxYA5Pk= github.com/pion/sctp v1.7.6/go.mod h1:ichkYQ5tlgCQwEwvgfdcAolqx1nHbYCxo4D7zK/K0X8= -github.com/pion/sctp v1.9.4 h1:cMxEu0F5tbP4qH07bKf1Zjf4rUih9LIo0qQt424e258= -github.com/pion/sctp v1.9.4/go.mod h1:N20Dq6LY+JvJDAh9VVh1JELngb2rQ8dPgds5yBWiPgw= +github.com/pion/sctp v1.9.5 h1:QoSFB/drmAsmSeSFNQNI3xx010nW4HsycCZckRVWWag= +github.com/pion/sctp v1.9.5/go.mod h1:N20Dq6LY+JvJDAh9VVh1JELngb2rQ8dPgds5yBWiPgw= github.com/pion/sdp/v2 v2.3.7/go.mod h1:+ZZf35r1+zbaWYiZLfPutWfx58DAWcGb2QsS3D/s9M8= github.com/pion/sdp/v3 v3.0.18 h1:l0bAXazKHpepazVdp+tPYnrsy9dfh7ZbT8DxesH5ZnI= github.com/pion/sdp/v3 v3.0.18/go.mod h1:ZREGo6A9ZygQ9XkqAj5xYCQtQpif0i6Pa81HOiAdqQ8= @@ -1035,10 +1035,12 @@ github.com/pion/transport/v4 v4.0.1/go.mod h1:nEuEA4AD5lPdcIegQDpVLgNoDGreqM/Yqm github.com/pion/turn/v2 v2.0.3/go.mod h1:kl1hmT3NxcLynpXVnwJgObL8C9NaCyPTeqI2DcCpSZs= github.com/pion/turn/v4 v4.1.4 h1:EU11yMXKIsK43FhcUnjLlrhE4nboHZq+TXBIi3QpcxQ= github.com/pion/turn/v4 v4.1.4/go.mod h1:ES1DXVFKnOhuDkqn9hn5VJlSWmZPaRJLyBXoOeO/BmQ= +github.com/pion/turn/v5 v5.0.3 h1:I+Nw0fQgdPWF1SXDj0egWDhCkcff7gWiigdQpOK52Ak= +github.com/pion/turn/v5 v5.0.3/go.mod h1:fs4SogUh/aRGQzonc4Lx3Jp4EU3j3t0PfNDEd9KcD/w= github.com/pion/webrtc/v2 v2.2.7/go.mod h1:EfCuvKjzMgX4F/aSryRUC7L9o3u2N8WNUgnzd6wOO+8= github.com/pion/webrtc/v2 v2.2.9/go.mod h1:TcArPDphZIBtZ+mh8J/qOREyY3ca7ihQrenulOIvfPQ= -github.com/pion/webrtc/v4 v4.2.11 h1:QUX1QZKlNIn4O7U5JxLPGP0sV5RTncZkzu9SPR3jVNU= -github.com/pion/webrtc/v4 v4.2.11/go.mod h1:s/rAiyy77GyRFrZMx+Ls6aua26dIBPudH8/ZHYbIRWY= +github.com/pion/webrtc/v4 v4.2.12 h1:ux8i+aJxu0OdhcAcVO39JEeodWugD0wdVJoRDtXk1CY= +github.com/pion/webrtc/v4 v4.2.12/go.mod h1:M/DeGZkhdWZVmVgGr34HOD9yUDekVJtz9c9PGO18urQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -1314,6 +1316,8 @@ go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5tU= +go.mongodb.org/mongo-driver v1.17.9/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -1778,10 +1782,10 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= -modernc.org/cc/v4 v4.27.3 h1:uNCgn37E5U09mTv1XgskEVUJ8ADKpmFMPxzGJ0TSo+U= -modernc.org/cc/v4 v4.27.3/go.mod h1:3YjcbCqhoTTHPycJDRl2WZKKFj0nwcOIPBfEZK0Hdk8= -modernc.org/ccgo/v4 v4.32.4 h1:L5OB8rpEX4ZsXEQwGozRfJyJSFHbbNVOoQ59DU9/KuU= -modernc.org/ccgo/v4 v4.32.4/go.mod h1:lY7f+fiTDHfcv6YlRgSkxYfhs+UvOEEzj49jAn2TOx0= +modernc.org/cc/v4 v4.28.1 h1:XpLbkYVQ24E8tX5u8+yWGvaxerxkR/S4zqxI8ZoSBuc= +modernc.org/cc/v4 v4.28.1/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI= +modernc.org/ccgo/v4 v4.34.0 h1:yRLPFZieg532OT4rp4JFNIVcquwalMX26G95WQDqwCQ= +modernc.org/ccgo/v4 v4.34.0/go.mod h1:AS5WYMyBakQ+fhsHhtP8mWB82KTGPkNNJDGfGQCe0/A= modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM= modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU= modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= @@ -1790,18 +1794,18 @@ modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo= modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= -modernc.org/libc v1.72.0 h1:IEu559v9a0XWjw0DPoVKtXpO2qt5NVLAnFaBbjq+n8c= -modernc.org/libc v1.72.0/go.mod h1:tTU8DL8A+XLVkEY3x5E/tO7s2Q/q42EtnNWda/L5QhQ= +modernc.org/libc v1.72.2 h1:HwRjrHwX7hZIFCfRyw6otVlY+BoZEHFQmHQa5B0BzDE= +modernc.org/libc v1.72.2/go.mod h1:43RZAMuEX483KwP1bW+3lTFm3dzwFpl6R8HMEutqy/w= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= -modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= -modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg= +modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.48.2 h1:5CnW4uP8joZtA0LedVqLbZV5GD7F/0x91AXeSyjoh5c= -modernc.org/sqlite v1.48.2/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig= +modernc.org/sqlite v1.50.0 h1:eMowQSWLK0MeiQTdmz3lqoF5dqclujdlIKeJA11+7oM= +modernc.org/sqlite v1.50.0/go.mod h1:m0w8xhwYUVY3H6pSDwc3gkJ/irZT/0YEXwBlhaxQEew= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md index fa477145fa..d730a3bc35 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md @@ -1,5 +1,18 @@ # Release History +## 1.21.1 (2026-04-16) + +### Bugs Fixed + +* Fixed an issue in `ResponseError.Error()` where the request URL path was being logged unescaped. +* Redact query parameters when logging errors. +* For `runtime.JoinPaths`, don't add a slash between root and paths when `paths` starts with `?` (query string). + +### Other Changes + +* Upgraded to Go 1.25.0. +* Upgraded dependencies. + ## 1.21.0 (2026-01-12) ### Features Added diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/response_error.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/response_error.go index ef0635bb22..8246b6a5b8 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/response_error.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/response_error.go @@ -131,7 +131,7 @@ func (e *ResponseError) Error() string { msg := &bytes.Buffer{} if e.RawResponse != nil { if e.RawResponse.Request != nil { - fmt.Fprintf(msg, "%s %s://%s%s\n", e.RawResponse.Request.Method, e.RawResponse.Request.URL.Scheme, e.RawResponse.Request.URL.Host, e.RawResponse.Request.URL.Path) + fmt.Fprintf(msg, "%s %s://%s%s\n", e.RawResponse.Request.Method, e.RawResponse.Request.URL.Scheme, e.RawResponse.Request.URL.Host, e.RawResponse.Request.URL.EscapedPath()) } else { fmt.Fprintln(msg, "Request information not available") } diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go index 213202e336..8ad391dd26 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go @@ -37,5 +37,5 @@ const ( Module = "azcore" // Version is the semantic version (see http://semver.org) of this module. - Version = "v1.21.0" + Version = "v1.21.1" ) diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_http_trace.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_http_trace.go index ddf9ede01e..7017d5e106 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_http_trace.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_http_trace.go @@ -5,10 +5,8 @@ package runtime import ( "context" - "errors" "fmt" "net/http" - "net/url" "strings" "github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported" @@ -73,14 +71,7 @@ func (h *httpTracePolicy) Do(req *policy.Request) (resp *http.Response, err erro span.SetAttributes(tracing.Attribute{Key: attrAZServiceReqID, Value: reqID}) } } else if err != nil { - var urlErr *url.Error - if errors.As(err, &urlErr) { - // calling *url.Error.Error() will include the unsanitized URL - // which we don't want. in addition, we already have the HTTP verb - // and sanitized URL in the trace so we aren't losing any info - err = urlErr.Err - } - span.SetStatus(tracing.SpanStatusError, err.Error()) + span.SetStatus(tracing.SpanStatusError, getSanitizedURLString(err.Error(), req.Raw().URL, h.allowedQP)) } span.End() }() diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_logging.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_logging.go index dd59fbc99b..d9e6481de8 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_logging.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_logging.go @@ -158,6 +158,27 @@ func getSanitizedURL(u url.URL, allowedQueryParams map[string]struct{}) string { return u.String() } +// getSanitizedURLString returns s with the query params of u redacted. +// if s doesn't contain u, then s is returned unchanged. +func getSanitizedURLString(s string, u *url.URL, allowedQueryParams map[string]struct{}) string { + if u.RawQuery == "" { + // the URL doesn't have any query params, so nothing to redact + return s + } + + urlIndex := strings.Index(strings.ToLower(s), strings.ToLower(u.String())) + if urlIndex < 0 { + // the URL isn't in the string, so nothing to redact + return s + } + + // replace the unsanitized URL in the error message with the sanitized version + sanitizedURL := getSanitizedURL(*u, allowedQueryParams) + s = s[:urlIndex] + sanitizedURL + s[urlIndex+len(u.String()):] + + return s +} + // writeRequestWithResponse appends a formatted HTTP request into a Buffer. If request and/or err are // not nil, then these are also written into the Buffer. func (p *logPolicy) writeRequestWithResponse(b *bytes.Buffer, req *policy.Request, resp *http.Response, err error) { @@ -171,7 +192,7 @@ func (p *logPolicy) writeRequestWithResponse(b *bytes.Buffer, req *policy.Reques } if err != nil { fmt.Fprintln(b, " --------------------------------------------------------------------------------") - fmt.Fprint(b, " ERROR:\n"+err.Error()+"\n") + fmt.Fprint(b, " ERROR:\n"+getSanitizedURLString(err.Error(), req.Raw().URL, p.allowedQP)+"\n") } } diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/request.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/request.go index df7826b763..20d86496d2 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/request.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/request.go @@ -90,12 +90,16 @@ func JoinPaths(root string, paths ...string) string { } if qps != "" { - p = p + "?" + qps + if strings.Contains(p, "?") { + p = p + "&" + qps + } else { + p = p + "?" + qps + } } if strings.HasSuffix(root, "/") && strings.HasPrefix(p, "/") { root = root[:len(root)-1] - } else if !strings.HasSuffix(root, "/") && !strings.HasPrefix(p, "/") { + } else if !strings.HasSuffix(root, "/") && !strings.HasPrefix(p, "/") && !strings.HasPrefix(p, "?") { p = "/" + p } return root + p diff --git a/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go index 57bfbfb694..e589f61561 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go @@ -3,4 +3,4 @@ package aws // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.41.5" +const goModuleVersion = "1.41.7" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/config/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/config/CHANGELOG.md index b819f6f08b..6f932e910a 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/config/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/config/CHANGELOG.md @@ -1,3 +1,17 @@ +# v1.32.17 (2026-04-29) + +* **Dependency Update**: Update to smithy-go v1.25.1. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.16 (2026-04-17) + +* **Dependency Update**: Bump smithy-go to 1.25.0 to support endpointBdd trait +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.15 (2026-04-16) + +* No change notes available for this release. + # v1.32.14 (2026-04-02) * **Dependency Update**: Updated to the latest SDK module versions diff --git a/vendor/github.com/aws/aws-sdk-go-v2/config/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/config/go_module_metadata.go index c311f3b003..fdbfa78e45 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/config/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/config/go_module_metadata.go @@ -3,4 +3,4 @@ package config // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.32.14" +const goModuleVersion = "1.32.17" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/ini/errors.go b/vendor/github.com/aws/aws-sdk-go-v2/config/internal/ini/errors.go similarity index 100% rename from vendor/github.com/aws/aws-sdk-go-v2/internal/ini/errors.go rename to vendor/github.com/aws/aws-sdk-go-v2/config/internal/ini/errors.go diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/ini/ini.go b/vendor/github.com/aws/aws-sdk-go-v2/config/internal/ini/ini.go similarity index 100% rename from vendor/github.com/aws/aws-sdk-go-v2/internal/ini/ini.go rename to vendor/github.com/aws/aws-sdk-go-v2/config/internal/ini/ini.go diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/ini/parse.go b/vendor/github.com/aws/aws-sdk-go-v2/config/internal/ini/parse.go similarity index 100% rename from vendor/github.com/aws/aws-sdk-go-v2/internal/ini/parse.go rename to vendor/github.com/aws/aws-sdk-go-v2/config/internal/ini/parse.go diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/ini/sections.go b/vendor/github.com/aws/aws-sdk-go-v2/config/internal/ini/sections.go similarity index 100% rename from vendor/github.com/aws/aws-sdk-go-v2/internal/ini/sections.go rename to vendor/github.com/aws/aws-sdk-go-v2/config/internal/ini/sections.go diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/ini/strings.go b/vendor/github.com/aws/aws-sdk-go-v2/config/internal/ini/strings.go similarity index 100% rename from vendor/github.com/aws/aws-sdk-go-v2/internal/ini/strings.go rename to vendor/github.com/aws/aws-sdk-go-v2/config/internal/ini/strings.go diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/ini/token.go b/vendor/github.com/aws/aws-sdk-go-v2/config/internal/ini/token.go similarity index 100% rename from vendor/github.com/aws/aws-sdk-go-v2/internal/ini/token.go rename to vendor/github.com/aws/aws-sdk-go-v2/config/internal/ini/token.go diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/ini/tokenize.go b/vendor/github.com/aws/aws-sdk-go-v2/config/internal/ini/tokenize.go similarity index 100% rename from vendor/github.com/aws/aws-sdk-go-v2/internal/ini/tokenize.go rename to vendor/github.com/aws/aws-sdk-go-v2/config/internal/ini/tokenize.go diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/ini/value.go b/vendor/github.com/aws/aws-sdk-go-v2/config/internal/ini/value.go similarity index 100% rename from vendor/github.com/aws/aws-sdk-go-v2/internal/ini/value.go rename to vendor/github.com/aws/aws-sdk-go-v2/config/internal/ini/value.go diff --git a/vendor/github.com/aws/aws-sdk-go-v2/config/shared_config.go b/vendor/github.com/aws/aws-sdk-go-v2/config/shared_config.go index 44c616fd57..5b251f54f5 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/config/shared_config.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/config/shared_config.go @@ -12,8 +12,8 @@ import ( "time" "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config/internal/ini" "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" - "github.com/aws/aws-sdk-go-v2/internal/ini" "github.com/aws/aws-sdk-go-v2/internal/shareddefaults" "github.com/aws/smithy-go/logging" smithyrequestcompression "github.com/aws/smithy-go/private/requestcompression" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/credentials/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/credentials/CHANGELOG.md index 6e28ba4f4b..0b215e6b83 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/credentials/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/credentials/CHANGELOG.md @@ -1,3 +1,13 @@ +# v1.19.16 (2026-04-29) + +* **Dependency Update**: Update to smithy-go v1.25.1. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.15 (2026-04-17) + +* **Dependency Update**: Bump smithy-go to 1.25.0 to support endpointBdd trait +* **Dependency Update**: Updated to the latest SDK module versions + # v1.19.14 (2026-04-02) * **Dependency Update**: Updated to the latest SDK module versions diff --git a/vendor/github.com/aws/aws-sdk-go-v2/credentials/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/credentials/go_module_metadata.go index e1e8954049..5abad90cd9 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/credentials/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/credentials/go_module_metadata.go @@ -3,4 +3,4 @@ package credentials // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.19.14" +const goModuleVersion = "1.19.16" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/CHANGELOG.md index 829592ace2..e17294549f 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/CHANGELOG.md @@ -1,3 +1,13 @@ +# v1.18.23 (2026-04-29) + +* **Dependency Update**: Update to smithy-go v1.25.1. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.18.22 (2026-04-17) + +* **Dependency Update**: Bump smithy-go to 1.25.0 to support endpointBdd trait +* **Dependency Update**: Updated to the latest SDK module versions + # v1.18.21 (2026-03-26) * **Dependency Update**: Updated to the latest SDK module versions diff --git a/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/go_module_metadata.go index 52c3d3923d..7f59387edc 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/go_module_metadata.go @@ -3,4 +3,4 @@ package imds // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.18.21" +const goModuleVersion = "1.18.23" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md index 1def5e2d9f..0990a4143a 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md @@ -1,3 +1,13 @@ +# v1.4.23 (2026-04-29) + +* **Dependency Update**: Update to smithy-go v1.25.1. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.22 (2026-04-17) + +* **Dependency Update**: Bump smithy-go to 1.25.0 to support endpointBdd trait +* **Dependency Update**: Updated to the latest SDK module versions + # v1.4.21 (2026-03-26) * **Dependency Update**: Updated to the latest SDK module versions diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go index 548da96016..05a8d3e7bc 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go @@ -3,4 +3,4 @@ package configsources // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.4.21" +const goModuleVersion = "1.4.23" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md index a2a1c183ff..49577e3e94 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md @@ -1,3 +1,13 @@ +# v2.7.23 (2026-04-29) + +* **Dependency Update**: Update to smithy-go v1.25.1. +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.22 (2026-04-17) + +* **Dependency Update**: Bump smithy-go to 1.25.0 to support endpointBdd trait +* **Dependency Update**: Updated to the latest SDK module versions + # v2.7.21 (2026-03-26) * **Dependency Update**: Updated to the latest SDK module versions diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go index 03a0b8c038..1e92900a1e 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go @@ -3,4 +3,4 @@ package endpoints // goModuleVersion is the tagged release for this module -const goModuleVersion = "2.7.21" +const goModuleVersion = "2.7.23" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/ini/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/internal/ini/CHANGELOG.md deleted file mode 100644 index fdf434a5eb..0000000000 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/ini/CHANGELOG.md +++ /dev/null @@ -1,296 +0,0 @@ -# v1.8.6 (2026-03-13) - -* **Bug Fix**: Replace usages of the old ioutil/ package throughout the SDK. - -# v1.8.5 (2026-03-03) - -* **Bug Fix**: Modernize non codegen files with go fix -* **Dependency Update**: Bump minimum Go version to 1.24 - -# v1.8.4 (2025-10-16) - -* **Dependency Update**: Bump minimum Go version to 1.23. - -# v1.8.3 (2025-02-18) - -* **Bug Fix**: Bump go version to 1.22 - -# v1.8.2 (2025-01-24) - -* **Bug Fix**: Refactor filepath.Walk to filepath.WalkDir - -# v1.8.1 (2024-08-15) - -* **Dependency Update**: Bump minimum Go version to 1.21. - -# v1.8.0 (2024-02-13) - -* **Feature**: Bump minimum Go version to 1.20 per our language support policy. - -# v1.7.3 (2024-01-22) - -* **Bug Fix**: Remove invalid escaping of shared config values. All values in the shared config file will now be interpreted literally, save for fully-quoted strings which are unwrapped for legacy reasons. - -# v1.7.2 (2023-12-08) - -* **Bug Fix**: Correct loading of [services *] sections into shared config. - -# v1.7.1 (2023-11-16) - -* **Bug Fix**: Fix recognition of trailing comments in shared config properties. # or ; separators that aren't preceded by whitespace at the end of a property value should be considered part of it. - -# v1.7.0 (2023-11-13) - -* **Feature**: Replace the legacy config parser with a modern, less-strict implementation. Parsing failures within a section will now simply ignore the invalid line rather than silently drop the entire section. - -# v1.6.0 (2023-11-09.2) - -* **Feature**: BREAKFIX: In order to support subproperty parsing, invalid property definitions must not be ignored - -# v1.5.2 (2023-11-09) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.5.1 (2023-11-07) - -* **Bug Fix**: Fix subproperty performance regression - -# v1.5.0 (2023-11-01) - -* **Feature**: Adds support for configured endpoints via environment variables and the AWS shared configuration file. -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.4.0 (2023-10-31) - -* **Feature**: **BREAKING CHANGE**: Bump minimum go version to 1.19 per the revised [go version support policy](https://aws.amazon.com/blogs/developer/aws-sdk-for-go-aligns-with-go-release-policy-on-supported-runtimes/). -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.45 (2023-10-12) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.44 (2023-10-06) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.43 (2023-09-22) - -* **Bug Fix**: Fixed a bug where merging `max_attempts` or `duration_seconds` fields across shared config files with invalid values would silently default them to 0. -* **Bug Fix**: Move type assertion of config values out of the parsing stage, which resolves an issue where the contents of a profile would silently be dropped with certain numeric formats. - -# v1.3.42 (2023-08-21) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.41 (2023-08-18) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.40 (2023-08-17) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.39 (2023-08-07) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.38 (2023-07-31) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.37 (2023-07-28) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.36 (2023-07-13) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.35 (2023-06-13) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.34 (2023-04-24) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.33 (2023-04-07) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.32 (2023-03-21) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.31 (2023-03-10) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.30 (2023-02-20) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.29 (2023-02-03) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.28 (2022-12-15) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.27 (2022-12-02) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.26 (2022-10-24) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.25 (2022-10-21) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.24 (2022-09-20) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.23 (2022-09-14) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.22 (2022-09-02) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.21 (2022-08-31) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.20 (2022-08-29) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.19 (2022-08-11) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.18 (2022-08-09) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.17 (2022-08-08) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.16 (2022-08-01) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.15 (2022-07-05) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.14 (2022-06-29) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.13 (2022-06-07) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.12 (2022-05-17) - -* **Bug Fix**: Removes the fuzz testing files from the module, as they are invalid and not used. -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.11 (2022-04-25) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.10 (2022-03-30) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.9 (2022-03-24) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.8 (2022-03-23) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.7 (2022-03-08) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.6 (2022-02-24) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.5 (2022-01-28) - -* **Bug Fix**: Fixes the SDK's handling of `duration_sections` in the shared credentials file or specified in multiple shared config and shared credentials files under the same profile. [#1568](https://github.com/aws/aws-sdk-go-v2/pull/1568). Thanks to [Amir Szekely](https://github.com/kichik) for help reproduce this bug. - -# v1.3.4 (2022-01-14) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.3 (2022-01-07) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.2 (2021-12-02) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.1 (2021-11-19) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.3.0 (2021-11-06) - -* **Feature**: The SDK now supports configuration of FIPS and DualStack endpoints using environment variables, shared configuration, or programmatically. -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.2.5 (2021-10-21) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.2.4 (2021-10-11) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.2.3 (2021-09-17) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.2.2 (2021-08-27) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.2.1 (2021-08-19) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.2.0 (2021-08-04) - -* **Feature**: adds error handling for defered close calls -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.1.1 (2021-07-15) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.1.0 (2021-07-01) - -* **Feature**: Support for `:`, `=`, `[`, `]` being present in expression values. - -# v1.0.1 (2021-06-25) - -* **Dependency Update**: Updated to the latest SDK module versions - -# v1.0.0 (2021-05-20) - -* **Release**: The `github.com/aws/aws-sdk-go-v2/internal/ini` package is now a Go Module. -* **Dependency Update**: Updated to the latest SDK module versions - diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/CHANGELOG.md new file mode 100644 index 0000000000..e1e3c23a74 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/CHANGELOG.md @@ -0,0 +1,460 @@ +# v1.4.24 (2026-04-29) + +* **Dependency Update**: Update to smithy-go v1.25.1. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.23 (2026-04-17) + +* **Dependency Update**: Bump smithy-go to 1.25.0 to support endpointBdd trait +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.22 (2026-03-26) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.21 (2026-03-13) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.20 (2026-03-05) + +* **Bug Fix**: Read the correct auth property for SigV4A signing names. + +# v1.4.19 (2026-03-03) + +* **Bug Fix**: Modernize non codegen files with go fix +* **Dependency Update**: Bump minimum Go version to 1.24 +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.18 (2026-02-23) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.17 (2026-01-09) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.16 (2025-12-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.15 (2025-12-02) + +* **Dependency Update**: Updated to the latest SDK module versions +* **Dependency Update**: Upgrade to smithy-go v1.24.0. Notably this version of the library reduces the allocation footprint of the middleware system. We observe a ~10% reduction in allocations per SDK call with this change. + +# v1.4.14 (2025-11-19.2) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.13 (2025-11-04) + +* **Dependency Update**: Updated to the latest SDK module versions +* **Dependency Update**: Upgrade to smithy-go v1.23.2 which should convey some passive reduction of overall allocations, especially when not using the metrics system. + +# v1.4.12 (2025-10-30) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.11 (2025-10-23) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.10 (2025-10-16) + +* **Dependency Update**: Bump minimum Go version to 1.23. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.9 (2025-09-26) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.8 (2025-09-23) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.7 (2025-09-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.6 (2025-08-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.5 (2025-08-27) + +* **Dependency Update**: Update to smithy-go v1.23.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.4 (2025-08-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.3 (2025-08-11) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.2 (2025-08-04) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.1 (2025-07-30) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.0 (2025-07-28) + +* **Feature**: Add support for HTTP interceptors. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.37 (2025-07-19) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.36 (2025-06-17) + +* **Dependency Update**: Update to smithy-go v1.22.4. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.35 (2025-06-10) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.34 (2025-02-27) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.33 (2025-02-18) + +* **Bug Fix**: Bump go version to 1.22 +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.32 (2025-02-05) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.31 (2025-01-31) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.30 (2025-01-30) + +* **Bug Fix**: Do not sign Transfer-Encoding header in Sigv4[a]. Fixes a signer mismatch issue with S3 Accelerate. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.29 (2025-01-24) + +* **Dependency Update**: Updated to the latest SDK module versions +* **Dependency Update**: Upgrade to smithy-go v1.22.2. + +# v1.3.28 (2025-01-15) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.27 (2025-01-09) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.26 (2024-12-19) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.25 (2024-12-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.24 (2024-11-18) + +* **Dependency Update**: Update to smithy-go v1.22.1. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.23 (2024-11-06) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.22 (2024-10-28) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.21 (2024-10-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.20 (2024-10-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.19 (2024-10-04) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.18 (2024-09-20) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.17 (2024-09-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.16 (2024-08-15) + +* **Dependency Update**: Bump minimum Go version to 1.21. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.15 (2024-07-10.2) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.14 (2024-07-10) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.13 (2024-06-28) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.12 (2024-06-19) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.11 (2024-06-18) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.10 (2024-06-17) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.9 (2024-06-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.8 (2024-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.7 (2024-05-16) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.6 (2024-05-15) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.5 (2024-03-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.4 (2024-03-18) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.3 (2024-03-07) + +* **Bug Fix**: Remove dependency on go-cmp. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.2 (2024-02-23) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.1 (2024-02-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.3.0 (2024-02-13) + +* **Feature**: Bump minimum Go version to 1.20 per our language support policy. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.2.10 (2024-01-04) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.2.9 (2023-12-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.2.8 (2023-12-01) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.2.7 (2023-11-30) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.2.6 (2023-11-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.2.5 (2023-11-28.2) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.2.4 (2023-11-20) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.2.3 (2023-11-15) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.2.2 (2023-11-09) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.2.1 (2023-11-01) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.2.0 (2023-10-31) + +* **Feature**: **BREAKING CHANGE**: Bump minimum go version to 1.19 per the revised [go version support policy](https://aws.amazon.com/blogs/developer/aws-sdk-for-go-aligns-with-go-release-policy-on-supported-runtimes/). +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.1.6 (2023-10-12) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.1.5 (2023-10-06) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.1.4 (2023-08-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.1.3 (2023-08-18) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.1.2 (2023-08-17) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.1.1 (2023-08-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.1.0 (2023-07-31) + +* **Feature**: Adds support for smithy-modeled endpoint resolution. A new rules-based endpoint resolution will be added to the SDK which will supercede and deprecate existing endpoint resolution. Specifically, EndpointResolver will be deprecated while BaseEndpoint and EndpointResolverV2 will take its place. For more information, please see the Endpoints section in our Developer Guide. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.28 (2023-07-28) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.27 (2023-07-13) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.26 (2023-06-13) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.25 (2023-04-24) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.24 (2023-04-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.23 (2023-03-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.22 (2023-03-10) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.21 (2023-02-20) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.20 (2023-02-14) + +* No change notes available for this release. + +# v1.0.19 (2023-02-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.18 (2022-12-15) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.17 (2022-12-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.16 (2022-10-24) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.15 (2022-10-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.14 (2022-09-20) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.13 (2022-09-14) + +* **Bug Fix**: Fixes an issues where an error from an underlying SigV4 credential provider would not be surfaced from the SigV4a credential provider. Contribution by [sakthipriyan-aqfer](https://github.com/sakthipriyan-aqfer). +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.12 (2022-09-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.11 (2022-08-31) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.10 (2022-08-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.9 (2022-08-11) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.8 (2022-08-09) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.7 (2022-08-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.6 (2022-08-01) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.5 (2022-07-05) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.4 (2022-06-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.3 (2022-06-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.2 (2022-05-17) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.1 (2022-04-25) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.0 (2022-04-07) + +* **Release**: New internal v4a signing module location. + diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/ini/LICENSE.txt b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/LICENSE.txt similarity index 100% rename from vendor/github.com/aws/aws-sdk-go-v2/internal/ini/LICENSE.txt rename to vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/LICENSE.txt diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/credentials.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/credentials.go new file mode 100644 index 0000000000..3ae3a019e6 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/credentials.go @@ -0,0 +1,141 @@ +package v4a + +import ( + "context" + "crypto/ecdsa" + "fmt" + "sync" + "sync/atomic" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/internal/sdk" +) + +// Credentials is Context, ECDSA, and Optional Session Token that can be used +// to sign requests using SigV4a +type Credentials struct { + Context string + PrivateKey *ecdsa.PrivateKey + SessionToken string + + // Time the credentials will expire. + CanExpire bool + Expires time.Time +} + +// Expired returns if the credentials have expired. +func (v Credentials) Expired() bool { + if v.CanExpire { + return !v.Expires.After(sdk.NowTime()) + } + + return false +} + +// HasKeys returns if the credentials keys are set. +func (v Credentials) HasKeys() bool { + return len(v.Context) > 0 && v.PrivateKey != nil +} + +// SymmetricCredentialAdaptor wraps a SigV4 AccessKey/SecretKey provider and adapts the credentials +// to a ECDSA PrivateKey for signing with SiV4a +type SymmetricCredentialAdaptor struct { + SymmetricProvider aws.CredentialsProvider + + asymmetric atomic.Value + m sync.Mutex +} + +// Retrieve retrieves symmetric credentials from the underlying provider. +func (s *SymmetricCredentialAdaptor) Retrieve(ctx context.Context) (aws.Credentials, error) { + symCreds, err := s.retrieveFromSymmetricProvider(ctx) + if err != nil { + return aws.Credentials{}, err + } + + if asymCreds := s.getCreds(); asymCreds == nil { + return symCreds, nil + } + + s.m.Lock() + defer s.m.Unlock() + + asymCreds := s.getCreds() + if asymCreds == nil { + return symCreds, nil + } + + // if the context does not match the access key id clear it + if asymCreds.Context != symCreds.AccessKeyID { + s.asymmetric.Store((*Credentials)(nil)) + } + + return symCreds, nil +} + +// RetrievePrivateKey returns credentials suitable for SigV4a signing +func (s *SymmetricCredentialAdaptor) RetrievePrivateKey(ctx context.Context) (Credentials, error) { + if asymCreds := s.getCreds(); asymCreds != nil { + return *asymCreds, nil + } + + s.m.Lock() + defer s.m.Unlock() + + if asymCreds := s.getCreds(); asymCreds != nil { + return *asymCreds, nil + } + + symmetricCreds, err := s.retrieveFromSymmetricProvider(ctx) + if err != nil { + return Credentials{}, fmt.Errorf("failed to retrieve symmetric credentials: %v", err) + } + + privateKey, err := deriveKeyFromAccessKeyPair(symmetricCreds.AccessKeyID, symmetricCreds.SecretAccessKey) + if err != nil { + return Credentials{}, fmt.Errorf("failed to derive assymetric key from credentials") + } + + creds := Credentials{ + Context: symmetricCreds.AccessKeyID, + PrivateKey: privateKey, + SessionToken: symmetricCreds.SessionToken, + CanExpire: symmetricCreds.CanExpire, + Expires: symmetricCreds.Expires, + } + + s.asymmetric.Store(&creds) + + return creds, nil +} + +func (s *SymmetricCredentialAdaptor) getCreds() *Credentials { + v := s.asymmetric.Load() + + if v == nil { + return nil + } + + c := v.(*Credentials) + if c != nil && c.HasKeys() && !c.Expired() { + return c + } + + return nil +} + +func (s *SymmetricCredentialAdaptor) retrieveFromSymmetricProvider(ctx context.Context) (aws.Credentials, error) { + credentials, err := s.SymmetricProvider.Retrieve(ctx) + if err != nil { + return aws.Credentials{}, err + } + + return credentials, nil +} + +// CredentialsProvider is the interface for a provider to retrieve credentials +// to sign requests with. +type CredentialsProvider interface { + RetrievePrivateKey(context.Context) (Credentials, error) +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/error.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/error.go new file mode 100644 index 0000000000..380d174271 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/error.go @@ -0,0 +1,17 @@ +package v4a + +import "fmt" + +// SigningError indicates an error condition occurred while performing SigV4a signing +type SigningError struct { + Err error +} + +func (e *SigningError) Error() string { + return fmt.Sprintf("failed to sign request: %v", e.Err) +} + +// Unwrap returns the underlying error cause +func (e *SigningError) Unwrap() error { + return e.Err +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/ini/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/go_module_metadata.go similarity index 74% rename from vendor/github.com/aws/aws-sdk-go-v2/internal/ini/go_module_metadata.go rename to vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/go_module_metadata.go index 1dc2e12aa8..455cb74e1a 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/ini/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/go_module_metadata.go @@ -1,6 +1,6 @@ // Code generated by internal/repotools/cmd/updatemodulemeta DO NOT EDIT. -package ini +package v4a // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.8.6" +const goModuleVersion = "1.4.24" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/crypto/compare.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/crypto/compare.go new file mode 100644 index 0000000000..1d0f25f8c2 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/crypto/compare.go @@ -0,0 +1,30 @@ +package crypto + +import "fmt" + +// ConstantTimeByteCompare is a constant-time byte comparison of x and y. This function performs an absolute comparison +// if the two byte slices assuming they represent a big-endian number. +// +// error if len(x) != len(y) +// -1 if x < y +// 0 if x == y +// +1 if x > y +func ConstantTimeByteCompare(x, y []byte) (int, error) { + if len(x) != len(y) { + return 0, fmt.Errorf("slice lengths do not match") + } + + xLarger, yLarger := 0, 0 + + for i := 0; i < len(x); i++ { + xByte, yByte := int(x[i]), int(y[i]) + + x := ((yByte - xByte) >> 8) & 1 + y := ((xByte - yByte) >> 8) & 1 + + xLarger |= x &^ yLarger + yLarger |= y &^ xLarger + } + + return xLarger - yLarger, nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/crypto/ecc.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/crypto/ecc.go new file mode 100644 index 0000000000..758c73fcb3 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/crypto/ecc.go @@ -0,0 +1,113 @@ +package crypto + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/hmac" + "encoding/asn1" + "encoding/binary" + "fmt" + "hash" + "math" + "math/big" +) + +type ecdsaSignature struct { + R, S *big.Int +} + +// ECDSAKey takes the given elliptic curve, and private key (d) byte slice +// and returns the private ECDSA key. +func ECDSAKey(curve elliptic.Curve, d []byte) *ecdsa.PrivateKey { + return ECDSAKeyFromPoint(curve, (&big.Int{}).SetBytes(d)) +} + +// ECDSAKeyFromPoint takes the given elliptic curve and point and returns the +// private and public keypair +func ECDSAKeyFromPoint(curve elliptic.Curve, d *big.Int) *ecdsa.PrivateKey { + pX, pY := curve.ScalarBaseMult(d.Bytes()) + + privKey := &ecdsa.PrivateKey{ + PublicKey: ecdsa.PublicKey{ + Curve: curve, + X: pX, + Y: pY, + }, + D: d, + } + + return privKey +} + +// ECDSAPublicKey takes the provide curve and (x, y) coordinates and returns +// *ecdsa.PublicKey. Returns an error if the given points are not on the curve. +func ECDSAPublicKey(curve elliptic.Curve, x, y []byte) (*ecdsa.PublicKey, error) { + xPoint := (&big.Int{}).SetBytes(x) + yPoint := (&big.Int{}).SetBytes(y) + + if !curve.IsOnCurve(xPoint, yPoint) { + return nil, fmt.Errorf("point(%v, %v) is not on the given curve", xPoint.String(), yPoint.String()) + } + + return &ecdsa.PublicKey{ + Curve: curve, + X: xPoint, + Y: yPoint, + }, nil +} + +// VerifySignature takes the provided public key, hash, and asn1 encoded signature and returns +// whether the given signature is valid. +func VerifySignature(key *ecdsa.PublicKey, hash []byte, signature []byte) (bool, error) { + var ecdsaSignature ecdsaSignature + + _, err := asn1.Unmarshal(signature, &ecdsaSignature) + if err != nil { + return false, err + } + + return ecdsa.Verify(key, hash, ecdsaSignature.R, ecdsaSignature.S), nil +} + +// HMACKeyDerivation provides an implementation of a NIST-800-108 of a KDF (Key Derivation Function) in Counter Mode. +// For the purposes of this implantation HMAC is used as the PRF (Pseudorandom function), where the value of +// `r` is defined as a 4 byte counter. +func HMACKeyDerivation(hash func() hash.Hash, bitLen int, key []byte, label, context []byte) ([]byte, error) { + // verify that we won't overflow the counter + n := int64(math.Ceil((float64(bitLen) / 8) / float64(hash().Size()))) + if n > 0x7FFFFFFF { + return nil, fmt.Errorf("unable to derive key of size %d using 32-bit counter", bitLen) + } + + // verify the requested bit length is not larger then the length encoding size + if int64(bitLen) > 0x7FFFFFFF { + return nil, fmt.Errorf("bitLen is greater than 32-bits") + } + + fixedInput := bytes.NewBuffer(nil) + fixedInput.Write(label) + fixedInput.WriteByte(0x00) + fixedInput.Write(context) + if err := binary.Write(fixedInput, binary.BigEndian, int32(bitLen)); err != nil { + return nil, fmt.Errorf("failed to write bit length to fixed input string: %v", err) + } + + var output []byte + + h := hmac.New(hash, key) + + for i := int64(1); i <= n; i++ { + h.Reset() + if err := binary.Write(h, binary.BigEndian, int32(i)); err != nil { + return nil, err + } + _, err := h.Write(fixedInput.Bytes()) + if err != nil { + return nil, err + } + output = append(output, h.Sum(nil)...) + } + + return output[:bitLen/8], nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/const.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/const.go new file mode 100644 index 0000000000..89a76e2eaa --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/const.go @@ -0,0 +1,36 @@ +package v4 + +const ( + // EmptyStringSHA256 is the hex encoded sha256 value of an empty string + EmptyStringSHA256 = `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855` + + // UnsignedPayload indicates that the request payload body is unsigned + UnsignedPayload = "UNSIGNED-PAYLOAD" + + // AmzAlgorithmKey indicates the signing algorithm + AmzAlgorithmKey = "X-Amz-Algorithm" + + // AmzSecurityTokenKey indicates the security token to be used with temporary credentials + AmzSecurityTokenKey = "X-Amz-Security-Token" + + // AmzDateKey is the UTC timestamp for the request in the format YYYYMMDD'T'HHMMSS'Z' + AmzDateKey = "X-Amz-Date" + + // AmzCredentialKey is the access key ID and credential scope + AmzCredentialKey = "X-Amz-Credential" + + // AmzSignedHeadersKey is the set of headers signed for the request + AmzSignedHeadersKey = "X-Amz-SignedHeaders" + + // AmzSignatureKey is the query parameter to store the SigV4 signature + AmzSignatureKey = "X-Amz-Signature" + + // TimeFormat is the time format to be used in the X-Amz-Date header or query parameter + TimeFormat = "20060102T150405Z" + + // ShortTimeFormat is the shorten time format used in the credential scope + ShortTimeFormat = "20060102" + + // ContentSHAKey is the SHA256 of request body + ContentSHAKey = "X-Amz-Content-Sha256" +) diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/header_rules.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/header_rules.go new file mode 100644 index 0000000000..a15177e8f3 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/header_rules.go @@ -0,0 +1,82 @@ +package v4 + +import ( + sdkstrings "github.com/aws/aws-sdk-go-v2/internal/strings" +) + +// Rules houses a set of Rule needed for validation of a +// string value +type Rules []Rule + +// Rule interface allows for more flexible rules and just simply +// checks whether or not a value adheres to that Rule +type Rule interface { + IsValid(value string) bool +} + +// IsValid will iterate through all rules and see if any rules +// apply to the value and supports nested rules +func (r Rules) IsValid(value string) bool { + for _, rule := range r { + if rule.IsValid(value) { + return true + } + } + return false +} + +// MapRule generic Rule for maps +type MapRule map[string]struct{} + +// IsValid for the map Rule satisfies whether it exists in the map +func (m MapRule) IsValid(value string) bool { + _, ok := m[value] + return ok +} + +// AllowList is a generic Rule for whitelisting +type AllowList struct { + Rule +} + +// IsValid for AllowList checks if the value is within the AllowList +func (w AllowList) IsValid(value string) bool { + return w.Rule.IsValid(value) +} + +// DenyList is a generic Rule for blacklisting +type DenyList struct { + Rule +} + +// IsValid for AllowList checks if the value is within the AllowList +func (b DenyList) IsValid(value string) bool { + return !b.Rule.IsValid(value) +} + +// Patterns is a list of strings to match against +type Patterns []string + +// IsValid for Patterns checks each pattern and returns if a match has +// been found +func (p Patterns) IsValid(value string) bool { + for _, pattern := range p { + if sdkstrings.HasPrefixFold(value, pattern) { + return true + } + } + return false +} + +// InclusiveRules rules allow for rules to depend on one another +type InclusiveRules []Rule + +// IsValid will return true if all rules are true +func (r InclusiveRules) IsValid(value string) bool { + for _, rule := range r { + if !rule.IsValid(value) { + return false + } + } + return true +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/headers.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/headers.go new file mode 100644 index 0000000000..688f834742 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/headers.go @@ -0,0 +1,68 @@ +package v4 + +// IgnoredHeaders is a list of headers that are ignored during signing +var IgnoredHeaders = Rules{ + DenyList{ + MapRule{ + "Authorization": struct{}{}, + "User-Agent": struct{}{}, + "X-Amzn-Trace-Id": struct{}{}, + "Transfer-Encoding": struct{}{}, + }, + }, +} + +// RequiredSignedHeaders is a whitelist for Build canonical headers. +var RequiredSignedHeaders = Rules{ + AllowList{ + MapRule{ + "Cache-Control": struct{}{}, + "Content-Disposition": struct{}{}, + "Content-Encoding": struct{}{}, + "Content-Language": struct{}{}, + "Content-Md5": struct{}{}, + "Content-Type": struct{}{}, + "Expires": struct{}{}, + "If-Match": struct{}{}, + "If-Modified-Since": struct{}{}, + "If-None-Match": struct{}{}, + "If-Unmodified-Since": struct{}{}, + "Range": struct{}{}, + "X-Amz-Acl": struct{}{}, + "X-Amz-Copy-Source": struct{}{}, + "X-Amz-Copy-Source-If-Match": struct{}{}, + "X-Amz-Copy-Source-If-Modified-Since": struct{}{}, + "X-Amz-Copy-Source-If-None-Match": struct{}{}, + "X-Amz-Copy-Source-If-Unmodified-Since": struct{}{}, + "X-Amz-Copy-Source-Range": struct{}{}, + "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": struct{}{}, + "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key": struct{}{}, + "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5": struct{}{}, + "X-Amz-Grant-Full-control": struct{}{}, + "X-Amz-Grant-Read": struct{}{}, + "X-Amz-Grant-Read-Acp": struct{}{}, + "X-Amz-Grant-Write": struct{}{}, + "X-Amz-Grant-Write-Acp": struct{}{}, + "X-Amz-Metadata-Directive": struct{}{}, + "X-Amz-Mfa": struct{}{}, + "X-Amz-Request-Payer": struct{}{}, + "X-Amz-Server-Side-Encryption": struct{}{}, + "X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": struct{}{}, + "X-Amz-Server-Side-Encryption-Customer-Algorithm": struct{}{}, + "X-Amz-Server-Side-Encryption-Customer-Key": struct{}{}, + "X-Amz-Server-Side-Encryption-Customer-Key-Md5": struct{}{}, + "X-Amz-Storage-Class": struct{}{}, + "X-Amz-Website-Redirect-Location": struct{}{}, + "X-Amz-Content-Sha256": struct{}{}, + "X-Amz-Tagging": struct{}{}, + }, + }, + Patterns{"X-Amz-Meta-"}, +} + +// AllowedQueryHoisting is a whitelist for Build query headers. The boolean value +// represents whether or not it is a pattern. +var AllowedQueryHoisting = InclusiveRules{ + DenyList{RequiredSignedHeaders}, + Patterns{"X-Amz-"}, +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/hmac.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/hmac.go new file mode 100644 index 0000000000..e7fa7a1b1e --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/hmac.go @@ -0,0 +1,13 @@ +package v4 + +import ( + "crypto/hmac" + "crypto/sha256" +) + +// HMACSHA256 computes a HMAC-SHA256 of data given the provided key. +func HMACSHA256(key []byte, data []byte) []byte { + hash := hmac.New(sha256.New, key) + hash.Write(data) + return hash.Sum(nil) +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/host.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/host.go new file mode 100644 index 0000000000..bf93659a43 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/host.go @@ -0,0 +1,75 @@ +package v4 + +import ( + "net/http" + "strings" +) + +// SanitizeHostForHeader removes default port from host and updates request.Host +func SanitizeHostForHeader(r *http.Request) { + host := getHost(r) + port := portOnly(host) + if port != "" && isDefaultPort(r.URL.Scheme, port) { + r.Host = stripPort(host) + } +} + +// Returns host from request +func getHost(r *http.Request) string { + if r.Host != "" { + return r.Host + } + + return r.URL.Host +} + +// Hostname returns u.Host, without any port number. +// +// If Host is an IPv6 literal with a port number, Hostname returns the +// IPv6 literal without the square brackets. IPv6 literals may include +// a zone identifier. +// +// Copied from the Go 1.8 standard library (net/url) +func stripPort(hostport string) string { + colon := strings.IndexByte(hostport, ':') + if colon == -1 { + return hostport + } + if i := strings.IndexByte(hostport, ']'); i != -1 { + return strings.TrimPrefix(hostport[:i], "[") + } + return hostport[:colon] +} + +// Port returns the port part of u.Host, without the leading colon. +// If u.Host doesn't contain a port, Port returns an empty string. +// +// Copied from the Go 1.8 standard library (net/url) +func portOnly(hostport string) string { + colon := strings.IndexByte(hostport, ':') + if colon == -1 { + return "" + } + if i := strings.Index(hostport, "]:"); i != -1 { + return hostport[i+len("]:"):] + } + if strings.Contains(hostport, "]") { + return "" + } + return hostport[colon+len(":"):] +} + +// Returns true if the specified URI is using the standard port +// (i.e. port 80 for HTTP URIs or 443 for HTTPS URIs) +func isDefaultPort(scheme, port string) bool { + if port == "" { + return true + } + + lowerCaseScheme := strings.ToLower(scheme) + if (lowerCaseScheme == "http" && port == "80") || (lowerCaseScheme == "https" && port == "443") { + return true + } + + return false +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/time.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/time.go new file mode 100644 index 0000000000..1de06a765d --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/time.go @@ -0,0 +1,36 @@ +package v4 + +import "time" + +// SigningTime provides a wrapper around a time.Time which provides cached values for SigV4 signing. +type SigningTime struct { + time.Time + timeFormat string + shortTimeFormat string +} + +// NewSigningTime creates a new SigningTime given a time.Time +func NewSigningTime(t time.Time) SigningTime { + return SigningTime{ + Time: t, + } +} + +// TimeFormat provides a time formatted in the X-Amz-Date format. +func (m *SigningTime) TimeFormat() string { + return m.format(&m.timeFormat, TimeFormat) +} + +// ShortTimeFormat provides a time formatted of 20060102. +func (m *SigningTime) ShortTimeFormat() string { + return m.format(&m.shortTimeFormat, ShortTimeFormat) +} + +func (m *SigningTime) format(target *string, format string) string { + if len(*target) > 0 { + return *target + } + v := m.Time.Format(format) + *target = v + return v +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/util.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/util.go new file mode 100644 index 0000000000..741019b5f9 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4/util.go @@ -0,0 +1,64 @@ +package v4 + +import ( + "net/url" + "strings" +) + +const doubleSpace = " " + +// StripExcessSpaces will rewrite the passed in slice's string values to not +// contain muliple side-by-side spaces. +func StripExcessSpaces(str string) string { + var j, k, l, m, spaces int + // Trim trailing spaces + for j = len(str) - 1; j >= 0 && str[j] == ' '; j-- { + } + + // Trim leading spaces + for k = 0; k < j && str[k] == ' '; k++ { + } + str = str[k : j+1] + + // Strip multiple spaces. + j = strings.Index(str, doubleSpace) + if j < 0 { + return str + } + + buf := []byte(str) + for k, m, l = j, j, len(buf); k < l; k++ { + if buf[k] == ' ' { + if spaces == 0 { + // First space. + buf[m] = buf[k] + m++ + } + spaces++ + } else { + // End of multiple spaces. + spaces = 0 + buf[m] = buf[k] + m++ + } + } + + return string(buf[:m]) +} + +// GetURIPath returns the escaped URI component from the provided URL +func GetURIPath(u *url.URL) string { + var uri string + + if len(u.Opaque) > 0 { + uri = "/" + strings.Join(strings.Split(u.Opaque, "/")[3:], "/") + } else { + uri = u.EscapedPath() + } + + if len(uri) == 0 { + uri = "/" + } + + return uri +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/middleware.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/middleware.go new file mode 100644 index 0000000000..64b8b4e330 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/middleware.go @@ -0,0 +1,118 @@ +package v4a + +import ( + "context" + "fmt" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4" + internalauth "github.com/aws/aws-sdk-go-v2/internal/auth" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" + "net/http" + "time" +) + +// HTTPSigner is SigV4a HTTP signer implementation +type HTTPSigner interface { + SignHTTP(ctx context.Context, credentials Credentials, r *http.Request, payloadHash string, service string, regionSet []string, signingTime time.Time, optfns ...func(*SignerOptions)) error +} + +// SignHTTPRequestMiddlewareOptions is the middleware options for constructing a SignHTTPRequestMiddleware. +type SignHTTPRequestMiddlewareOptions struct { + Credentials CredentialsProvider + Signer HTTPSigner + LogSigning bool +} + +// SignHTTPRequestMiddleware is a middleware for signing an HTTP request using SigV4a. +type SignHTTPRequestMiddleware struct { + credentials CredentialsProvider + signer HTTPSigner + logSigning bool +} + +// NewSignHTTPRequestMiddleware constructs a SignHTTPRequestMiddleware using the given SignHTTPRequestMiddlewareOptions. +func NewSignHTTPRequestMiddleware(options SignHTTPRequestMiddlewareOptions) *SignHTTPRequestMiddleware { + return &SignHTTPRequestMiddleware{ + credentials: options.Credentials, + signer: options.Signer, + logSigning: options.LogSigning, + } +} + +// ID the middleware identifier. +func (s *SignHTTPRequestMiddleware) ID() string { + return "Signing" +} + +// HandleFinalize signs an HTTP request using SigV4a. +func (s *SignHTTPRequestMiddleware) HandleFinalize( + ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler, +) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + if !hasCredentialProvider(s.credentials) { + return next.HandleFinalize(ctx, in) + } + + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, fmt.Errorf("unexpected request middleware type %T", in.Request) + } + + signingName, signingRegion := awsmiddleware.GetSigningName(ctx), awsmiddleware.GetSigningRegion(ctx) + payloadHash := v4.GetPayloadHash(ctx) + if len(payloadHash) == 0 { + return out, metadata, &SigningError{Err: fmt.Errorf("computed payload hash missing from context")} + } + + credentials, err := s.credentials.RetrievePrivateKey(ctx) + if err != nil { + return out, metadata, &SigningError{Err: fmt.Errorf("failed to retrieve credentials: %w", err)} + } + + signerOptions := []func(o *SignerOptions){ + func(o *SignerOptions) { + o.Logger = middleware.GetLogger(ctx) + o.LogSigning = s.logSigning + }, + } + + // existing DisableURIPathEscaping is equivalent in purpose + // to authentication scheme property DisableDoubleEncoding + disableDoubleEncoding, overridden := internalauth.GetDisableDoubleEncoding(ctx) + if overridden { + signerOptions = append(signerOptions, func(o *SignerOptions) { + o.DisableURIPathEscaping = disableDoubleEncoding + }) + } + + err = s.signer.SignHTTP(ctx, credentials, req.Request, payloadHash, signingName, []string{signingRegion}, time.Now().UTC(), signerOptions...) + if err != nil { + return out, metadata, &SigningError{Err: fmt.Errorf("failed to sign http request, %w", err)} + } + + return next.HandleFinalize(ctx, in) +} + +func hasCredentialProvider(p CredentialsProvider) bool { + if p == nil { + return false + } + + return true +} + +// RegisterSigningMiddleware registers the SigV4a signing middleware to the stack. If a signing middleware is already +// present, this provided middleware will be swapped. Otherwise the middleware will be added at the tail of the +// finalize step. +func RegisterSigningMiddleware(stack *middleware.Stack, signingMiddleware *SignHTTPRequestMiddleware) (err error) { + const signedID = "Signing" + _, present := stack.Finalize.Get(signedID) + if present { + _, err = stack.Finalize.Swap(signedID, signingMiddleware) + } else { + err = stack.Finalize.Add(signingMiddleware, middleware.After) + } + return err +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/presign_middleware.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/presign_middleware.go new file mode 100644 index 0000000000..951fc415d5 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/presign_middleware.go @@ -0,0 +1,117 @@ +package v4a + +import ( + "context" + "fmt" + "net/http" + "time" + + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4" + "github.com/aws/aws-sdk-go-v2/internal/sdk" + "github.com/aws/smithy-go/middleware" + smithyHTTP "github.com/aws/smithy-go/transport/http" +) + +// HTTPPresigner is an interface to a SigV4a signer that can sign create a +// presigned URL for a HTTP requests. +type HTTPPresigner interface { + PresignHTTP( + ctx context.Context, credentials Credentials, r *http.Request, + payloadHash string, service string, regionSet []string, signingTime time.Time, + optFns ...func(*SignerOptions), + ) (url string, signedHeader http.Header, err error) +} + +// PresignHTTPRequestMiddlewareOptions is the options for the PresignHTTPRequestMiddleware middleware. +type PresignHTTPRequestMiddlewareOptions struct { + CredentialsProvider CredentialsProvider + Presigner HTTPPresigner + LogSigning bool +} + +// PresignHTTPRequestMiddleware provides the Finalize middleware for creating a +// presigned URL for an HTTP request. +// +// Will short circuit the middleware stack and not forward onto the next +// Finalize handler. +type PresignHTTPRequestMiddleware struct { + credentialsProvider CredentialsProvider + presigner HTTPPresigner + logSigning bool +} + +// NewPresignHTTPRequestMiddleware returns a new PresignHTTPRequestMiddleware +// initialized with the presigner. +func NewPresignHTTPRequestMiddleware(options PresignHTTPRequestMiddlewareOptions) *PresignHTTPRequestMiddleware { + return &PresignHTTPRequestMiddleware{ + credentialsProvider: options.CredentialsProvider, + presigner: options.Presigner, + logSigning: options.LogSigning, + } +} + +// ID provides the middleware ID. +func (*PresignHTTPRequestMiddleware) ID() string { return "PresignHTTPRequest" } + +// HandleFinalize will take the provided input and create a presigned url for +// the http request using the SigV4 presign authentication scheme. +func (s *PresignHTTPRequestMiddleware) HandleFinalize( + ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler, +) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + req, ok := in.Request.(*smithyHTTP.Request) + if !ok { + return out, metadata, &SigningError{ + Err: fmt.Errorf("unexpected request middleware type %T", in.Request), + } + } + + httpReq := req.Build(ctx) + if !hasCredentialProvider(s.credentialsProvider) { + out.Result = &v4.PresignedHTTPRequest{ + URL: httpReq.URL.String(), + Method: httpReq.Method, + SignedHeader: http.Header{}, + } + + return out, metadata, nil + } + + signingName := awsmiddleware.GetSigningName(ctx) + signingRegion := awsmiddleware.GetSigningRegion(ctx) + payloadHash := v4.GetPayloadHash(ctx) + if len(payloadHash) == 0 { + return out, metadata, &SigningError{ + Err: fmt.Errorf("computed payload hash missing from context"), + } + } + + credentials, err := s.credentialsProvider.RetrievePrivateKey(ctx) + if err != nil { + return out, metadata, &SigningError{ + Err: fmt.Errorf("failed to retrieve credentials: %w", err), + } + } + + u, h, err := s.presigner.PresignHTTP(ctx, credentials, + httpReq, payloadHash, signingName, []string{signingRegion}, sdk.NowTime(), + func(o *SignerOptions) { + o.Logger = middleware.GetLogger(ctx) + o.LogSigning = s.logSigning + }) + if err != nil { + return out, metadata, &SigningError{ + Err: fmt.Errorf("failed to sign http request, %w", err), + } + } + + out.Result = &v4.PresignedHTTPRequest{ + URL: u, + Method: httpReq.Method, + SignedHeader: h, + } + + return out, metadata, nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/smithy.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/smithy.go new file mode 100644 index 0000000000..c3b689bace --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/smithy.go @@ -0,0 +1,92 @@ +package v4a + +import ( + "context" + "fmt" + "time" + + internalcontext "github.com/aws/aws-sdk-go-v2/internal/context" + + v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4" + "github.com/aws/aws-sdk-go-v2/internal/sdk" + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/auth" + "github.com/aws/smithy-go/logging" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// CredentialsAdapter adapts v4a.Credentials to smithy auth.Identity. +type CredentialsAdapter struct { + Credentials Credentials +} + +var _ auth.Identity = (*CredentialsAdapter)(nil) + +// Expiration returns the time of expiration for the credentials. +func (v *CredentialsAdapter) Expiration() time.Time { + return v.Credentials.Expires +} + +// CredentialsProviderAdapter adapts v4a.CredentialsProvider to +// auth.IdentityResolver. +type CredentialsProviderAdapter struct { + Provider CredentialsProvider +} + +var _ (auth.IdentityResolver) = (*CredentialsProviderAdapter)(nil) + +// GetIdentity retrieves v4a credentials using the underlying provider. +func (v *CredentialsProviderAdapter) GetIdentity(ctx context.Context, _ smithy.Properties) ( + auth.Identity, error, +) { + creds, err := v.Provider.RetrievePrivateKey(ctx) + if err != nil { + return nil, fmt.Errorf("get credentials: %w", err) + } + + return &CredentialsAdapter{Credentials: creds}, nil +} + +// SignerAdapter adapts v4a.HTTPSigner to smithy http.Signer. +type SignerAdapter struct { + Signer HTTPSigner + Logger logging.Logger + LogSigning bool +} + +var _ (smithyhttp.Signer) = (*SignerAdapter)(nil) + +// SignRequest signs the request with the provided identity. +func (v *SignerAdapter) SignRequest(ctx context.Context, r *smithyhttp.Request, identity auth.Identity, props smithy.Properties) error { + ca, ok := identity.(*CredentialsAdapter) + if !ok { + return fmt.Errorf("unexpected identity type: %T", identity) + } + + name, ok := smithyhttp.GetSigV4ASigningName(&props) + if !ok { + return fmt.Errorf("sigv4a signing name is required") + } + + regions, ok := smithyhttp.GetSigV4ASigningRegions(&props) + if !ok { + return fmt.Errorf("sigv4a signing region is required") + } + + hash := v4.GetPayloadHash(ctx) + signingTime := sdk.NowTime() + if skew := internalcontext.GetAttemptSkewContext(ctx); skew != 0 { + signingTime.Add(skew) + } + err := v.Signer.SignHTTP(ctx, ca.Credentials, r.Request, hash, name, regions, signingTime, func(o *SignerOptions) { + o.DisableURIPathEscaping, _ = smithyhttp.GetDisableDoubleEncoding(&props) + + o.Logger = v.Logger + o.LogSigning = v.LogSigning + }) + if err != nil { + return fmt.Errorf("sign http: %w", err) + } + + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/v4a.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/v4a.go new file mode 100644 index 0000000000..f226bcdced --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/v4a.go @@ -0,0 +1,520 @@ +package v4a + +import ( + "bytes" + "context" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "fmt" + "hash" + "math/big" + "net/http" + "net/textproto" + "net/url" + "sort" + "strconv" + "strings" + "time" + + signerCrypto "github.com/aws/aws-sdk-go-v2/internal/v4a/internal/crypto" + v4Internal "github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4" + "github.com/aws/smithy-go/encoding/httpbinding" + "github.com/aws/smithy-go/logging" +) + +const ( + // AmzRegionSetKey represents the region set header used for sigv4a + AmzRegionSetKey = "X-Amz-Region-Set" + amzAlgorithmKey = v4Internal.AmzAlgorithmKey + amzSecurityTokenKey = v4Internal.AmzSecurityTokenKey + amzDateKey = v4Internal.AmzDateKey + amzCredentialKey = v4Internal.AmzCredentialKey + amzSignedHeadersKey = v4Internal.AmzSignedHeadersKey + authorizationHeader = "Authorization" + + signingAlgorithm = "AWS4-ECDSA-P256-SHA256" + + timeFormat = "20060102T150405Z" + shortTimeFormat = "20060102" + + // EmptyStringSHA256 is a hex encoded SHA-256 hash of an empty string + EmptyStringSHA256 = v4Internal.EmptyStringSHA256 + + // Version of signing v4a + Version = "SigV4A" +) + +var ( + p256 elliptic.Curve + nMinusTwoP256 *big.Int + + one = new(big.Int).SetInt64(1) +) + +func init() { + // Ensure the elliptic curve parameters are initialized on package import rather then on first usage + p256 = elliptic.P256() + + nMinusTwoP256 = new(big.Int).SetBytes(p256.Params().N.Bytes()) + nMinusTwoP256 = nMinusTwoP256.Sub(nMinusTwoP256, new(big.Int).SetInt64(2)) +} + +// SignerOptions is the SigV4a signing options for constructing a Signer. +type SignerOptions struct { + Logger logging.Logger + LogSigning bool + + // Disables the Signer's moving HTTP header key/value pairs from the HTTP + // request header to the request's query string. This is most commonly used + // with pre-signed requests preventing headers from being added to the + // request's query string. + DisableHeaderHoisting bool + + // Disables the automatic escaping of the URI path of the request for the + // siganture's canonical string's path. For services that do not need additional + // escaping then use this to disable the signer escaping the path. + // + // S3 is an example of a service that does not need additional escaping. + // + // http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html + DisableURIPathEscaping bool +} + +// Signer is a SigV4a HTTP signing implementation +type Signer struct { + options SignerOptions +} + +// NewSigner constructs a SigV4a Signer. +func NewSigner(optFns ...func(*SignerOptions)) *Signer { + options := SignerOptions{} + + for _, fn := range optFns { + fn(&options) + } + + return &Signer{options: options} +} + +// deriveKeyFromAccessKeyPair derives a NIST P-256 PrivateKey from the given +// IAM AccessKey and SecretKey pair. +// +// Based on FIPS.186-4 Appendix B.4.2 +func deriveKeyFromAccessKeyPair(accessKey, secretKey string) (*ecdsa.PrivateKey, error) { + params := p256.Params() + bitLen := params.BitSize // Testing random candidates does not require an additional 64 bits + counter := 0x01 + + buffer := make([]byte, 1+len(accessKey)) // 1 byte counter + len(accessKey) + kdfContext := bytes.NewBuffer(buffer) + + inputKey := append([]byte("AWS4A"), []byte(secretKey)...) + + d := new(big.Int) + for { + kdfContext.Reset() + kdfContext.WriteString(accessKey) + kdfContext.WriteByte(byte(counter)) + + key, err := signerCrypto.HMACKeyDerivation(sha256.New, bitLen, inputKey, []byte(signingAlgorithm), kdfContext.Bytes()) + if err != nil { + return nil, err + } + + // Check key first before calling SetBytes if key key is in fact a valid candidate. + // This ensures the byte slice is the correct length (32-bytes) to compare in constant-time + cmp, err := signerCrypto.ConstantTimeByteCompare(key, nMinusTwoP256.Bytes()) + if err != nil { + return nil, err + } + if cmp == -1 { + d.SetBytes(key) + break + } + + counter++ + if counter > 0xFF { + return nil, fmt.Errorf("exhausted single byte external counter") + } + } + d = d.Add(d, one) + + priv := new(ecdsa.PrivateKey) + priv.PublicKey.Curve = p256 + priv.D = d + priv.PublicKey.X, priv.PublicKey.Y = p256.ScalarBaseMult(d.Bytes()) + + return priv, nil +} + +type httpSigner struct { + Request *http.Request + ServiceName string + RegionSet []string + Time time.Time + Credentials Credentials + IsPreSign bool + + Logger logging.Logger + Debug bool + + // PayloadHash is the hex encoded SHA-256 hash of the request payload + // If len(PayloadHash) == 0 the signer will attempt to send the request + // as an unsigned payload. Note: Unsigned payloads only work for a subset of services. + PayloadHash string + + DisableHeaderHoisting bool + DisableURIPathEscaping bool +} + +// SignHTTP takes the provided http.Request, payload hash, service, regionSet, and time and signs using SigV4a. +// The passed in request will be modified in place. +func (s *Signer) SignHTTP(ctx context.Context, credentials Credentials, r *http.Request, payloadHash string, service string, regionSet []string, signingTime time.Time, optFns ...func(*SignerOptions)) error { + options := s.options + for _, fn := range optFns { + fn(&options) + } + + signer := &httpSigner{ + Request: r, + PayloadHash: payloadHash, + ServiceName: service, + RegionSet: regionSet, + Credentials: credentials, + Time: signingTime.UTC(), + DisableHeaderHoisting: options.DisableHeaderHoisting, + DisableURIPathEscaping: options.DisableURIPathEscaping, + } + + signedRequest, err := signer.Build() + if err != nil { + return err + } + + logHTTPSigningInfo(ctx, options, signedRequest) + + return nil +} + +// PresignHTTP takes the provided http.Request, payload hash, service, regionSet, and time and presigns using SigV4a +// Returns the presigned URL along with the headers that were signed with the request. +// +// PresignHTTP will not set the expires time of the presigned request +// automatically. To specify the expire duration for a request add the +// "X-Amz-Expires" query parameter on the request with the value as the +// duration in seconds the presigned URL should be considered valid for. This +// parameter is not used by all AWS services, and is most notable used by +// Amazon S3 APIs. +func (s *Signer) PresignHTTP(ctx context.Context, credentials Credentials, r *http.Request, payloadHash string, service string, regionSet []string, signingTime time.Time, optFns ...func(*SignerOptions)) (signedURI string, signedHeaders http.Header, err error) { + options := s.options + for _, fn := range optFns { + fn(&options) + } + + signer := &httpSigner{ + Request: r, + PayloadHash: payloadHash, + ServiceName: service, + RegionSet: regionSet, + Credentials: credentials, + Time: signingTime.UTC(), + IsPreSign: true, + DisableHeaderHoisting: options.DisableHeaderHoisting, + DisableURIPathEscaping: options.DisableURIPathEscaping, + } + + signedRequest, err := signer.Build() + if err != nil { + return "", nil, err + } + + logHTTPSigningInfo(ctx, options, signedRequest) + + signedHeaders = make(http.Header) + + // For the signed headers we canonicalize the header keys in the returned map. + // This avoids situations where can standard library double headers like host header. For example the standard + // library will set the Host header, even if it is present in lower-case form. + for k, v := range signedRequest.SignedHeaders { + key := textproto.CanonicalMIMEHeaderKey(k) + signedHeaders[key] = append(signedHeaders[key], v...) + } + + return signedRequest.Request.URL.String(), signedHeaders, nil +} + +func (s *httpSigner) setRequiredSigningFields(headers http.Header, query url.Values) { + amzDate := s.Time.Format(timeFormat) + + if s.IsPreSign { + query.Set(AmzRegionSetKey, strings.Join(s.RegionSet, ",")) + query.Set(amzDateKey, amzDate) + query.Set(amzAlgorithmKey, signingAlgorithm) + if len(s.Credentials.SessionToken) > 0 { + query.Set(amzSecurityTokenKey, s.Credentials.SessionToken) + } + return + } + + headers.Set(AmzRegionSetKey, strings.Join(s.RegionSet, ",")) + headers.Set(amzDateKey, amzDate) + if len(s.Credentials.SessionToken) > 0 { + headers.Set(amzSecurityTokenKey, s.Credentials.SessionToken) + } +} + +func (s *httpSigner) Build() (signedRequest, error) { + req := s.Request + + query := req.URL.Query() + headers := req.Header + + s.setRequiredSigningFields(headers, query) + + // Sort Each Query Key's Values + for key := range query { + sort.Strings(query[key]) + } + + v4Internal.SanitizeHostForHeader(req) + + credentialScope := s.buildCredentialScope() + credentialStr := s.Credentials.Context + "/" + credentialScope + if s.IsPreSign { + query.Set(amzCredentialKey, credentialStr) + } + + unsignedHeaders := headers + if s.IsPreSign && !s.DisableHeaderHoisting { + urlValues := url.Values{} + urlValues, unsignedHeaders = buildQuery(v4Internal.AllowedQueryHoisting, unsignedHeaders) + for k := range urlValues { + query[k] = urlValues[k] + } + } + + host := req.URL.Host + if len(req.Host) > 0 { + host = req.Host + } + + signedHeaders, signedHeadersStr, canonicalHeaderStr := s.buildCanonicalHeaders(host, v4Internal.IgnoredHeaders, unsignedHeaders, s.Request.ContentLength) + + if s.IsPreSign { + query.Set(amzSignedHeadersKey, signedHeadersStr) + } + + rawQuery := strings.Replace(query.Encode(), "+", "%20", -1) + + canonicalURI := v4Internal.GetURIPath(req.URL) + if !s.DisableURIPathEscaping { + canonicalURI = httpbinding.EscapePath(canonicalURI, false) + } + + canonicalString := s.buildCanonicalString( + req.Method, + canonicalURI, + rawQuery, + signedHeadersStr, + canonicalHeaderStr, + ) + + strToSign := s.buildStringToSign(credentialScope, canonicalString) + signingSignature, err := s.buildSignature(strToSign) + if err != nil { + return signedRequest{}, err + } + + if s.IsPreSign { + rawQuery += "&X-Amz-Signature=" + signingSignature + } else { + headers[authorizationHeader] = append(headers[authorizationHeader][:0], buildAuthorizationHeader(credentialStr, signedHeadersStr, signingSignature)) + } + + req.URL.RawQuery = rawQuery + + return signedRequest{ + Request: req, + SignedHeaders: signedHeaders, + CanonicalString: canonicalString, + StringToSign: strToSign, + PreSigned: s.IsPreSign, + }, nil +} + +func buildAuthorizationHeader(credentialStr, signedHeadersStr, signingSignature string) string { + const credential = "Credential=" + const signedHeaders = "SignedHeaders=" + const signature = "Signature=" + const commaSpace = ", " + + var parts strings.Builder + parts.Grow(len(signingAlgorithm) + 1 + + len(credential) + len(credentialStr) + len(commaSpace) + + len(signedHeaders) + len(signedHeadersStr) + len(commaSpace) + + len(signature) + len(signingSignature), + ) + parts.WriteString(signingAlgorithm) + parts.WriteRune(' ') + parts.WriteString(credential) + parts.WriteString(credentialStr) + parts.WriteString(commaSpace) + parts.WriteString(signedHeaders) + parts.WriteString(signedHeadersStr) + parts.WriteString(commaSpace) + parts.WriteString(signature) + parts.WriteString(signingSignature) + return parts.String() +} + +func (s *httpSigner) buildCredentialScope() string { + return strings.Join([]string{ + s.Time.Format(shortTimeFormat), + s.ServiceName, + "aws4_request", + }, "/") + +} + +func buildQuery(r v4Internal.Rule, header http.Header) (url.Values, http.Header) { + query := url.Values{} + unsignedHeaders := http.Header{} + for k, h := range header { + if r.IsValid(k) { + query[k] = h + } else { + unsignedHeaders[k] = h + } + } + + return query, unsignedHeaders +} + +func (s *httpSigner) buildCanonicalHeaders(host string, rule v4Internal.Rule, header http.Header, length int64) (signed http.Header, signedHeaders, canonicalHeadersStr string) { + signed = make(http.Header) + + var headers []string + const hostHeader = "host" + headers = append(headers, hostHeader) + signed[hostHeader] = append(signed[hostHeader], host) + + if length > 0 { + const contentLengthHeader = "content-length" + headers = append(headers, contentLengthHeader) + signed[contentLengthHeader] = append(signed[contentLengthHeader], strconv.FormatInt(length, 10)) + } + + for k, v := range header { + if !rule.IsValid(k) { + continue // ignored header + } + + lowerCaseKey := strings.ToLower(k) + if _, ok := signed[lowerCaseKey]; ok { + // include additional values + signed[lowerCaseKey] = append(signed[lowerCaseKey], v...) + continue + } + + headers = append(headers, lowerCaseKey) + signed[lowerCaseKey] = v + } + sort.Strings(headers) + + signedHeaders = strings.Join(headers, ";") + + var canonicalHeaders strings.Builder + n := len(headers) + const colon = ':' + for i := range n { + if headers[i] == hostHeader { + canonicalHeaders.WriteString(hostHeader) + canonicalHeaders.WriteRune(colon) + canonicalHeaders.WriteString(v4Internal.StripExcessSpaces(host)) + } else { + canonicalHeaders.WriteString(headers[i]) + canonicalHeaders.WriteRune(colon) + // Trim out leading, trailing, and dedup inner spaces from signed header values. + values := signed[headers[i]] + for j, v := range values { + cleanedValue := strings.TrimSpace(v4Internal.StripExcessSpaces(v)) + canonicalHeaders.WriteString(cleanedValue) + if j < len(values)-1 { + canonicalHeaders.WriteRune(',') + } + } + } + canonicalHeaders.WriteRune('\n') + } + canonicalHeadersStr = canonicalHeaders.String() + + return signed, signedHeaders, canonicalHeadersStr +} + +func (s *httpSigner) buildCanonicalString(method, uri, query, signedHeaders, canonicalHeaders string) string { + return strings.Join([]string{ + method, + uri, + query, + canonicalHeaders, + signedHeaders, + s.PayloadHash, + }, "\n") +} + +func (s *httpSigner) buildStringToSign(credentialScope, canonicalRequestString string) string { + return strings.Join([]string{ + signingAlgorithm, + s.Time.Format(timeFormat), + credentialScope, + hex.EncodeToString(makeHash(sha256.New(), []byte(canonicalRequestString))), + }, "\n") +} + +func makeHash(hash hash.Hash, b []byte) []byte { + hash.Reset() + hash.Write(b) + return hash.Sum(nil) +} + +func (s *httpSigner) buildSignature(strToSign string) (string, error) { + sig, err := s.Credentials.PrivateKey.Sign(rand.Reader, makeHash(sha256.New(), []byte(strToSign)), crypto.SHA256) + if err != nil { + return "", err + } + return hex.EncodeToString(sig), nil +} + +const logSignInfoMsg = `Request Signature: +---[ CANONICAL STRING ]----------------------------- +%s +---[ STRING TO SIGN ]-------------------------------- +%s%s +-----------------------------------------------------` +const logSignedURLMsg = ` +---[ SIGNED URL ]------------------------------------ +%s` + +func logHTTPSigningInfo(ctx context.Context, options SignerOptions, r signedRequest) { + if !options.LogSigning { + return + } + signedURLMsg := "" + if r.PreSigned { + signedURLMsg = fmt.Sprintf(logSignedURLMsg, r.Request.URL.String()) + } + logger := logging.WithContext(ctx, options.Logger) + logger.Logf(logging.Debug, logSignInfoMsg, r.CanonicalString, r.StringToSign, signedURLMsg) +} + +type signedRequest struct { + Request *http.Request + SignedHeaders http.Header + CanonicalString string + StringToSign string + PreSigned bool +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding/CHANGELOG.md index 497d372304..cf6c5e0911 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding/CHANGELOG.md @@ -1,3 +1,11 @@ +# v1.13.9 (2026-04-29) + +* **Dependency Update**: Update to smithy-go v1.25.1. + +# v1.13.8 (2026-04-17) + +* **Dependency Update**: Bump smithy-go to 1.25.0 to support endpointBdd trait + # v1.13.7 (2026-03-13) * **Bug Fix**: Replace usages of the old ioutil/ package throughout the SDK. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding/go_module_metadata.go index 5679a2b2b1..e145070706 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding/go_module_metadata.go @@ -3,4 +3,4 @@ package acceptencoding // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.13.7" +const goModuleVersion = "1.13.9" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md index 7c5e13816e..96adad5261 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md @@ -1,3 +1,13 @@ +# v1.13.23 (2026-04-29) + +* **Dependency Update**: Update to smithy-go v1.25.1. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.13.22 (2026-04-17) + +* **Dependency Update**: Bump smithy-go to 1.25.0 to support endpointBdd trait +* **Dependency Update**: Updated to the latest SDK module versions + # v1.13.21 (2026-03-26) * **Dependency Update**: Updated to the latest SDK module versions diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go index 456855e885..5737e9c0c1 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go @@ -3,4 +3,4 @@ package presignedurl // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.13.21" +const goModuleVersion = "1.13.23" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/route53/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/route53/CHANGELOG.md index 9811ed448a..9fff90f62c 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/route53/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/route53/CHANGELOG.md @@ -1,3 +1,13 @@ +# v1.62.7 (2026-04-29) + +* **Dependency Update**: Update to smithy-go v1.25.1. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.62.6 (2026-04-17) + +* **Dependency Update**: Bump smithy-go to 1.25.0 to support endpointBdd trait +* **Dependency Update**: Updated to the latest SDK module versions + # v1.62.5 (2026-03-26) * **Bug Fix**: Fix a bug where a recorded clock skew could persist on the client even if the client and server clock ended up realigning. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/route53/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/route53/go_module_metadata.go index 3910c2b234..0ed5798175 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/route53/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/route53/go_module_metadata.go @@ -3,4 +3,4 @@ package route53 // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.62.5" +const goModuleVersion = "1.62.7" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/CHANGELOG.md index d93bf5e7cc..253e035967 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/CHANGELOG.md @@ -1,3 +1,13 @@ +# v1.0.11 (2026-04-29) + +* **Dependency Update**: Update to smithy-go v1.25.1. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.0.10 (2026-04-17) + +* **Dependency Update**: Bump smithy-go to 1.25.0 to support endpointBdd trait +* **Dependency Update**: Updated to the latest SDK module versions + # v1.0.9 (2026-03-26) * **Bug Fix**: Fix a bug where a recorded clock skew could persist on the client even if the client and server clock ended up realigning. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/go_module_metadata.go index c922e7adfb..eba7ad7774 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/go_module_metadata.go @@ -3,4 +3,4 @@ package signin // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.0.9" +const goModuleVersion = "1.0.11" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/CHANGELOG.md index 6dd182becc..26c80a2c23 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/CHANGELOG.md @@ -1,3 +1,13 @@ +# v1.30.17 (2026-04-29) + +* **Dependency Update**: Update to smithy-go v1.25.1. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.30.16 (2026-04-17) + +* **Dependency Update**: Bump smithy-go to 1.25.0 to support endpointBdd trait +* **Dependency Update**: Updated to the latest SDK module versions + # v1.30.15 (2026-04-02) * No change notes available for this release. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/go_module_metadata.go index 08ae9cb32b..9d12dd55bc 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/go_module_metadata.go @@ -3,4 +3,4 @@ package sso // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.30.15" +const goModuleVersion = "1.30.17" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/CHANGELOG.md index b894e050f4..e645209405 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/CHANGELOG.md @@ -1,3 +1,13 @@ +# v1.35.21 (2026-04-29) + +* **Dependency Update**: Update to smithy-go v1.25.1. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.35.20 (2026-04-17) + +* **Dependency Update**: Bump smithy-go to 1.25.0 to support endpointBdd trait +* **Dependency Update**: Updated to the latest SDK module versions + # v1.35.19 (2026-04-02) * No change notes available for this release. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/go_module_metadata.go index 96cd1638a0..af00268dfc 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/go_module_metadata.go @@ -3,4 +3,4 @@ package ssooidc // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.35.19" +const goModuleVersion = "1.35.21" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/CHANGELOG.md index c009086381..199f7a79ce 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/CHANGELOG.md @@ -1,3 +1,14 @@ +# v1.42.1 (2026-04-29) + +* **Dependency Update**: Update to smithy-go v1.25.1. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.42.0 (2026-04-17) + +* **Feature**: The STS client now supports configuring SigV4a through the auth scheme preference setting. SigV4a uses asymmetric cryptography, enabling customers using long-term IAM credentials to continue making STS API calls even when a region is isolated from the partition leader. +* **Dependency Update**: Bump smithy-go to 1.25.0 to support endpointBdd trait +* **Dependency Update**: Updated to the latest SDK module versions + # v1.41.10 (2026-03-26) * **Bug Fix**: Fix a bug where a recorded clock skew could persist on the client even if the client and server clock ended up realigning. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_client.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_client.go index c0c6af3a15..958c83c1a8 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_client.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_client.go @@ -16,6 +16,7 @@ import ( internalauth "github.com/aws/aws-sdk-go-v2/internal/auth" internalauthsmithy "github.com/aws/aws-sdk-go-v2/internal/auth/smithy" internalConfig "github.com/aws/aws-sdk-go-v2/internal/configsources" + "github.com/aws/aws-sdk-go-v2/internal/v4a" acceptencodingcust "github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding" presignedurlcust "github.com/aws/aws-sdk-go-v2/service/internal/presigned-url" smithy "github.com/aws/smithy-go" @@ -207,6 +208,8 @@ func New(options Options, optFns ...func(*Options)) *Client { resolveEndpointResolverV2(&options) + resolveHTTPSignerV4a(&options) + resolveTracerProvider(&options) resolveMeterProvider(&options) @@ -381,6 +384,11 @@ func resolveAuthSchemes(options *Options) { Logger: options.Logger, LogSigning: options.ClientLogMode.IsSigning(), }), + internalauth.NewHTTPAuthScheme("aws.auth#sigv4a", &v4a.SignerAdapter{ + Signer: options.httpSignerV4a, + Logger: options.Logger, + LogSigning: options.ClientLogMode.IsSigning(), + }), } } } @@ -758,6 +766,26 @@ func resolveUseFIPSEndpoint(cfg aws.Config, o *Options) error { return nil } +type httpSignerV4a interface { + SignHTTP(ctx context.Context, credentials v4a.Credentials, r *http.Request, payloadHash, + service string, regionSet []string, signingTime time.Time, + optFns ...func(*v4a.SignerOptions)) error +} + +func resolveHTTPSignerV4a(o *Options) { + if o.httpSignerV4a != nil { + return + } + o.httpSignerV4a = newDefaultV4aSigner(*o) +} + +func newDefaultV4aSigner(o Options) *v4a.Signer { + return v4a.NewSigner(func(so *v4a.SignerOptions) { + so.Logger = o.Logger + so.LogSigning = o.ClientLogMode.IsSigning() + }) +} + func initializeTimeOffsetResolver(c *Client) { c.timeOffset = new(atomic.Int64) } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/auth.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/auth.go index 4db5a51f93..71c5db38b7 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/auth.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/auth.go @@ -149,6 +149,16 @@ func serviceAuthOptions(params *AuthResolverParameters) []*smithyauth.Option { return props }(), }, + + { + SchemeID: smithyauth.SchemeIDSigV4A, + SignerProperties: func() smithy.Properties { + var props smithy.Properties + smithyhttp.SetSigV4ASigningName(&props, "sts") + smithyhttp.SetSigV4ASigningRegions(&props, []string{params.Region}) + return props + }(), + }, } } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/generated.json b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/generated.json index b5556cbfbf..2fc7b400f7 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/generated.json +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/generated.json @@ -3,6 +3,7 @@ "github.com/aws/aws-sdk-go-v2": "v1.4.0", "github.com/aws/aws-sdk-go-v2/internal/configsources": "v0.0.0-00010101000000-000000000000", "github.com/aws/aws-sdk-go-v2/internal/endpoints/v2": "v2.0.0-00010101000000-000000000000", + "github.com/aws/aws-sdk-go-v2/internal/v4a": "v0.0.0-00010101000000-000000000000", "github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding": "v1.0.5", "github.com/aws/aws-sdk-go-v2/service/internal/presigned-url": "v1.0.7", "github.com/aws/smithy-go": "v1.4.0" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/go_module_metadata.go index 317746f0fd..bdd6a15d8f 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/go_module_metadata.go @@ -3,4 +3,4 @@ package sts // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.41.10" +const goModuleVersion = "1.42.1" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/options.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/options.go index c66e69a8d9..a9f2361fd3 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/options.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/options.go @@ -4,9 +4,11 @@ package sts import ( "context" + "fmt" "github.com/aws/aws-sdk-go-v2/aws" awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" internalauthsmithy "github.com/aws/aws-sdk-go-v2/internal/auth/smithy" + "github.com/aws/aws-sdk-go-v2/internal/v4a" smithyauth "github.com/aws/smithy-go/auth" "github.com/aws/smithy-go/logging" "github.com/aws/smithy-go/metrics" @@ -107,6 +109,9 @@ type Options struct { // The client tracer provider. TracerProvider tracing.TracerProvider + // Signature Version 4a (SigV4a) Signer + httpSignerV4a httpSignerV4a + // The initial DefaultsMode used when the client options were constructed. If the // DefaultsMode was set to aws.DefaultsModeAuto this will store what the resolved // value was at that point in time. @@ -146,6 +151,9 @@ func (o Options) GetIdentityResolver(schemeID string) smithyauth.IdentityResolve if schemeID == "aws.auth#sigv4" { return getSigV4IdentityResolver(o) } + if schemeID == "aws.auth#sigv4a" { + return getSigV4AIdentityResolver(o) + } if schemeID == "smithy.api#noAuth" { return &smithyauth.AnonymousIdentityResolver{} } @@ -231,6 +239,46 @@ func WithSigV4SigningRegion(region string) func(*Options) { } } +func getSigV4AIdentityResolver(o Options) smithyauth.IdentityResolver { + if o.Credentials != nil { + return &v4a.CredentialsProviderAdapter{ + Provider: &v4a.SymmetricCredentialAdaptor{ + SymmetricProvider: o.Credentials, + }, + } + } + return nil +} + +// WithSigV4ASigningRegions applies an override to the authentication workflow to +// use the given signing region set for SigV4A-authenticated operations. +// +// This is an advanced setting. The value here is FINAL, taking precedence over +// the resolved signing region set from both auth scheme resolution and endpoint +// resolution. +func WithSigV4ASigningRegions(regions []string) func(*Options) { + fn := func(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, + ) { + rscheme := getResolvedAuthScheme(ctx) + if rscheme == nil { + return out, metadata, fmt.Errorf("no resolved auth scheme") + } + + smithyhttp.SetSigV4ASigningRegions(&rscheme.SignerProperties, regions) + return next.HandleFinalize(ctx, in) + } + return func(o *Options) { + o.APIOptions = append(o.APIOptions, func(s *middleware.Stack) error { + return s.Finalize.Insert( + middleware.FinalizeMiddlewareFunc("withSigV4ASigningRegions", fn), + "Signing", + middleware.Before, + ) + }) + } +} + func ignoreAnonymousAuth(options *Options) { if aws.IsCredentialsProvider(options.Credentials, (*aws.AnonymousCredentials)(nil)) { options.Credentials = nil diff --git a/vendor/github.com/aws/smithy-go/CHANGELOG.md b/vendor/github.com/aws/smithy-go/CHANGELOG.md index 2db174e021..b9cd114ed1 100644 --- a/vendor/github.com/aws/smithy-go/CHANGELOG.md +++ b/vendor/github.com/aws/smithy-go/CHANGELOG.md @@ -1,3 +1,12 @@ +# Release (2026-04-23) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go`: v1.25.1 + * **Bug Fix**: Fixed a memory leak in the LRU cache implementation used by some AWS services. + # Release (2026-04-15) ## General Highlights diff --git a/vendor/github.com/aws/smithy-go/go_module_metadata.go b/vendor/github.com/aws/smithy-go/go_module_metadata.go index 35938d4072..a1e928754a 100644 --- a/vendor/github.com/aws/smithy-go/go_module_metadata.go +++ b/vendor/github.com/aws/smithy-go/go_module_metadata.go @@ -3,4 +3,4 @@ package smithy // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.25.0" +const goModuleVersion = "1.25.1" diff --git a/vendor/github.com/cockroachdb/errors/errbase/encode.go b/vendor/github.com/cockroachdb/errors/errbase/encode.go index 418cc50e81..61ea0bb397 100644 --- a/vendor/github.com/cockroachdb/errors/errbase/encode.go +++ b/vendor/github.com/cockroachdb/errors/errbase/encode.go @@ -305,6 +305,30 @@ func GetTypeMark(err error) errorspb.ErrorTypeMark { return errorspb.ErrorTypeMark{FamilyName: familyName, Extension: extension} } +// EqualTypeMark checks whether `GetTypeMark(e1).Equals(GetTypeMark(e2))`. It +// is written to be be optimized for the case where neither error has +// serialized type information. +func EqualTypeMark(e1, e2 error) bool { + slowPath := func(err error) bool { + switch err.(type) { + case *opaqueLeaf: + return true + case *opaqueLeafCauses: + return true + case *opaqueWrapper: + return true + case TypeKeyMarker: + return true + } + return false + } + if slowPath(e1) || slowPath(e2) { + return GetTypeMark(e1).Equals(GetTypeMark(e2)) + } + + return reflect.TypeOf(e1) == reflect.TypeOf(e2) +} + // RegisterLeafEncoder can be used to register new leaf error types to // the library. Registered types will be encoded using their own // Go type when an error is encoded. Wrappers that have not been @@ -385,9 +409,7 @@ func RegisterWrapperEncoder(theType TypeKey, encoder WrapperEncoder) { // Note: if the error type has been migrated from a previous location // or a different type, ensure that RegisterTypeMigration() was called // prior to RegisterWrapperEncoder(). -func RegisterWrapperEncoderWithMessageType( - theType TypeKey, encoder WrapperEncoderWithMessageType, -) { +func RegisterWrapperEncoderWithMessageType(theType TypeKey, encoder WrapperEncoderWithMessageType) { if encoder == nil { delete(encoders, theType) } else { diff --git a/vendor/github.com/cockroachdb/errors/markers/markers.go b/vendor/github.com/cockroachdb/errors/markers/markers.go index 3f8179499f..50a16e8b59 100644 --- a/vendor/github.com/cockroachdb/errors/markers/markers.go +++ b/vendor/github.com/cockroachdb/errors/markers/markers.go @@ -68,29 +68,42 @@ func Is(err, reference error) bool { } } - if err == nil { - // Err is nil and reference is non-nil, so it cannot match. We - // want to short-circuit the loop below in this case, otherwise - // we're paying the expense of getMark() without need. - return false - } - - // Not directly equal. Try harder, using error marks. We don't do - // this during the loop above as it may be more expensive. - // - // Note: there is a more effective recursive algorithm that ensures - // that any pair of string only gets compared once. Should the - // following code become a performance bottleneck, that algorithm - // can be considered instead. - refMark := getMark(reference) - for c := err; c != nil; c = errbase.UnwrapOnce(c) { - if equalMarks(getMark(c), refMark) { + for errNext := err; errNext != nil; errNext = errbase.UnwrapOnce(errNext) { + if isMarkEqual(errNext, reference) { return true } } + return false } +func isMarkEqual(err, reference error) bool { + _, errIsMark := err.(*withMark) + _, refIsMark := reference.(*withMark) + if errIsMark || refIsMark { + // If either error is a mark, use the more general + // equalMarks() function. + return equalMarks(getMark(err), getMark(reference)) + } + + m1 := err + m2 := reference + for m1 != nil && m2 != nil { + if !errbase.EqualTypeMark(m1, m2) { + return false + } + m1 = errbase.UnwrapOnce(m1) + m2 = errbase.UnwrapOnce(m2) + } + + // The two chains have different lengths, so they cannot be equal. + if m1 != nil || m2 != nil { + return false + } + + return safeGetErrMsg(err) == safeGetErrMsg(reference) +} + func tryDelegateToIsMethod(err, reference error) bool { if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(reference) { return true @@ -150,62 +163,9 @@ func If(err error, pred func(err error) (interface{}, bool)) (interface{}, bool) // package location or a different type, ensure that // RegisterTypeMigration() was called prior to IsAny(). func IsAny(err error, references ...error) bool { - if err == nil { - for _, refErr := range references { - if refErr == nil { - return true - } - } - // The mark-based comparison below will never match anything if - // the error is nil, so don't bother with computing the marks in - // that case. This avoids the computational expense of computing - // the reference marks upfront. - return false - } - - // First try using direct reference comparison. - for c := err; c != nil; c = errbase.UnwrapOnce(c) { - for _, refErr := range references { - if refErr == nil { - continue - } - isComparable := reflect.TypeOf(refErr).Comparable() - if isComparable && c == refErr { - return true - } - // Compatibility with std go errors: if the error object itself - // implements Is(), try to use that. - if tryDelegateToIsMethod(c, refErr) { - return true - } - } - - // Recursively try multi-error causes, if applicable. - for _, me := range errbase.UnwrapMulti(c) { - if IsAny(me, references...) { - return true - } - } - } - - // Try harder with marks. - // Note: there is a more effective recursive algorithm that ensures - // that any pair of string only gets compared once. Should this - // become a performance bottleneck, that algorithm can be considered - // instead. - refMarks := make([]errorMark, 0, len(references)) - for _, refErr := range references { - if refErr == nil { - continue - } - refMarks = append(refMarks, getMark(refErr)) - } - for c := err; c != nil; c = errbase.UnwrapOnce(c) { - errMark := getMark(c) - for _, refMark := range refMarks { - if equalMarks(errMark, refMark) { - return true - } + for _, reference := range references { + if Is(err, reference) { + return true } } return false @@ -221,6 +181,9 @@ func equalMarks(m1, m2 errorMark) bool { if m1.msg != m2.msg { return false } + if len(m1.types) != len(m2.types) { + return false + } for i, t := range m1.types { if !t.Equals(m2.types[i]) { return false @@ -234,7 +197,10 @@ func getMark(err error) errorMark { if m, ok := err.(*withMark); ok { return m.mark } - m := errorMark{msg: safeGetErrMsg(err), types: []errorspb.ErrorTypeMark{errbase.GetTypeMark(err)}} + m := errorMark{ + msg: safeGetErrMsg(err), + types: []errorspb.ErrorTypeMark{errbase.GetTypeMark(err)}, + } for c := errbase.UnwrapOnce(err); c != nil; c = errbase.UnwrapOnce(c) { m.types = append(m.types, errbase.GetTypeMark(c)) } diff --git a/vendor/github.com/cockroachdb/errors/report/report.go b/vendor/github.com/cockroachdb/errors/report/report.go index 817583d124..055868f461 100644 --- a/vendor/github.com/cockroachdb/errors/report/report.go +++ b/vendor/github.com/cockroachdb/errors/report/report.go @@ -365,8 +365,11 @@ var redactedMarker = redact.RedactableString(redact.RedactedMarker()).StripMarke func ReportError(err error) (eventID string) { event, extraDetails := BuildSentryReport(err) + if event.Contexts == nil { + event.Contexts = make(map[string]sentry.Context) + } for extraKey, extraValue := range extraDetails { - event.Extra[extraKey] = extraValue + event.Contexts[extraKey] = sentry.Context{"value": extraValue} } // Avoid leaking the machine's hostname by injecting the literal "". diff --git a/vendor/github.com/deckarep/golang-set/v2/README.md b/vendor/github.com/deckarep/golang-set/v2/README.md index e4713680a7..2c6dd6b9ad 100644 --- a/vendor/github.com/deckarep/golang-set/v2/README.md +++ b/vendor/github.com/deckarep/golang-set/v2/README.md @@ -9,6 +9,13 @@ The missing `generic` set collection for the Go language. Until Go has sets bui ## Psst * Hi there, 👋! Do you use or have interest in the [Zig programming language](https://ziglang.org/) created by Andrew Kelley? If so, the golang-set project has a new sibling project: [ziglang-set](https://github.com/deckarep/ziglang-set)! Come check it out! +## Update 4/20/2026 +* Packaged version: `2.9.0` contains the following: + * Address an edge case where a deadlock may occur with `Each` should a panic occur: [PR #164](https://github.com/deckarep/golang-set/pull/164) + * BSON Marshaling support for Marshaling and Unmarshaling: [PR #142](https://github.com/deckarep/golang-set/pull/142) + * New PopN method: [PR #166](https://github.com/deckarep/golang-set/pull/166) + * Reduced allocations by using capacity arg for some methods that may allocate: [PR #171](https://github.com/deckarep/golang-set/pull/171) + ## Update 3/14/2025 * Packaged version: `2.8.0` introduces support for true iterators for Go 1.23+. Please see [issue #141](https://github.com/deckarep/golang-set/issues/141) for further details on the implications of how iterations work between older Go versions vs newer Go versions. Additionally, this diff --git a/vendor/github.com/deckarep/golang-set/v2/set.go b/vendor/github.com/deckarep/golang-set/v2/set.go index e9409aa87c..92b67bff92 100644 --- a/vendor/github.com/deckarep/golang-set/v2/set.go +++ b/vendor/github.com/deckarep/golang-set/v2/set.go @@ -35,6 +35,8 @@ SOFTWARE. // that can enforce mutual exclusion through other means. package mapset +import "go.mongodb.org/mongo-driver/bson/bsontype" + // Set is the primary interface provided by the mapset package. It // represents an unordered set of data and a large number of // operations that can be applied to that set. @@ -189,6 +191,12 @@ type Set[T comparable] interface { // Pop removes and returns an arbitrary item from the set. Pop() (T, bool) + // PopN removes and returns up to n arbitrary items from the set. + // It returns a slice of the removed items and the actual number of items removed. + // If the set is empty or n is less than or equal to 0s, it returns an empty slice and 0. + // If n is greater than the set's size, all items are + PopN(n int) ([]T, int) + // ToSlice returns the members of the set as a slice. ToSlice() []T @@ -196,8 +204,15 @@ type Set[T comparable] interface { MarshalJSON() ([]byte, error) // UnmarshalJSON will unmarshal a JSON-based byte slice into a full Set datastructure. - // For this to work, set subtypes must implemented the Marshal/Unmarshal interface. + // For this to work, set subtypes must implement the Marshal/Unmarshal interface. UnmarshalJSON(b []byte) error + + // MarshalBSONValue will marshal the set into a BSON-based representation. + MarshalBSONValue() (bsontype.Type, []byte, error) + + // UnmarshalBSONValue will unmarshal a BSON-based byte slice into a full Set datastructure. + // For this to work, set subtypes must implement the Marshal/Unmarshal interface. + UnmarshalBSONValue(bt bsontype.Type, b []byte) error } // NewSet creates and returns a new set with the given elements. diff --git a/vendor/github.com/deckarep/golang-set/v2/threadsafe.go b/vendor/github.com/deckarep/golang-set/v2/threadsafe.go index 664fc6115e..835797ea6b 100644 --- a/vendor/github.com/deckarep/golang-set/v2/threadsafe.go +++ b/vendor/github.com/deckarep/golang-set/v2/threadsafe.go @@ -25,7 +25,11 @@ SOFTWARE. package mapset -import "sync" +import ( + "sync" + + "go.mongodb.org/mongo-driver/bson/bsontype" +) type threadSafeSet[T comparable] struct { sync.RWMutex @@ -208,12 +212,12 @@ func (t *threadSafeSet[T]) Cardinality() int { func (t *threadSafeSet[T]) Each(cb func(T) bool) { t.RLock() + defer t.RUnlock() for elem := range *t.uss { if cb(elem) { break } } - t.RUnlock() } func (t *threadSafeSet[T]) Iter() <-chan T { @@ -285,9 +289,16 @@ func (t *threadSafeSet[T]) Pop() (T, bool) { return t.uss.Pop() } +func (t *threadSafeSet[T]) PopN(n int) ([]T, int) { + t.Lock() + defer t.Unlock() + return t.uss.PopN(n) +} + func (t *threadSafeSet[T]) ToSlice() []T { - keys := make([]T, 0, t.Cardinality()) t.RLock() + l := len(*t.uss) + keys := make([]T, 0, l) for elem := range *t.uss { keys = append(keys, elem) } @@ -304,9 +315,25 @@ func (t *threadSafeSet[T]) MarshalJSON() ([]byte, error) { } func (t *threadSafeSet[T]) UnmarshalJSON(p []byte) error { - t.RLock() + t.Lock() err := t.uss.UnmarshalJSON(p) + t.Unlock() + + return err +} + +func (t *threadSafeSet[T]) MarshalBSONValue() (bsontype.Type, []byte, error) { + t.RLock() + bt, b, err := t.uss.MarshalBSONValue() t.RUnlock() + return bt, b, err +} + +func (t *threadSafeSet[T]) UnmarshalBSONValue(bt bsontype.Type, p []byte) error { + t.Lock() + err := t.uss.UnmarshalBSONValue(bt, p) + t.Unlock() + return err } diff --git a/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go b/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go index c95d32b408..708c025433 100644 --- a/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go +++ b/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go @@ -29,6 +29,9 @@ import ( "encoding/json" "fmt" "strings" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsontype" ) type threadUnsafeSet[T comparable] map[T]struct{} @@ -138,13 +141,13 @@ func (s *threadUnsafeSet[T]) contains(v T) (ok bool) { func (s *threadUnsafeSet[T]) Difference(other Set[T]) Set[T] { o := other.(*threadUnsafeSet[T]) - diff := newThreadUnsafeSet[T]() + diff := make(threadUnsafeSet[T], s.Cardinality()) for elem := range *s { if !o.contains(elem) { diff.add(elem) } } - return diff + return &diff } func (s *threadUnsafeSet[T]) Each(cb func(T) bool) { @@ -172,22 +175,24 @@ func (s *threadUnsafeSet[T]) Equal(other Set[T]) bool { func (s *threadUnsafeSet[T]) Intersect(other Set[T]) Set[T] { o := other.(*threadUnsafeSet[T]) - intersection := newThreadUnsafeSet[T]() + var intersection threadUnsafeSet[T] // loop over smaller set if s.Cardinality() < other.Cardinality() { + intersection = make(threadUnsafeSet[T], s.Cardinality()) for elem := range *s { if o.contains(elem) { intersection.add(elem) } } } else { + intersection = make(threadUnsafeSet[T], o.Cardinality()) for elem := range *o { if s.contains(elem) { intersection.add(elem) } } } - return intersection + return &intersection } func (s *threadUnsafeSet[T]) IsEmpty() bool { @@ -259,6 +264,27 @@ func (s *threadUnsafeSet[T]) Pop() (v T, ok bool) { return v, false } +func (s *threadUnsafeSet[T]) PopN(n int) (items []T, count int) { + if n <= 0 || len(*s) == 0 { + return make([]T, 0), 0 + } + sn := s.Cardinality() + if n > sn { + n = sn + } + + items = make([]T, 0, sn) + for item := range *s { + if count >= n { + break + } + delete(*s, item) + items = append(items, item) + count++ + } + return items, count +} + func (s threadUnsafeSet[T]) Remove(v T) { delete(s, v) } @@ -281,7 +307,9 @@ func (s threadUnsafeSet[T]) String() string { func (s *threadUnsafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { o := other.(*threadUnsafeSet[T]) - sd := newThreadUnsafeSet[T]() + // maximum number of elements is the sum of s and o cardinalities (when s and o are disjoint) + n := s.Cardinality() + o.Cardinality() + sd := make(threadUnsafeSet[T], n) for elem := range *s { if !o.contains(elem) { sd.add(elem) @@ -292,7 +320,7 @@ func (s *threadUnsafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { sd.add(elem) } } - return sd + return &sd } func (s threadUnsafeSet[T]) ToSlice() []T { @@ -307,10 +335,8 @@ func (s threadUnsafeSet[T]) ToSlice() []T { func (s threadUnsafeSet[T]) Union(other Set[T]) Set[T] { o := other.(*threadUnsafeSet[T]) - n := s.Cardinality() - if o.Cardinality() > n { - n = o.Cardinality() - } + // maximum number of elements is the sum of s and o cardinalities (when s and o are disjoint) + n := s.Cardinality() + o.Cardinality() unionedSet := make(threadUnsafeSet[T], n) for elem := range s { @@ -350,3 +376,24 @@ func (s *threadUnsafeSet[T]) UnmarshalJSON(b []byte) error { return nil } + +// MarshalBSON creates a BSON array from the set. +func (s threadUnsafeSet[T]) MarshalBSONValue() (bsontype.Type, []byte, error) { + return bson.MarshalValue(s.ToSlice()) +} + +// UnmarshalBSON recreates a set from a BSON array. +func (s threadUnsafeSet[T]) UnmarshalBSONValue(bt bsontype.Type, b []byte) error { + if bt != bson.TypeArray { + return fmt.Errorf("must use BSON Array to unmarshal Set") + } + + var i []T + err := bson.UnmarshalValue(bt, b, &i) + if err != nil { + return err + } + s.Append(i...) + + return nil +} diff --git a/vendor/github.com/dlclark/regexp2/README.md b/vendor/github.com/dlclark/regexp2/README.md index 9cbc1d8d0a..3d56cf3eb6 100644 --- a/vendor/github.com/dlclark/regexp2/README.md +++ b/vendor/github.com/dlclark/regexp2/README.md @@ -69,7 +69,7 @@ The internals of `regexp2` always operate on `[]rune` so `Index` and `Length` da | --- | --- | --- | | Catastrophic backtracking possible | no, constant execution time guarantees | yes, if your pattern is at risk you can use the `re.MatchTimeout` field | | Python-style capture groups `(?Pre)` | yes | no (yes in RE2 compat mode) | -| .NET-style capture groups `(?re)` or `(?'name're)` | no | yes | +| .NET-style capture groups `(?re)` or `(?'name're)` | yes | yes | | comments `(?#comment)` | no | yes | | branch numbering reset `(?\|a\|b)` | no | no | | possessive match `(?>re)` | no | yes | diff --git a/vendor/github.com/dlclark/regexp2/runner.go b/vendor/github.com/dlclark/regexp2/runner.go index 56759f1474..4bb0dfa1ab 100644 --- a/vendor/github.com/dlclark/regexp2/runner.go +++ b/vendor/github.com/dlclark/regexp2/runner.go @@ -313,12 +313,9 @@ func (r *runner) execute() error { } } else { // The inner expression found an empty match, so we'll go directly to 'back2' if we - // backtrack. In this case, we need to push something on the stack, since back2 pops. - // However, in the case of ()+? or similar, this empty match may be legitimate, so push the text - // position associated with that empty match. - r.stackPush(oldMarkPos) - - r.trackPushNeg1(r.stackPeek()) // Save old mark + // backtrack. Don't touch the grouping stack here; instead, record the old mark and + // a flag indicating that backtracking doesn't need to pop a grouping stack frame. + r.trackPushNeg2(oldMarkPos, 0) } r.advance(1) continue @@ -334,18 +331,22 @@ func (r *runner) execute() error { r.trackPopN(2) pos := r.trackPeekN(1) - r.trackPushNeg1(r.trackPeek()) // Save old mark - r.stackPush(pos) // Make new mark - r.textto(pos) // Recall position - r.goTo(r.operand(0)) // Loop + r.trackPushNeg2(r.trackPeek(), 1) // Save old mark, note that we pushed a new mark + r.stackPush(pos) // Make new mark + r.textto(pos) // Recall position + r.goTo(r.operand(0)) // Loop continue case syntax.Lazybranchmark | syntax.Back2: // The lazy loop has failed. We'll do a true backtrack and // start over before the lazy loop. - r.stackPop() - r.trackPop() - r.stackPush(r.trackPeek()) // Recall old mark + r.trackPopN(2) + oldMark := r.trackPeek() + needsPop := r.trackPeekN(1) + if needsPop != 0 { + r.stackPop() + } + r.stackPush(oldMark) // Recall old mark break case syntax.Setcount: diff --git a/vendor/github.com/fsnotify/fsnotify/.cirrus.yml b/vendor/github.com/fsnotify/fsnotify/.cirrus.yml deleted file mode 100644 index 7f257e99ac..0000000000 --- a/vendor/github.com/fsnotify/fsnotify/.cirrus.yml +++ /dev/null @@ -1,14 +0,0 @@ -freebsd_task: - name: 'FreeBSD' - freebsd_instance: - image_family: freebsd-14-2 - install_script: - - pkg update -f - - pkg install -y go - test_script: - # run tests as user "cirrus" instead of root - - pw useradd cirrus -m - - chown -R cirrus:cirrus . - - FSNOTIFY_BUFFER=4096 sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race ./... - - sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race ./... - - FSNOTIFY_DEBUG=1 sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race -v ./... diff --git a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md index 6468d2cf40..3027f3c67a 100644 --- a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md +++ b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md @@ -1,5 +1,54 @@ # Changelog +1.10.1 2026-05-04 +----------------- + +### Changes and fixes + +- inotify: don't remove sibling watches sharing a path prefix ([#754]) + +- inotify, windows: don't rename sibling watches sharing a path prefix + ([#755]) + + +[#754]: https://github.com/fsnotify/fsnotify/pull/754 +[#755]: https://github.com/fsnotify/fsnotify/pull/755 + + +1.10.0 2026-04-30 +----------------- +This version of fsnotify needs Go 1.23. + +### Changes and fixes + +- inotify: improve initialization error message ([#731]) + +- inotify: send Rename event if recursive watch is renamed ([#696]) + +- inotify: avoid copying event buffers when reading names ([#741]) + +- kqueue: skip dangling symlinks (ENOENT) in watchDirectoryFiles, so a + bad entry no longer aborts Watcher.Add for the whole directory ([#748]) + +- kqueue: drop watches directly in Close() to fix a file descriptor leak + when recycling watchers ([#740]) + +- windows: fix nil pointer dereference in remWatch ([#736]) + +- windows: lock watch field updates against concurrent WatchList to fix + a race introduced in v1.9.0 ([#709], [#749]) + + +[#696]: https://github.com/fsnotify/fsnotify/pull/696 +[#709]: https://github.com/fsnotify/fsnotify/pull/709 +[#731]: https://github.com/fsnotify/fsnotify/pull/731 +[#736]: https://github.com/fsnotify/fsnotify/pull/736 +[#740]: https://github.com/fsnotify/fsnotify/pull/740 +[#741]: https://github.com/fsnotify/fsnotify/pull/741 +[#748]: https://github.com/fsnotify/fsnotify/pull/748 +[#749]: https://github.com/fsnotify/fsnotify/pull/749 + + 1.9.0 2024-04-04 ---------------- diff --git a/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md b/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md index 4cc40fa597..cd0ee612da 100644 --- a/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md +++ b/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md @@ -77,6 +77,8 @@ End-of-line escapes with `\` are not supported. debug [yes/no] # Enable/disable FSNOTIFY_DEBUG (tests are run in parallel by default, so -parallel=1 is probably a good idea). + state # Print internal state to stderr (exact output differs + # per backend). print [any strings] # Print text to stdout; for debugging. touch path diff --git a/vendor/github.com/fsnotify/fsnotify/README.md b/vendor/github.com/fsnotify/fsnotify/README.md index 1f4eb583d5..2e56ef4c9a 100644 --- a/vendor/github.com/fsnotify/fsnotify/README.md +++ b/vendor/github.com/fsnotify/fsnotify/README.md @@ -1,7 +1,7 @@ fsnotify is a Go library to provide cross-platform filesystem notifications on Windows, Linux, macOS, BSD, and illumos. -Go 1.17 or newer is required; the full documentation is at +Go 1.23 or newer is required; the full documentation is at https://pkg.go.dev/github.com/fsnotify/fsnotify --- @@ -12,7 +12,7 @@ Platform support: | :-------------------- | :--------- | :------------------------------------------------------------------------ | | inotify | Linux | Supported | | kqueue | BSD, macOS | Supported | -| ReadDirectoryChangesW | Windows | Supported | +| ReadDirectoryChangesW | Windows | Supported ([excluding `Chmod` operations][#487]) | | FEN | illumos | Supported | | fanotify | Linux 5.9+ | [Not yet](https://github.com/fsnotify/fsnotify/issues/114) | | FSEvents | macOS | [Needs support in x/sys/unix][fsevents] | @@ -22,6 +22,7 @@ Platform support: Linux and illumos should include Android and Solaris, but these are currently untested. +[#487]: https://github.com/fsnotify/fsnotify/issues/487 [fsevents]: https://github.com/fsnotify/fsnotify/issues/11#issuecomment-1279133120 [usn]: https://github.com/fsnotify/fsnotify/issues/53#issuecomment-1279829847 @@ -126,7 +127,7 @@ settings* until we have a native FSEvents implementation (see [#11]). ### Watching a file doesn't work well Watching individual files (rather than directories) is generally not recommended as many programs (especially editors) update files atomically: it will write to -a temporary file which is then moved to to destination, overwriting the original +a temporary file which is then moved to a destination, overwriting the original (or some variant thereof). The watcher on the original file is now lost, as that no longer exists. @@ -151,26 +152,57 @@ This is the event that inotify sends, so not much can be changed about this. The `fs.inotify.max_user_watches` sysctl variable specifies the upper limit for the number of watches per user, and `fs.inotify.max_user_instances` specifies the maximum number of inotify instances per user. Every Watcher you create is an -"instance", and every path you add is a "watch". +"instance", and every path you add is a "watch". Reaching the limit will result +in a "no space left on device" or "too many open files" error. These are also exposed in `/proc` as `/proc/sys/fs/inotify/max_user_watches` and -`/proc/sys/fs/inotify/max_user_instances` +`/proc/sys/fs/inotify/max_user_instances`. The default values differ per distro +and available memory. To increase them you can use `sysctl` or write the value to proc file: - # The default values on Linux 5.18 - sysctl fs.inotify.max_user_watches=124983 - sysctl fs.inotify.max_user_instances=128 + sysctl fs.inotify.max_user_watches=200000 + sysctl fs.inotify.max_user_instances=256 To make the changes persist on reboot edit `/etc/sysctl.conf` or `/usr/lib/sysctl.d/50-default.conf` (details differ per Linux distro; check your distro's documentation): - fs.inotify.max_user_watches=124983 - fs.inotify.max_user_instances=128 + fs.inotify.max_user_watches=200000 + fs.inotify.max_user_instances=256 + +### Windows +Recursive watching is not currently enabled through fsnotify's public API +(see the FAQ "Are subdirectories watched?" above). The notes below +describe Windows backend behavior observed when recursive watching is +enabled internally (for example, in fsnotify's own tests). They are kept +here as a reference for maintainers and contributors who encounter the +behavior, since the recursive code path still exists in the backend. + +When recursive watching is enabled and you watch a directory, you may +receive a `Write` event for an intermediate directory whenever a child +entry inside it is created, renamed, or removed. For example, with a +recursive watch on `/a` and a new file `/a/b/c`, you will receive +`Create /a/b/c` and may also receive `Write /a/b`. + +This happens because, on NTFS-backed volumes, modifying the entries of a +directory updates that directory's last-write time, and the Windows +backend requests `FILE_NOTIFY_CHANGE_LAST_WRITE` to support `Write` events +on files. The same `Write` filter therefore picks up the directory's +metadata update. + +kqueue has the same "directory `Write` = directory contents changed" +semantics, so portable code that treats `Write` on a directory as +"something inside it changed" works on Windows and BSD/macOS, but not on +Linux (inotify uses `Write` only for file-content changes). If you only +care about file content, filter out `Write` events whose path refers to a +directory. + +Whether the directory `Write` is actually delivered alongside the child +events is not guaranteed: it depends on `ReadDirectoryChangesW` buffering, +NTFS metadata update timing, and event coalescing, none of which fsnotify +controls. -Reaching the limit will result in a "no space left on device" or "too many open -files" error. ### kqueue (macOS, all BSD systems) kqueue requires opening a file descriptor for every file that's being watched; diff --git a/vendor/github.com/fsnotify/fsnotify/backend_fen.go b/vendor/github.com/fsnotify/fsnotify/backend_fen.go index 57fc692848..e43c6d088c 100644 --- a/vendor/github.com/fsnotify/fsnotify/backend_fen.go +++ b/vendor/github.com/fsnotify/fsnotify/backend_fen.go @@ -158,7 +158,9 @@ func (w *fen) readEvents() { pevents := make([]unix.PortEvent, 8) for { - count, err := w.port.Get(pevents, 1, nil) + count, err := internal.IgnoringEINTR(func() (int, error) { + return w.port.Get(pevents, 1, nil) + }) if err != nil && err != unix.ETIME { // Interrupted system call (count should be 0) ignore and continue if errors.Is(err, unix.EINTR) && count == 0 { diff --git a/vendor/github.com/fsnotify/fsnotify/backend_inotify.go b/vendor/github.com/fsnotify/fsnotify/backend_inotify.go index a36cb89d73..4c3f6f7c28 100644 --- a/vendor/github.com/fsnotify/fsnotify/backend_inotify.go +++ b/vendor/github.com/fsnotify/fsnotify/backend_inotify.go @@ -55,10 +55,10 @@ type ( path map[string]uint32 // pathname → wd } watch struct { - wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall) - flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags) - path string // Watch path. - recurse bool // Recursion with ./...? + wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall) + flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags) + path string // Watch path. + watchFlags watchFlag } koekje struct { cookie uint32 @@ -66,6 +66,9 @@ type ( } ) +func (w watch) byUser() bool { return w.watchFlags&flagByUser != 0 } +func (w watch) recurse() bool { return w.watchFlags&flagRecurse != 0 } + func newWatches() *watches { return &watches{ wd: make(map[uint32]*watch), @@ -79,6 +82,13 @@ func (w *watches) len() int { return len(w.wd) } func (w *watches) add(ww *watch) { w.wd[ww.wd] = ww; w.path[ww.path] = ww.wd } func (w *watches) remove(watch *watch) { delete(w.path, watch.path); delete(w.wd, watch.wd) } +func isSameOrDescendantPath(path, root string) bool { + if path == root { + return true + } + return strings.HasPrefix(path, root+string(os.PathSeparator)) +} + func (w *watches) removePath(path string) ([]uint32, error) { path, recurse := recursivePath(path) wd, ok := w.path[path] @@ -87,20 +97,20 @@ func (w *watches) removePath(path string) ([]uint32, error) { } watch := w.wd[wd] - if recurse && !watch.recurse { + if recurse && !watch.recurse() { return nil, fmt.Errorf("can't use /... with non-recursive watch %q", path) } delete(w.path, path) delete(w.wd, wd) - if !watch.recurse { + if !watch.recurse() { return []uint32{wd}, nil } wds := make([]uint32, 0, 8) wds = append(wds, wd) for p, rwd := range w.path { - if strings.HasPrefix(p, path) { + if isSameOrDescendantPath(p, path) { delete(w.path, p) delete(w.wd, rwd) wds = append(wds, rwd) @@ -139,7 +149,7 @@ func newBackend(ev chan Event, errs chan error) (backend, error) { // I/O operations won't terminate on close. fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) if fd == -1 { - return nil, errno + return nil, fmt.Errorf("couldn't initialize inotify: %w", errno) } w := &inotify{ @@ -188,11 +198,8 @@ func (w *inotify) AddWith(path string, opts ...addOpt) error { return fmt.Errorf("%w: %s", xErrUnsupported, with.op) } - add := func(path string, with withOpts, recurse bool) error { + add := func(path string, with withOpts, wf watchFlag) error { var flags uint32 - if with.noFollow { - flags |= unix.IN_DONT_FOLLOW - } if with.op.Has(Create) { flags |= unix.IN_CREATE } @@ -220,7 +227,7 @@ func (w *inotify) AddWith(path string, opts ...addOpt) error { if with.op.Has(xUnportableCloseRead) { flags |= unix.IN_CLOSE_NOWRITE } - return w.register(path, flags, recurse) + return w.register(path, flags, wf) } w.mu.Lock() @@ -248,14 +255,18 @@ func (w *inotify) AddWith(path string, opts ...addOpt) error { w.sendEvent(Event{Name: root, Op: Create}) } - return add(root, with, true) + wf := flagRecurse + if root == path { + wf |= flagByUser + } + return add(root, with, wf) }) } - return add(path, with, false) + return add(path, with, 0) } -func (w *inotify) register(path string, flags uint32, recurse bool) error { +func (w *inotify) register(path string, flags uint32, wf watchFlag) error { return w.watches.updatePath(path, func(existing *watch) (*watch, error) { if existing != nil { flags |= existing.flags | unix.IN_MASK_ADD @@ -272,10 +283,10 @@ func (w *inotify) register(path string, flags uint32, recurse bool) error { if existing == nil { return &watch{ - wd: uint32(wd), - path: path, - flags: flags, - recurse: recurse, + wd: uint32(wd), + path: path, + flags: flags, + watchFlags: wf, }, nil } @@ -425,11 +436,7 @@ func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offs nameLen = uint32(inEvent.Len) ) if nameLen > 0 { - /// Point "bytes" at the first byte of the filename - bb := *buf - bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&bb[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen] - /// The filename is padded with NULL bytes. TrimRight() gets rid of those. - name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\x00") + name += "/" + inotifyEventName(buf, offset, nameLen) } if debug { @@ -450,7 +457,9 @@ func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offs // We can't really update the state when a watched path is moved; only // IN_MOVE_SELF is sent and not IN_MOVED_{FROM,TO}. So remove the watch. if inEvent.Mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF { - if watch.recurse { // Do nothing + // Watch is set up as part of recurse: do nothing as the move gets + // registered from the parent directory. + if watch.recurse() && !watch.byUser() { return Event{}, true } @@ -460,6 +469,10 @@ func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offs return Event{}, false } } + + if watch.recurse() { + return Event{Name: watch.path, Op: Rename}, true + } } /// Skip if we're watching both this path and the parent; the parent will @@ -473,11 +486,11 @@ func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offs ev := w.newEvent(name, inEvent.Mask, inEvent.Cookie) // Need to update watch path for recurse. - if watch.recurse { + if watch.recurse() { isDir := inEvent.Mask&unix.IN_ISDIR == unix.IN_ISDIR /// New directory created: set up watch on it. if isDir && ev.Has(Create) { - err := w.register(ev.Name, watch.flags, true) + err := w.register(ev.Name, watch.flags, flagRecurse) if !w.sendError(err) { return Event{}, false } @@ -495,7 +508,7 @@ func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offs if k == watch.wd || ww.path == ev.Name { continue } - if strings.HasPrefix(ww.path, ev.renamedFrom) { + if isSameOrDescendantPath(ww.path, ev.renamedFrom) { ww.path = strings.Replace(ww.path, ev.renamedFrom, ev.Name, 1) w.watches.wd[k] = ww } @@ -507,12 +520,13 @@ func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offs return ev, true } -func (w *inotify) isRecursive(path string) bool { - ww := w.watches.byPath(path) - if ww == nil { // path could be a file, so also check the Dir. - ww = w.watches.byPath(filepath.Dir(path)) +func inotifyEventName(buf *[65536]byte, offset, nameLen uint32) string { + start := int(offset + unix.SizeofInotifyEvent) + bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[start]))[:nameLen:nameLen] + for nameLen > 0 && bytes[nameLen-1] == 0 { + nameLen-- } - return ww != nil && ww.recurse + return string(bytes[:nameLen]) } func (w *inotify) newEvent(name string, mask, cookie uint32) Event { @@ -578,6 +592,6 @@ func (w *inotify) state() { w.mu.Lock() defer w.mu.Unlock() for wd, ww := range w.watches.wd { - fmt.Fprintf(os.Stderr, "%4d: recurse=%t %q\n", wd, ww.recurse, ww.path) + fmt.Fprintf(os.Stderr, "%4d: %q watchFlags=0x%x\n", wd, ww.path, ww.watchFlags) } } diff --git a/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go b/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go index 340aeec061..d2c8cfb6c6 100644 --- a/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go +++ b/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "runtime" + "sort" "sync" "time" @@ -245,9 +246,26 @@ func (w *kqueue) Close() error { return nil } + // Snapshot and drop all watches directly. w.Remove -> w.remove + // short-circuits on isClosed() (which is already true after + // w.shared.close() above), so calling Remove here in the happy path + // leaked every watched directory + file descriptor. On macOS a + // single directory watch opens an fd for every file in the dir, so + // long-running processes that recreate watchers (hot-reload dev + // servers, etc.) ran out of fds with EMFILE (#732). pathsToRemove := w.watches.listPaths(false) for _, name := range pathsToRemove { - w.Remove(name) + info, ok := w.watches.byPath(name) + if !ok { + // w.path has an entry for name but w.wd doesn't -- + // drop the stale lookup entry so the map state is + // consistent after Close. + w.watches.remove(0, name) + continue + } + _ = w.register([]int{info.wd}, unix.EV_DELETE, 0) + unix.Close(info.wd) + w.watches.remove(info.wd, name) } unix.Close(w.closepipe[1]) // Send "quit" message to readEvents @@ -376,19 +394,12 @@ func (w *kqueue) addWatch(name string, flags uint32, listDir bool) (string, erro } } - // Retry on EINTR; open() can return EINTR in practice on macOS. - // See #354, and Go issues 11180 and 39237. - for { - info.wd, err = unix.Open(name, openMode, 0) - if err == nil { - break - } - if errors.Is(err, unix.EINTR) { - continue - } + info.wd, err = internal.IgnoringEINTR(func() (int, error) { + return unix.Open(name, openMode, 0) + }) + if err != nil { return "", err } - info.isDir = fi.IsDir() } @@ -436,9 +447,10 @@ func (w *kqueue) readEvents() { eventBuffer := make([]unix.Kevent_t, 10) for { - kevents, err := w.read(eventBuffer) - // EINTR is okay, the syscall was interrupted before timeout expired. - if err != nil && err != unix.EINTR { + kevents, err := internal.IgnoringEINTR(func() ([]unix.Kevent_t, error) { + return w.read(eventBuffer) + }) + if err != nil { if !w.sendError(fmt.Errorf("fsnotify.readEvents: %w", err)) { return } @@ -583,12 +595,14 @@ func (w *kqueue) watchDirectoryFiles(dirPath string) error { cleanPath, err := w.internalWatch(path, fi) if err != nil { - // No permission to read the file; that's not a problem: just skip. - // But do add it to w.fileExists to prevent it from being picked up - // as a "new" file later (it still shows up in the directory + // No permission, or the entry resolved to a missing target + // (e.g. a dangling symlink): not a problem, just skip. But + // do mark it as seen to prevent it from being picked up as + // a "new" file later (it still shows up in the directory // listing). switch { - case errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM): + case errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM) || + errors.Is(err, os.ErrNotExist): cleanPath = filepath.Clean(path) default: return fmt.Errorf("%q: %w", path, err) @@ -703,3 +717,19 @@ func (w *kqueue) xSupports(op Op) bool { } return true } + +func (w *kqueue) state() { + w.watches.mu.Lock() + defer w.watches.mu.Unlock() + + all := make([]int, 0, len(w.watches.wd)) + for wd := range w.watches.wd { + all = append(all, wd) + } + sort.Ints(all) + + for _, wd := range all { + ww := w.watches.wd[wd] + fmt.Fprintf(os.Stderr, "%4d %q linkname=%q\n", wd, ww.name, ww.linkName) + } +} diff --git a/vendor/github.com/fsnotify/fsnotify/backend_windows.go b/vendor/github.com/fsnotify/fsnotify/backend_windows.go index 3433642d64..fb9210f24e 100644 --- a/vendor/github.com/fsnotify/fsnotify/backend_windows.go +++ b/vendor/github.com/fsnotify/fsnotify/backend_windows.go @@ -11,7 +11,6 @@ import ( "fmt" "os" "path/filepath" - "reflect" "runtime" "strings" "sync" @@ -37,6 +36,13 @@ type readDirChangesW struct { var defaultBufferSize = 50 +func isSameOrDescendantPath(path, root string) bool { + if path == root { + return true + } + return strings.HasPrefix(path, root+string(os.PathSeparator)) +} + func newBackend(ev chan Event, errs chan error) (backend, error) { port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0) if err != nil { @@ -359,22 +365,26 @@ func (w *readDirChangesW) addWatch(pathname string, flags uint64, bufsize int) e } else { windows.CloseHandle(ino.handle) } + w.mu.Lock() if pathname == dir { watchEntry.mask |= flags } else { watchEntry.names[filepath.Base(pathname)] |= flags } + w.mu.Unlock() err = w.startRead(watchEntry) if err != nil { return err } + w.mu.Lock() if pathname == dir { watchEntry.mask &= ^provisional } else { watchEntry.names[filepath.Base(pathname)] &= ^provisional } + w.mu.Unlock() return nil } @@ -394,8 +404,13 @@ func (w *readDirChangesW) remWatch(pathname string) error { w.mu.Lock() watch := w.watches.get(ino) w.mu.Unlock() + if watch == nil { + windows.CloseHandle(ino.handle) + return fmt.Errorf("%w: %s", ErrNonExistentWatch, pathname) + } if recurse && !watch.recurse { + windows.CloseHandle(ino.handle) return fmt.Errorf("can't use \\... with non-recursive watch %q", pathname) } @@ -403,16 +418,19 @@ func (w *readDirChangesW) remWatch(pathname string) error { if err != nil { w.sendError(os.NewSyscallError("CloseHandle", err)) } - if watch == nil { - return fmt.Errorf("%w: %s", ErrNonExistentWatch, pathname) - } if pathname == dir { - w.sendEvent(watch.path, "", watch.mask&sysFSIGNORED) + w.mu.Lock() + mask := watch.mask watch.mask = 0 + w.mu.Unlock() + w.sendEvent(watch.path, "", mask&sysFSIGNORED) } else { name := filepath.Base(pathname) - w.sendEvent(filepath.Join(watch.path, name), "", watch.names[name]&sysFSIGNORED) + w.mu.Lock() + mask := watch.names[name] delete(watch.names, name) + w.mu.Unlock() + w.sendEvent(filepath.Join(watch.path, name), "", mask&sysFSIGNORED) } return w.startRead(watch) @@ -420,17 +438,23 @@ func (w *readDirChangesW) remWatch(pathname string) error { // Must run within the I/O thread. func (w *readDirChangesW) deleteWatch(watch *watch) { - for name, mask := range watch.names { - if mask&provisional == 0 { - w.sendEvent(filepath.Join(watch.path, name), "", mask&sysFSIGNORED) + // Snapshot+clear under the lock so concurrent WatchList() readers see a + // consistent state. sendEvent must run outside the lock since it can + // block on the user-facing Events channel. + w.mu.Lock() + names := watch.names + watch.names = make(map[string]uint64) + mask := watch.mask + watch.mask = 0 + w.mu.Unlock() + + for name, m := range names { + if m&provisional == 0 { + w.sendEvent(filepath.Join(watch.path, name), "", m&sysFSIGNORED) } - delete(watch.names, name) } - if watch.mask != 0 { - if watch.mask&provisional == 0 { - w.sendEvent(watch.path, "", watch.mask&sysFSIGNORED) - } - watch.mask = 0 + if mask != 0 && mask&provisional == 0 { + w.sendEvent(watch.path, "", mask&sysFSIGNORED) } } @@ -457,9 +481,8 @@ func (w *readDirChangesW) startRead(watch *watch) error { } // We need to pass the array, rather than the slice. - hdr := (*reflect.SliceHeader)(unsafe.Pointer(&watch.buf)) rdErr := windows.ReadDirectoryChanges(watch.ino.handle, - (*byte)(unsafe.Pointer(hdr.Data)), uint32(hdr.Len), + unsafe.SliceData(watch.buf), uint32(len(watch.buf)), watch.recurse, mask, nil, &watch.ov, 0) if rdErr != nil { err := os.NewSyscallError("ReadDirectoryChanges", rdErr) @@ -565,12 +588,7 @@ func (w *readDirChangesW) readEvents() { // Create a buf that is the size of the path name size := int(raw.FileNameLength / 2) - var buf []uint16 - // TODO: Use unsafe.Slice in Go 1.17; https://stackoverflow.com/questions/51187973 - sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) - sh.Data = uintptr(unsafe.Pointer(&raw.FileName)) - sh.Len = size - sh.Cap = size + buf := unsafe.Slice(&raw.FileName, size) name := windows.UTF16ToString(buf) fullname := filepath.Join(watch.path, name) @@ -587,31 +605,35 @@ func (w *readDirChangesW) readEvents() { case windows.FILE_ACTION_RENAMED_OLD_NAME: watch.rename = name case windows.FILE_ACTION_RENAMED_NEW_NAME: - // Update saved path of all sub-watches. + // Update saved path of all sub-watches and rename the + // names entry under the lock so WatchList() can't observe + // a torn state. old := filepath.Join(watch.path, watch.rename) w.mu.Lock() for _, watchMap := range w.watches { for _, ww := range watchMap { - if strings.HasPrefix(ww.path, old) { + if isSameOrDescendantPath(ww.path, old) { ww.path = filepath.Join(fullname, strings.TrimPrefix(ww.path, old)) } } } - w.mu.Unlock() - if watch.names[watch.rename] != 0 { watch.names[name] |= watch.names[watch.rename] delete(watch.names, watch.rename) mask = sysFSMOVESELF } + w.mu.Unlock() } if raw.Action != windows.FILE_ACTION_RENAMED_NEW_NAME { w.sendEvent(fullname, "", watch.names[name]&mask) } if raw.Action == windows.FILE_ACTION_REMOVED { - w.sendEvent(fullname, "", watch.names[name]&sysFSIGNORED) + w.mu.Lock() + ignored := watch.names[name] & sysFSIGNORED delete(watch.names, name) + w.mu.Unlock() + w.sendEvent(fullname, "", ignored) } if watch.rename != "" && raw.Action == windows.FILE_ACTION_RENAMED_NEW_NAME { diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify.go b/vendor/github.com/fsnotify/fsnotify/fsnotify.go index f64be4bf98..38cb4dd481 100644 --- a/vendor/github.com/fsnotify/fsnotify/fsnotify.go +++ b/vendor/github.com/fsnotify/fsnotify/fsnotify.go @@ -51,26 +51,25 @@ import ( // The fs.inotify.max_user_watches sysctl variable specifies the upper limit // for the number of watches per user, and fs.inotify.max_user_instances // specifies the maximum number of inotify instances per user. Every Watcher you -// create is an "instance", and every path you add is a "watch". +// create is an "instance", and every path you add is a "watch". Reaching the +// limit will result in a "no space left on device" or "too many open files" +// error. // // These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and -// /proc/sys/fs/inotify/max_user_instances +// /proc/sys/fs/inotify/max_user_instances. The default values differ per distro +// and available memory. // // To increase them you can use sysctl or write the value to the /proc file: // -// # Default values on Linux 5.18 -// sysctl fs.inotify.max_user_watches=124983 -// sysctl fs.inotify.max_user_instances=128 +// sysctl fs.inotify.max_user_watches=200000 +// sysctl fs.inotify.max_user_instances=256 // // To make the changes persist on reboot edit /etc/sysctl.conf or // /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check // your distro's documentation): // -// fs.inotify.max_user_watches=124983 -// fs.inotify.max_user_instances=128 -// -// Reaching the limit will result in a "no space left on device" or "too many open -// files" error. +// fs.inotify.max_user_watches=200000 +// fs.inotify.max_user_instances=256 // // # kqueue notes (macOS, BSD) // @@ -93,6 +92,28 @@ import ( // Sometimes it will send events for all files, sometimes it will send no // events, and often only for some files. // +// Recursive watching is not currently enabled through fsnotify's public +// API; the recursive code path is gated and only exercised by fsnotify's +// own tests. The note below describes backend behavior observed when +// recursive watching is enabled internally, and is kept here as a +// reference for maintainers and contributors who encounter it. +// +// When recursive watching is enabled and you watch a directory, you may +// receive a Write event for an intermediate directory whenever a child +// entry inside it is created, renamed, or removed. For example, with a +// recursive watch on /a and a new file /a/b/c, you will receive +// Create /a/b/c and may also receive Write /a/b. +// +// This happens because, on NTFS-backed volumes, modifying the entries of a +// directory updates that directory's last-write time, and the Windows +// backend requests FILE_NOTIFY_CHANGE_LAST_WRITE to support Write events +// on files. The same Write filter therefore picks up the directory's +// metadata update. +// +// Whether the directory Write is actually delivered alongside the child +// events is not guaranteed; it depends on ReadDirectoryChangesW buffering, +// NTFS metadata update timing, and event coalescing. +// // The default ReadDirectoryChangesW() buffer size is 64K, which is the largest // value that is guaranteed to work with SMB filesystems. If you have many // events in quick succession this may not be enough, and you will have to use @@ -129,8 +150,12 @@ type Watcher struct { // want to wait until you've stopped receiving them // (see the dedup example in cmd/fsnotify). // - // Some systems may send Write event for directories - // when the directory content changes. + // Some systems also send Write events for directories + // when the directory contents change. This is the + // case for kqueue, and on Windows for the directory + // that contains a created, renamed, or removed child + // entry. It does not happen on inotify. See the + // per-platform notes on [Watcher]. // // fsnotify.Chmod Attributes were changed. On Linux this is also sent // when a file is removed (or more accurately, when a @@ -179,7 +204,9 @@ const ( Create Op = 1 << iota // The pathname was written to; this does *not* mean the write has finished, - // and a write can be followed by more writes. + // and a write can be followed by more writes. On Windows and kqueue, a + // Write on a directory can also indicate that its contents changed; see + // the per-platform notes on [Watcher]. Write // The path was removed; any watches on it will be removed. Some "remove" @@ -220,7 +247,7 @@ const ( // File opened for reading was closed. // - // Only works on Linux and FreeBSD. + // Only works on Linux. xUnportableCloseRead ) @@ -410,7 +437,6 @@ type ( withOpts struct { bufsize int op Op - noFollow bool sendCreate bool } ) @@ -469,12 +495,6 @@ func withOps(op Op) addOpt { return func(opt *withOpts) { opt.op = op } } -// WithNoFollow disables following symlinks, so the symlinks themselves are -// watched. -func withNoFollow() addOpt { - return func(opt *withOpts) { opt.noFollow = true } -} - // "Internal" option for recursive watches on inotify. func withCreate() addOpt { return func(opt *withOpts) { opt.sendCreate = true } @@ -494,3 +514,13 @@ func recursivePath(path string) (string, bool) { } return path, false } + +type watchFlag uint8 + +const ( + // Added by user with Add(), rather than an internal watch. + flagByUser = watchFlag(0x01) + // Part of recursive watch; as the top-level path added by the user or an + // "internal" watch. + flagRecurse = watchFlag(0x02) +) diff --git a/vendor/github.com/fsnotify/fsnotify/internal/darwin.go b/vendor/github.com/fsnotify/fsnotify/internal/darwin.go index 0b01bc182a..6721aa6096 100644 --- a/vendor/github.com/fsnotify/fsnotify/internal/darwin.go +++ b/vendor/github.com/fsnotify/fsnotify/internal/darwin.go @@ -15,25 +15,6 @@ var ( var maxfiles uint64 -func SetRlimit() { - // Go 1.19 will do this automatically: https://go-review.googlesource.com/c/go/+/393354/ - var l syscall.Rlimit - err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &l) - if err == nil && l.Cur != l.Max { - l.Cur = l.Max - syscall.Setrlimit(syscall.RLIMIT_NOFILE, &l) - } - maxfiles = l.Cur - - if n, err := syscall.SysctlUint32("kern.maxfiles"); err == nil && uint64(n) < maxfiles { - maxfiles = uint64(n) - } - - if n, err := syscall.SysctlUint32("kern.maxfilesperproc"); err == nil && uint64(n) < maxfiles { - maxfiles = uint64(n) - } -} - func Maxfiles() uint64 { return maxfiles } func Mkfifo(path string, mode uint32) error { return unix.Mkfifo(path, mode) } func Mknod(path string, mode uint32, dev int) error { return unix.Mknod(path, mode, dev) } diff --git a/vendor/github.com/fsnotify/fsnotify/internal/debug_darwin.go b/vendor/github.com/fsnotify/fsnotify/internal/debug_darwin.go index 928319fb09..7600180742 100644 --- a/vendor/github.com/fsnotify/fsnotify/internal/debug_darwin.go +++ b/vendor/github.com/fsnotify/fsnotify/internal/debug_darwin.go @@ -6,52 +6,10 @@ var names = []struct { n string m uint32 }{ - {"NOTE_ABSOLUTE", unix.NOTE_ABSOLUTE}, {"NOTE_ATTRIB", unix.NOTE_ATTRIB}, - {"NOTE_BACKGROUND", unix.NOTE_BACKGROUND}, - {"NOTE_CHILD", unix.NOTE_CHILD}, - {"NOTE_CRITICAL", unix.NOTE_CRITICAL}, {"NOTE_DELETE", unix.NOTE_DELETE}, - {"NOTE_EXEC", unix.NOTE_EXEC}, - {"NOTE_EXIT", unix.NOTE_EXIT}, - {"NOTE_EXITSTATUS", unix.NOTE_EXITSTATUS}, - {"NOTE_EXIT_CSERROR", unix.NOTE_EXIT_CSERROR}, - {"NOTE_EXIT_DECRYPTFAIL", unix.NOTE_EXIT_DECRYPTFAIL}, - {"NOTE_EXIT_DETAIL", unix.NOTE_EXIT_DETAIL}, - {"NOTE_EXIT_DETAIL_MASK", unix.NOTE_EXIT_DETAIL_MASK}, - {"NOTE_EXIT_MEMORY", unix.NOTE_EXIT_MEMORY}, - {"NOTE_EXIT_REPARENTED", unix.NOTE_EXIT_REPARENTED}, {"NOTE_EXTEND", unix.NOTE_EXTEND}, - {"NOTE_FFAND", unix.NOTE_FFAND}, - {"NOTE_FFCOPY", unix.NOTE_FFCOPY}, - {"NOTE_FFCTRLMASK", unix.NOTE_FFCTRLMASK}, - {"NOTE_FFLAGSMASK", unix.NOTE_FFLAGSMASK}, - {"NOTE_FFNOP", unix.NOTE_FFNOP}, - {"NOTE_FFOR", unix.NOTE_FFOR}, - {"NOTE_FORK", unix.NOTE_FORK}, - {"NOTE_FUNLOCK", unix.NOTE_FUNLOCK}, - {"NOTE_LEEWAY", unix.NOTE_LEEWAY}, {"NOTE_LINK", unix.NOTE_LINK}, - {"NOTE_LOWAT", unix.NOTE_LOWAT}, - {"NOTE_MACHTIME", unix.NOTE_MACHTIME}, - {"NOTE_MACH_CONTINUOUS_TIME", unix.NOTE_MACH_CONTINUOUS_TIME}, - {"NOTE_NONE", unix.NOTE_NONE}, - {"NOTE_NSECONDS", unix.NOTE_NSECONDS}, - {"NOTE_OOB", unix.NOTE_OOB}, - //{"NOTE_PCTRLMASK", unix.NOTE_PCTRLMASK}, -0x100000 (?!) - {"NOTE_PDATAMASK", unix.NOTE_PDATAMASK}, - {"NOTE_REAP", unix.NOTE_REAP}, {"NOTE_RENAME", unix.NOTE_RENAME}, - {"NOTE_REVOKE", unix.NOTE_REVOKE}, - {"NOTE_SECONDS", unix.NOTE_SECONDS}, - {"NOTE_SIGNAL", unix.NOTE_SIGNAL}, - {"NOTE_TRACK", unix.NOTE_TRACK}, - {"NOTE_TRACKERR", unix.NOTE_TRACKERR}, - {"NOTE_TRIGGER", unix.NOTE_TRIGGER}, - {"NOTE_USECONDS", unix.NOTE_USECONDS}, - {"NOTE_VM_ERROR", unix.NOTE_VM_ERROR}, - {"NOTE_VM_PRESSURE", unix.NOTE_VM_PRESSURE}, - {"NOTE_VM_PRESSURE_SUDDEN_TERMINATE", unix.NOTE_VM_PRESSURE_SUDDEN_TERMINATE}, - {"NOTE_VM_PRESSURE_TERMINATE", unix.NOTE_VM_PRESSURE_TERMINATE}, {"NOTE_WRITE", unix.NOTE_WRITE}, } diff --git a/vendor/github.com/fsnotify/fsnotify/internal/debug_dragonfly.go b/vendor/github.com/fsnotify/fsnotify/internal/debug_dragonfly.go index 3186b0c349..7600180742 100644 --- a/vendor/github.com/fsnotify/fsnotify/internal/debug_dragonfly.go +++ b/vendor/github.com/fsnotify/fsnotify/internal/debug_dragonfly.go @@ -7,27 +7,9 @@ var names = []struct { m uint32 }{ {"NOTE_ATTRIB", unix.NOTE_ATTRIB}, - {"NOTE_CHILD", unix.NOTE_CHILD}, {"NOTE_DELETE", unix.NOTE_DELETE}, - {"NOTE_EXEC", unix.NOTE_EXEC}, - {"NOTE_EXIT", unix.NOTE_EXIT}, {"NOTE_EXTEND", unix.NOTE_EXTEND}, - {"NOTE_FFAND", unix.NOTE_FFAND}, - {"NOTE_FFCOPY", unix.NOTE_FFCOPY}, - {"NOTE_FFCTRLMASK", unix.NOTE_FFCTRLMASK}, - {"NOTE_FFLAGSMASK", unix.NOTE_FFLAGSMASK}, - {"NOTE_FFNOP", unix.NOTE_FFNOP}, - {"NOTE_FFOR", unix.NOTE_FFOR}, - {"NOTE_FORK", unix.NOTE_FORK}, {"NOTE_LINK", unix.NOTE_LINK}, - {"NOTE_LOWAT", unix.NOTE_LOWAT}, - {"NOTE_OOB", unix.NOTE_OOB}, - {"NOTE_PCTRLMASK", unix.NOTE_PCTRLMASK}, - {"NOTE_PDATAMASK", unix.NOTE_PDATAMASK}, {"NOTE_RENAME", unix.NOTE_RENAME}, - {"NOTE_REVOKE", unix.NOTE_REVOKE}, - {"NOTE_TRACK", unix.NOTE_TRACK}, - {"NOTE_TRACKERR", unix.NOTE_TRACKERR}, - {"NOTE_TRIGGER", unix.NOTE_TRIGGER}, {"NOTE_WRITE", unix.NOTE_WRITE}, } diff --git a/vendor/github.com/fsnotify/fsnotify/internal/debug_freebsd.go b/vendor/github.com/fsnotify/fsnotify/internal/debug_freebsd.go index f69fdb930f..b9e45f5551 100644 --- a/vendor/github.com/fsnotify/fsnotify/internal/debug_freebsd.go +++ b/vendor/github.com/fsnotify/fsnotify/internal/debug_freebsd.go @@ -6,37 +6,15 @@ var names = []struct { n string m uint32 }{ - {"NOTE_ABSTIME", unix.NOTE_ABSTIME}, - {"NOTE_ATTRIB", unix.NOTE_ATTRIB}, - {"NOTE_CHILD", unix.NOTE_CHILD}, - {"NOTE_CLOSE", unix.NOTE_CLOSE}, - {"NOTE_CLOSE_WRITE", unix.NOTE_CLOSE_WRITE}, {"NOTE_DELETE", unix.NOTE_DELETE}, - {"NOTE_EXEC", unix.NOTE_EXEC}, - {"NOTE_EXIT", unix.NOTE_EXIT}, + {"NOTE_WRITE", unix.NOTE_WRITE}, {"NOTE_EXTEND", unix.NOTE_EXTEND}, - {"NOTE_FFAND", unix.NOTE_FFAND}, - {"NOTE_FFCOPY", unix.NOTE_FFCOPY}, - {"NOTE_FFCTRLMASK", unix.NOTE_FFCTRLMASK}, - {"NOTE_FFLAGSMASK", unix.NOTE_FFLAGSMASK}, - {"NOTE_FFNOP", unix.NOTE_FFNOP}, - {"NOTE_FFOR", unix.NOTE_FFOR}, - {"NOTE_FILE_POLL", unix.NOTE_FILE_POLL}, - {"NOTE_FORK", unix.NOTE_FORK}, + {"NOTE_ATTRIB", unix.NOTE_ATTRIB}, {"NOTE_LINK", unix.NOTE_LINK}, - {"NOTE_LOWAT", unix.NOTE_LOWAT}, - {"NOTE_MSECONDS", unix.NOTE_MSECONDS}, - {"NOTE_NSECONDS", unix.NOTE_NSECONDS}, - {"NOTE_OPEN", unix.NOTE_OPEN}, - {"NOTE_PCTRLMASK", unix.NOTE_PCTRLMASK}, - {"NOTE_PDATAMASK", unix.NOTE_PDATAMASK}, - {"NOTE_READ", unix.NOTE_READ}, {"NOTE_RENAME", unix.NOTE_RENAME}, {"NOTE_REVOKE", unix.NOTE_REVOKE}, - {"NOTE_SECONDS", unix.NOTE_SECONDS}, - {"NOTE_TRACK", unix.NOTE_TRACK}, - {"NOTE_TRACKERR", unix.NOTE_TRACKERR}, - {"NOTE_TRIGGER", unix.NOTE_TRIGGER}, - {"NOTE_USECONDS", unix.NOTE_USECONDS}, - {"NOTE_WRITE", unix.NOTE_WRITE}, + {"NOTE_OPEN", unix.NOTE_OPEN}, + {"NOTE_CLOSE", unix.NOTE_CLOSE}, + {"NOTE_CLOSE_WRITE", unix.NOTE_CLOSE_WRITE}, + {"NOTE_READ", unix.NOTE_READ}, } diff --git a/vendor/github.com/fsnotify/fsnotify/internal/debug_kqueue.go b/vendor/github.com/fsnotify/fsnotify/internal/debug_kqueue.go index 607e683bd7..5d8116436d 100644 --- a/vendor/github.com/fsnotify/fsnotify/internal/debug_kqueue.go +++ b/vendor/github.com/fsnotify/fsnotify/internal/debug_kqueue.go @@ -27,6 +27,6 @@ func Debug(name string, kevent *unix.Kevent_t) { if unknown > 0 { l = append(l, fmt.Sprintf("0x%x", unknown)) } - fmt.Fprintf(os.Stderr, "FSNOTIFY_DEBUG: %s %10d:%-60s → %q\n", + fmt.Fprintf(os.Stderr, "FSNOTIFY_DEBUG: %s %10d:%-20s → %q\n", time.Now().Format("15:04:05.000000000"), mask, strings.Join(l, " | "), name) } diff --git a/vendor/github.com/fsnotify/fsnotify/internal/debug_netbsd.go b/vendor/github.com/fsnotify/fsnotify/internal/debug_netbsd.go index e5b3b6f694..7600180742 100644 --- a/vendor/github.com/fsnotify/fsnotify/internal/debug_netbsd.go +++ b/vendor/github.com/fsnotify/fsnotify/internal/debug_netbsd.go @@ -7,19 +7,9 @@ var names = []struct { m uint32 }{ {"NOTE_ATTRIB", unix.NOTE_ATTRIB}, - {"NOTE_CHILD", unix.NOTE_CHILD}, {"NOTE_DELETE", unix.NOTE_DELETE}, - {"NOTE_EXEC", unix.NOTE_EXEC}, - {"NOTE_EXIT", unix.NOTE_EXIT}, {"NOTE_EXTEND", unix.NOTE_EXTEND}, - {"NOTE_FORK", unix.NOTE_FORK}, {"NOTE_LINK", unix.NOTE_LINK}, - {"NOTE_LOWAT", unix.NOTE_LOWAT}, - {"NOTE_PCTRLMASK", unix.NOTE_PCTRLMASK}, - {"NOTE_PDATAMASK", unix.NOTE_PDATAMASK}, {"NOTE_RENAME", unix.NOTE_RENAME}, - {"NOTE_REVOKE", unix.NOTE_REVOKE}, - {"NOTE_TRACK", unix.NOTE_TRACK}, - {"NOTE_TRACKERR", unix.NOTE_TRACKERR}, {"NOTE_WRITE", unix.NOTE_WRITE}, } diff --git a/vendor/github.com/fsnotify/fsnotify/internal/debug_openbsd.go b/vendor/github.com/fsnotify/fsnotify/internal/debug_openbsd.go index 1dd455bc5a..871766d699 100644 --- a/vendor/github.com/fsnotify/fsnotify/internal/debug_openbsd.go +++ b/vendor/github.com/fsnotify/fsnotify/internal/debug_openbsd.go @@ -7,22 +7,10 @@ var names = []struct { m uint32 }{ {"NOTE_ATTRIB", unix.NOTE_ATTRIB}, - // {"NOTE_CHANGE", unix.NOTE_CHANGE}, // Not on 386? - {"NOTE_CHILD", unix.NOTE_CHILD}, {"NOTE_DELETE", unix.NOTE_DELETE}, - {"NOTE_EOF", unix.NOTE_EOF}, - {"NOTE_EXEC", unix.NOTE_EXEC}, - {"NOTE_EXIT", unix.NOTE_EXIT}, {"NOTE_EXTEND", unix.NOTE_EXTEND}, - {"NOTE_FORK", unix.NOTE_FORK}, {"NOTE_LINK", unix.NOTE_LINK}, - {"NOTE_LOWAT", unix.NOTE_LOWAT}, - {"NOTE_PCTRLMASK", unix.NOTE_PCTRLMASK}, - {"NOTE_PDATAMASK", unix.NOTE_PDATAMASK}, {"NOTE_RENAME", unix.NOTE_RENAME}, - {"NOTE_REVOKE", unix.NOTE_REVOKE}, - {"NOTE_TRACK", unix.NOTE_TRACK}, - {"NOTE_TRACKERR", unix.NOTE_TRACKERR}, {"NOTE_TRUNCATE", unix.NOTE_TRUNCATE}, {"NOTE_WRITE", unix.NOTE_WRITE}, } diff --git a/vendor/github.com/fsnotify/fsnotify/internal/freebsd.go b/vendor/github.com/fsnotify/fsnotify/internal/freebsd.go index 5ac8b50797..758a249052 100644 --- a/vendor/github.com/fsnotify/fsnotify/internal/freebsd.go +++ b/vendor/github.com/fsnotify/fsnotify/internal/freebsd.go @@ -15,17 +15,6 @@ var ( var maxfiles uint64 -func SetRlimit() { - // Go 1.19 will do this automatically: https://go-review.googlesource.com/c/go/+/393354/ - var l syscall.Rlimit - err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &l) - if err == nil && l.Cur != l.Max { - l.Cur = l.Max - syscall.Setrlimit(syscall.RLIMIT_NOFILE, &l) - } - maxfiles = uint64(l.Cur) -} - func Maxfiles() uint64 { return maxfiles } func Mkfifo(path string, mode uint32) error { return unix.Mkfifo(path, mode) } func Mknod(path string, mode uint32, dev int) error { return unix.Mknod(path, mode, uint64(dev)) } diff --git a/vendor/github.com/fsnotify/fsnotify/internal/unix.go b/vendor/github.com/fsnotify/fsnotify/internal/unix.go index b251fb8038..9c66f5d30d 100644 --- a/vendor/github.com/fsnotify/fsnotify/internal/unix.go +++ b/vendor/github.com/fsnotify/fsnotify/internal/unix.go @@ -15,17 +15,6 @@ var ( var maxfiles uint64 -func SetRlimit() { - // Go 1.19 will do this automatically: https://go-review.googlesource.com/c/go/+/393354/ - var l syscall.Rlimit - err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &l) - if err == nil && l.Cur != l.Max { - l.Cur = l.Max - syscall.Setrlimit(syscall.RLIMIT_NOFILE, &l) - } - maxfiles = uint64(l.Cur) -} - func Maxfiles() uint64 { return maxfiles } func Mkfifo(path string, mode uint32) error { return unix.Mkfifo(path, mode) } func Mknod(path string, mode uint32, dev int) error { return unix.Mknod(path, mode, dev) } diff --git a/vendor/github.com/fsnotify/fsnotify/internal/unix2.go b/vendor/github.com/fsnotify/fsnotify/internal/unix2.go index 37dfeddc28..b2d89592f7 100644 --- a/vendor/github.com/fsnotify/fsnotify/internal/unix2.go +++ b/vendor/github.com/fsnotify/fsnotify/internal/unix2.go @@ -2,6 +2,24 @@ package internal +import "syscall" + func HasPrivilegesForSymlink() bool { return true } + +// IgnoringEINTR makes a function call and repeats it if it returns an +// EINTR error. This appears to be required even though we install all +// signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846. +// Also #20400 and #36644 are issues in which a signal handler is +// installed without setting SA_RESTART. None of these are the common case, +// but there are enough of them that it seems that we can't avoid +// an EINTR loop. +func IgnoringEINTR[T any](fn func() (T, error)) (T, error) { + for { + v, err := fn() + if err != syscall.EINTR { + return v, err + } + } +} diff --git a/vendor/github.com/fsnotify/fsnotify/internal/windows.go b/vendor/github.com/fsnotify/fsnotify/internal/windows.go index 896bc2e5a2..e24d569264 100644 --- a/vendor/github.com/fsnotify/fsnotify/internal/windows.go +++ b/vendor/github.com/fsnotify/fsnotify/internal/windows.go @@ -14,7 +14,6 @@ var ( ErrUnixEACCES = errors.New("dummy") ) -func SetRlimit() {} func Maxfiles() uint64 { return 1<<64 - 1 } func Mkfifo(path string, mode uint32) error { return errors.New("no FIFOs on Windows") } func Mknod(path string, mode uint32, dev int) error { return errors.New("no device nodes on Windows") } diff --git a/vendor/github.com/getsentry/sentry-go/.codecov.yml b/vendor/github.com/getsentry/sentry-go/.codecov.yml index 0c0e695f27..c44ceb25ed 100644 --- a/vendor/github.com/getsentry/sentry-go/.codecov.yml +++ b/vendor/github.com/getsentry/sentry-go/.codecov.yml @@ -1,10 +1,3 @@ -codecov: - # across - notify: - # Do not notify until at least this number of reports have been uploaded - # from the CI pipeline. We normally have more than that number, but 6 - # should be enough to get a first notification. - after_n_builds: 6 coverage: status: project: diff --git a/vendor/github.com/getsentry/sentry-go/.craft.yml b/vendor/github.com/getsentry/sentry-go/.craft.yml index 31f4e5ca06..307d6b66ae 100644 --- a/vendor/github.com/getsentry/sentry-go/.craft.yml +++ b/vendor/github.com/getsentry/sentry-go/.craft.yml @@ -27,6 +27,9 @@ targets: - name: github tagPrefix: gin/v tagOnly: true + - name: github + tagPrefix: grpc/v + tagOnly: true - name: github tagPrefix: iris/v tagOnly: true diff --git a/vendor/github.com/getsentry/sentry-go/.golangci.yml b/vendor/github.com/getsentry/sentry-go/.golangci.yml index e487535b8e..6dc3dc9db1 100644 --- a/vendor/github.com/getsentry/sentry-go/.golangci.yml +++ b/vendor/github.com/getsentry/sentry-go/.golangci.yml @@ -38,7 +38,11 @@ linters: - linters: - gosec path: _test\.go - text: 'G306:' + text: "G306:" + - linters: + - staticcheck + path: _test\.go + text: "SA5011" - linters: - unused path: errors_test\.go diff --git a/vendor/github.com/getsentry/sentry-go/AGENTS.md b/vendor/github.com/getsentry/sentry-go/AGENTS.md index 0043024927..f040abe571 100644 --- a/vendor/github.com/getsentry/sentry-go/AGENTS.md +++ b/vendor/github.com/getsentry/sentry-go/AGENTS.md @@ -69,17 +69,18 @@ The `internalAsyncTransportAdapter` in `transport.go` bridges old `Transport` to Test tier preference (use the highest tier that covers what you need): -1. **Integration tests** (default) — `sentry.Init` with `BeforeSend` callbacks, `httptest.Server` with real framework routers, `sentry.Flush` to collect events. Prefer tests that use the public API. -2. **Context-level tests** — `NewTestContext` with `MockTransport` for span/transaction behavior when no HTTP server is needed. -3. **Unit tests** (sparingly) — Direct `NewClient` + `MockScope` only for self-contained logic like `BeforeSend` callbacks or sampling decisions. +1. **Integration tests** (default) — Prefer `internal/sentrytest` with `sentrytest.Run` or `sentrytest.NewFixture`, plus real routers / `httptest` requests where needed. Prefer tests that use the public API. +2. **Context-level tests** — Prefer `sentrytest.NewContext` or `fixture.NewContext(parent)` for tracing / context propagation tests. Prefer `sentrytest.NewFixture` for isolated client + hub setup when no HTTP server is needed. +3. **Unit tests** (sparingly) — Direct `NewClient` + `MockScope` only for self-contained logic where `sentrytest` would add unnecessary indirection. Conventions: - Table-driven tests for multiple inputs through the same code path - `t.Parallel()` for tests that don't share global state - `cmp.Diff` with `cmpopts.IgnoreFields` for `*Event` comparison — ignore `EventID`, `Timestamp`, `Sdk`, `sdkMetaData` -- `testutils.FlushTimeout()` when calling `sentry.Flush` (longer timeout in CI) -- `testify` for assertions, `internal/testutils/` for mocks +- Prefer `fixture.Flush()` over direct `sentry.Flush(...)` in tests built on `internal/sentrytest` +- Prefer `fixture.Events()` as the captured event stream; inspect `event.Type` in assertions instead of relying on separate fixture streams +- `testify` for assertions, `internal/testutils/` for non-assert test helpers like mocks and flush timing - All tests must pass `make test-race` What to test: diff --git a/vendor/github.com/getsentry/sentry-go/CHANGELOG.md b/vendor/github.com/getsentry/sentry-go/CHANGELOG.md index ca05ce6cab..a5e966c7fc 100644 --- a/vendor/github.com/getsentry/sentry-go/CHANGELOG.md +++ b/vendor/github.com/getsentry/sentry-go/CHANGELOG.md @@ -1,5 +1,52 @@ # Changelog +## 0.46.2 + +### Bug Fixes 🐛 + +- Add attachments to new event path by @giortzisg in [#1295](https://github.com/getsentry/sentry-go/pull/1295) + +## 0.46.1 + +### Bug Fixes 🐛 + +- Correctly capture request body for fasthttp and fiber. by @giortzisg in [#1284](https://github.com/getsentry/sentry-go/pull/1284) +- (http) Avoid async transport shutdown panics by @giortzisg in [#1288](https://github.com/getsentry/sentry-go/pull/1288) +- (httpclient) Clone request before adding trace headers by @giortzisg in [#1290](https://github.com/getsentry/sentry-go/pull/1290) +- (scope) Use scoped client for request PII by @giortzisg in [#1289](https://github.com/getsentry/sentry-go/pull/1289) +- Safe concurrent access for span and scope by @giortzisg in [#1285](https://github.com/getsentry/sentry-go/pull/1285) + +## 0.46.0 + +### Breaking Changes 🛠 + +- Remove SetExtra by @giortzisg in [#1274](https://github.com/getsentry/sentry-go/pull/1274) +- Update compatibility policy to align with Go, supporting only the last two major Go versions by @giortzisg in [#1264](https://github.com/getsentry/sentry-go/pull/1264) +- Drop support for Go 1.24 by @giortzisg in [#1264](https://github.com/getsentry/sentry-go/pull/1264) + +### New Features ✨ + +- Add internal_sdk_error client report on serialization fail by @giortzisg in [#1273](https://github.com/getsentry/sentry-go/pull/1273) +- Add grpc integration support by @ribice in [#938](https://github.com/getsentry/sentry-go/pull/938) +- Re-enable Telemetry Processor by default. To disable the behavior use the `DisableTelemetryBuffer` flag by @giortzisg in [#1254](https://github.com/getsentry/sentry-go/pull/1254) +- Simplify client DSN storage to `internal/protocol.Dsn` and make it safe to access by @giortzisg in [#1254](https://github.com/getsentry/sentry-go/pull/1254) + +### Internal Changes 🔧 + +#### Deps + +- Bump github.com/labstack/echo/v5 from 5.0.0 to 5.0.3 in /echo by @dependabot in [#1253](https://github.com/getsentry/sentry-go/pull/1253) +- Bump github.com/labstack/echo/v5 from 5.0.0 to 5.0.3 in /crosstest by @dependabot in [#1272](https://github.com/getsentry/sentry-go/pull/1272) +- Bump golangci-lint action from 2.1.1 to 2.11.4 by @giortzisg in [#1265](https://github.com/getsentry/sentry-go/pull/1265) +- Bump go.opentelemetry.io/otel/sdk from 1.40.0 to 1.43.0 in /otel by @dependabot in [#1256](https://github.com/getsentry/sentry-go/pull/1256) +- Bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp from 1.40.0 to 1.43.0 in /otel/otlp by @dependabot in [#1255](https://github.com/getsentry/sentry-go/pull/1255) + +#### Other + +- Improve ci by @giortzisg in [#1271](https://github.com/getsentry/sentry-go/pull/1271) +- Add crosstest package by @giortzisg in [#1269](https://github.com/getsentry/sentry-go/pull/1269) +- Add sentrytest package by @giortzisg in [#1267](https://github.com/getsentry/sentry-go/pull/1267) + ## 0.45.1 ### Bug Fixes 🐛 diff --git a/vendor/github.com/getsentry/sentry-go/Makefile b/vendor/github.com/getsentry/sentry-go/Makefile index 284d3d4ae6..edd17777ba 100644 --- a/vendor/github.com/getsentry/sentry-go/Makefile +++ b/vendor/github.com/getsentry/sentry-go/Makefile @@ -1,121 +1,65 @@ .DEFAULT_GOAL := help -MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) -MKFILE_DIR := $(dir $(MKFILE_PATH)) -ALL_GO_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | sort) GO = go +ALL_GO_MOD_DIRS := $(shell $(GO) work edit -json | jq -r '.Use[].DiskPath') +WORK_LINT_TARGETS := $(patsubst %, %/..., $(ALL_GO_MOD_DIRS)) TIMEOUT = 300 -# Parse Makefile and display the help help: ## Show help @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' .PHONY: help -build: ## Build everything - set -e ; \ - for dir in $(ALL_GO_MOD_DIRS); do \ - MOD_GO=$$(sed -n 's/^go \([0-9]*\.[0-9]*\).*/\1/p' "$${dir}/go.mod"); \ - CURRENT=$$($(GO) env GOVERSION | sed 's/^go//' | cut -d. -f1,2); \ - if [ "$$(printf '%s\n%s' "$${MOD_GO}" "$${CURRENT}" | sort -V | head -n1)" != "$${MOD_GO}" ]; then \ - echo ">>> Skipping $${dir} (requires Go $${MOD_GO}, have $${CURRENT})"; \ - continue; \ - fi; \ - echo ">>> Running 'go build' for module: $${dir}"; \ - (cd "$${dir}" && go build ./...); \ - done; +build: ## Build all workspace modules + $(GO) build work .PHONY: build -### Tests (inspired by https://github.com/open-telemetry/opentelemetry-go/blob/main/Makefile) -TEST_TARGETS := test-short test-verbose test-race -test-race: ARGS=-race -test-short: ARGS=-short -test-verbose: ARGS=-v -race -$(TEST_TARGETS): test -test: $(ALL_GO_MOD_DIRS:%=test/%) ## Run tests -test/%: DIR=$* -test/%: - @MOD_GO=$$(sed -n 's/^go \([0-9]*\.[0-9]*\).*/\1/p' "$(DIR)/go.mod"); \ - CURRENT=$$($(GO) env GOVERSION | sed 's/^go//' | cut -d. -f1,2); \ - if [ "$$(printf '%s\n%s' "$$MOD_GO" "$$CURRENT" | sort -V | head -n1)" != "$$MOD_GO" ]; then \ - echo ">>> Skipping tests for $(DIR) (requires Go $$MOD_GO, have $$CURRENT)"; \ - else \ - echo ">>> Running tests for module: $(DIR)"; \ - (cd $(DIR) && $(GO) test -count=1 -timeout $(TIMEOUT)s $(ARGS) ./...); \ - fi -.PHONY: $(TEST_TARGETS) test +test: ## Run tests across all workspace modules + $(GO) test -count=1 -timeout $(TIMEOUT)s work +.PHONY: test -# Coverage -COVERAGE_MODE = atomic -COVERAGE_PROFILE = coverage.out -COVERAGE_REPORT_DIR = .coverage -COVERAGE_REPORT_DIR_ABS = "$(MKFILE_DIR)/$(COVERAGE_REPORT_DIR)" -$(COVERAGE_REPORT_DIR): - mkdir -p $(COVERAGE_REPORT_DIR) -clean-report-dir: $(COVERAGE_REPORT_DIR) - test $(COVERAGE_REPORT_DIR) && rm -f $(COVERAGE_REPORT_DIR)/* -test-coverage: $(COVERAGE_REPORT_DIR) clean-report-dir ## Test with coverage enabled - set -e ; \ - for dir in $(ALL_GO_MOD_DIRS); do \ - MOD_GO=$$(sed -n 's/^go \([0-9]*\.[0-9]*\).*/\1/p' "$${dir}/go.mod"); \ - CURRENT=$$($(GO) env GOVERSION | sed 's/^go//' | cut -d. -f1,2); \ - if [ "$$(printf '%s\n%s' "$${MOD_GO}" "$${CURRENT}" | sort -V | head -n1)" != "$${MOD_GO}" ]; then \ - echo ">>> Skipping $${dir} (requires Go $${MOD_GO}, have $${CURRENT})"; \ - continue; \ - fi; \ - echo ">>> Running tests with coverage for module: $${dir}"; \ - DIR_ABS=$$(python -c 'import os, sys; print(os.path.realpath(sys.argv[1]))' $${dir}) ; \ - REPORT_NAME=$$(basename $${DIR_ABS}); \ - (cd "$${dir}" && \ - $(GO) test -count=1 -timeout $(TIMEOUT)s -coverpkg=./... -covermode=$(COVERAGE_MODE) -coverprofile="$(COVERAGE_PROFILE)" ./... && \ - cp $(COVERAGE_PROFILE) "$(COVERAGE_REPORT_DIR_ABS)/$${REPORT_NAME}_$(COVERAGE_PROFILE)" && \ - $(GO) tool cover -html=$(COVERAGE_PROFILE) -o coverage.html); \ - done; -.PHONY: test-coverage clean-report-dir -test-race-coverage: $(COVERAGE_REPORT_DIR) clean-report-dir ## Run tests with race detection and coverage - set -e ; \ - for dir in $(ALL_GO_MOD_DIRS); do \ - MOD_GO=$$(sed -n 's/^go \([0-9]*\.[0-9]*\).*/\1/p' "$${dir}/go.mod"); \ - CURRENT=$$($(GO) env GOVERSION | sed 's/^go//' | cut -d. -f1,2); \ - if [ "$$(printf '%s\n%s' "$${MOD_GO}" "$${CURRENT}" | sort -V | head -n1)" != "$${MOD_GO}" ]; then \ - echo ">>> Skipping $${dir} (requires Go $${MOD_GO}, have $${CURRENT})"; \ - continue; \ - fi; \ - echo ">>> Running tests with race detection and coverage for module: $${dir}"; \ - DIR_ABS=$$(python -c 'import os, sys; print(os.path.realpath(sys.argv[1]))' $${dir}) ; \ - REPORT_NAME=$$(basename $${DIR_ABS}); \ - (cd "$${dir}" && \ - $(GO) test -count=1 -timeout $(TIMEOUT)s -race -coverpkg=./... -covermode=$(COVERAGE_MODE) -coverprofile="$(COVERAGE_PROFILE)" ./... && \ - cp $(COVERAGE_PROFILE) "$(COVERAGE_REPORT_DIR_ABS)/$${REPORT_NAME}_$(COVERAGE_PROFILE)" && \ - $(GO) tool cover -html=$(COVERAGE_PROFILE) -o coverage.html); \ - done; +test-race: ## Run tests with race detection + $(GO) test -count=1 -timeout $(TIMEOUT)s -race work +.PHONY: test-race + +COVERAGE_MODE = atomic +COVERAGE_DIR = .coverage +COVERAGE_PROFILE = $(COVERAGE_DIR)/coverage.out + +$(COVERAGE_DIR): + mkdir -p $(COVERAGE_DIR) + +test-coverage: $(COVERAGE_DIR) ## Test with coverage enabled + rm -f $(COVERAGE_DIR)/* + $(GO) test -count=1 -timeout $(TIMEOUT)s -covermode=$(COVERAGE_MODE) -coverprofile=$(COVERAGE_PROFILE) work +.PHONY: test-coverage + +test-race-coverage: $(COVERAGE_DIR) ## Run tests with race detection and coverage + rm -f $(COVERAGE_DIR)/* + $(GO) test -count=1 -timeout $(TIMEOUT)s -race -covermode=$(COVERAGE_MODE) -coverprofile=$(COVERAGE_PROFILE) work .PHONY: test-race-coverage + +vet: ## Run "go vet" across all workspace modules + $(GO) vet work +.PHONY: vet + mod-tidy: ## Check go.mod tidiness - set -e ; \ + @set -e ; \ for dir in $(ALL_GO_MOD_DIRS); do \ MOD_GO=$$(sed -n 's/^go \([0-9.]*\)/\1/p' "$${dir}/go.mod"); \ echo ">>> Running 'go mod tidy' for module: $${dir} (go $${MOD_GO})"; \ - (cd "$${dir}" && GOTOOLCHAIN=local go mod tidy -go=$${MOD_GO} -compat=$${MOD_GO}); \ + (cd "$${dir}" && GOTOOLCHAIN=local $(GO) mod tidy -go=$${MOD_GO} -compat=$${MOD_GO}); \ done; \ - git diff --exit-code; + git diff --exit-code .PHONY: mod-tidy -vet: ## Run "go vet" - set -e ; \ - for dir in $(ALL_GO_MOD_DIRS); do \ - MOD_GO=$$(sed -n 's/^go \([0-9]*\.[0-9]*\).*/\1/p' "$${dir}/go.mod"); \ - CURRENT=$$($(GO) env GOVERSION | sed 's/^go//' | cut -d. -f1,2); \ - if [ "$$(printf '%s\n%s' "$${MOD_GO}" "$${CURRENT}" | sort -V | head -n1)" != "$${MOD_GO}" ]; then \ - echo ">>> Skipping $${dir} (requires Go $${MOD_GO}, have $${CURRENT})"; \ - continue; \ - fi; \ - echo ">>> Running 'go vet' for module: $${dir}"; \ - (cd "$${dir}" && go vet ./...); \ - done; -.PHONY: vet - +gotidy: $(ALL_GO_MOD_DIRS:%=gotidy/%) ## Run go mod tidy across all modules +gotidy/%: DIR=$* +gotidy/%: + @echo "==> $(DIR)" && (cd "$(DIR)" && $(GO) mod tidy) +.PHONY: gotidy lint: ## Lint (using "golangci-lint") - golangci-lint run + golangci-lint run $(WORK_LINT_TARGETS) .PHONY: lint fmt: ## Format all Go files diff --git a/vendor/github.com/getsentry/sentry-go/README.md b/vendor/github.com/getsentry/sentry-go/README.md index 1941fa0646..a44b969688 100644 --- a/vendor/github.com/getsentry/sentry-go/README.md +++ b/vendor/github.com/getsentry/sentry-go/README.md @@ -27,8 +27,9 @@ intended to replace the `raven-go` package. The only requirement is a Go compiler. -We verify this package against the 3 most recent releases of Go. Those are the -supported versions. The exact versions are defined in +We follow Go's [official release policy](https://go.dev/doc/devel/release#policy), +supporting the two most recent Go releases. Each major Go release is supported +until there are two newer major releases. The exact versions are defined in [`GitHub workflow`](.github/workflows/test.yml). In addition, we run tests against the current master branch of the Go toolchain, diff --git a/vendor/github.com/getsentry/sentry-go/baggage.go b/vendor/github.com/getsentry/sentry-go/baggage.go new file mode 100644 index 0000000000..b039fd0caa --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/baggage.go @@ -0,0 +1,52 @@ +package sentry + +import ( + "fmt" + "strings" + + "github.com/getsentry/sentry-go/internal/debuglog" + "github.com/getsentry/sentry-go/internal/otel/baggage" +) + +// MergeBaggage merges an existing baggage header with a Sentry-generated one. +// +// Existing third-party members are preserved. If both baggage strings contain +// the same member key, the Sentry-generated member wins. The helper is best-effort +// and only keeps the sentry baggage in case the existing one is malformed. +func MergeBaggage(existingHeader, sentryHeader string) (string, error) { + // TODO: we are reparsing the headers here, because we currently don't + // expose a method to get only DSC or its baggage members. + sentryBaggage, err := baggage.Parse(sentryHeader) + if err != nil { + return "", fmt.Errorf("cannot parse sentryHeader: %w", err) + } + + existingBaggage, err := baggage.Parse(existingHeader) + if err != nil { + if sentryBaggage.Len() == 0 { + return "", fmt.Errorf("cannot parse existingHeader: %w", err) + } + // in case that the incoming header is malformed we should only + // care about merging sentry related baggage information for distributed tracing. + debuglog.Printf("malformed incoming header: %v", err) + return sentryBaggage.String(), nil + } + + sentryKeys := make(map[string]struct{}, sentryBaggage.Len()) + for _, member := range sentryBaggage.Members() { + sentryKeys[member.Key()] = struct{}{} + } + + parts := make([]string, 0, sentryBaggage.Len()+existingBaggage.Len()) + if s := sentryBaggage.String(); s != "" { + parts = append(parts, s) + } + for _, member := range existingBaggage.Members() { + if _, collides := sentryKeys[member.Key()]; collides { + continue + } + parts = append(parts, member.String()) + } + + return strings.Join(parts, ","), nil +} diff --git a/vendor/github.com/getsentry/sentry-go/batch_processor.go b/vendor/github.com/getsentry/sentry-go/batch_processor.go index 9fcc452bc4..4b938ac05d 100644 --- a/vendor/github.com/getsentry/sentry-go/batch_processor.go +++ b/vendor/github.com/getsentry/sentry-go/batch_processor.go @@ -49,7 +49,7 @@ func (p *batchProcessor[T]) Send(item T) bool { func (p *batchProcessor[T]) Start() { p.startOnce.Do(func() { - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(context.Background()) //nolint:gosec // G118: cancel is stored in p.cancel and called in Shutdown() p.cancel = cancel p.wg.Add(1) go p.run(ctx) diff --git a/vendor/github.com/getsentry/sentry-go/client.go b/vendor/github.com/getsentry/sentry-go/client.go index aa314c9fe3..1e3c65fbf1 100644 --- a/vendor/github.com/getsentry/sentry-go/client.go +++ b/vendor/github.com/getsentry/sentry-go/client.go @@ -294,7 +294,7 @@ type ClientOptions struct { type Client struct { mu sync.RWMutex options ClientOptions - dsn *Dsn + dsn *protocol.Dsn eventProcessors []EventProcessor integrations []Integration externalTraceResolver externalContextTraceResolver @@ -395,10 +395,10 @@ func NewClient(options ClientOptions) (*Client, error) { } } - var dsn *Dsn + var dsn *protocol.Dsn if options.Dsn != "" { var err error - dsn, err = NewDsn(options.Dsn) + dsn, err = protocol.NewDsn(options.Dsn) if err != nil { return nil, err } @@ -419,25 +419,29 @@ func NewClient(options ClientOptions) (*Client, error) { client.reportProvider = a } - client.setupTransport() - - // noop Telemetry Buffers and Processor fow now - // if !options.DisableTelemetryBuffer { - // client.setupTelemetryProcessor() - // } else - if options.EnableLogs { - client.batchLogger = newLogBatchProcessor(&client) - client.batchLogger.Start() - } + // We currently disallow using custom Transport with the new Telemetry Processor, due to the difference in transport signatures. + // The option should be enabled when the new Transport interface signature changes. + if !options.DisableTelemetryBuffer && client.options.Transport == nil { + client.setupTelemetryProcessor() + } else { + if client.options.Transport != nil { + debuglog.Println("Cannot enable Telemetry Processor with custom Transport: fallback to old transport") + } + client.setupTransport() - if !options.DisableMetrics { - client.batchMeter = newMetricBatchProcessor(&client) - client.batchMeter.Start() + if options.EnableLogs { + client.batchLogger = newLogBatchProcessor(&client) + client.batchLogger.Start() + } + if !options.DisableMetrics { + client.batchMeter = newMetricBatchProcessor(&client) + client.batchMeter.Start() + } } + client.setupIntegrations() if options.OrgID != 0 && client.dsn != nil { client.dsn.SetOrgID(options.OrgID) } - client.setupIntegrations() return &client, nil } @@ -474,31 +478,19 @@ func (client *Client) setupTransport() { client.Transport = transport } -func (client *Client) setupTelemetryProcessor() { // nolint: unused - if client.options.DisableTelemetryBuffer { - return - } - - if client.dsn == nil { - debuglog.Println("Telemetry buffer disabled: no DSN configured") - return - } - - // We currently disallow using custom Transport with the new Telemetry Processor, due to the difference in transport signatures. - // The option should be enabled when the new Transport interface signature changes. - if client.options.Transport != nil { - debuglog.Println("Cannot enable Telemetry Processor/Buffers with custom Transport: fallback to old transport") - if client.options.EnableLogs { - client.batchLogger = newLogBatchProcessor(client) - client.batchLogger.Start() - } - if !client.options.DisableMetrics { - client.batchMeter = newMetricBatchProcessor(client) - client.batchMeter.Start() - } - return +func (client *Client) sdkInfo() *protocol.SdkInfo { + return &protocol.SdkInfo{ + Name: client.GetSDKIdentifier(), + Version: SDKVersion, + Integrations: client.listIntegrations(), + Packages: []SdkPackage{{ + Name: "sentry-go", + Version: SDKVersion, + }}, } +} +func (client *Client) setupTelemetryProcessor() { transport := httpInternal.NewAsyncTransport(httpInternal.TransportOptions{ Dsn: client.options.Dsn, HTTPClient: client.options.HTTPClient, @@ -508,6 +500,7 @@ func (client *Client) setupTelemetryProcessor() { // nolint: unused CaCerts: client.options.CaCerts, Recorder: client.reportRecorder, Provider: client.reportProvider, + SdkInfo: client.sdkInfo, }) client.Transport = &internalAsyncTransportAdapter{transport: transport} @@ -519,12 +512,7 @@ func (client *Client) setupTelemetryProcessor() { // nolint: unused ratelimit.CategoryTraceMetric: telemetry.NewRingBuffer[protocol.TelemetryItem](ratelimit.CategoryTraceMetric, 10*100, telemetry.OverflowPolicyDropOldest, 100, 5*time.Second, client.reportRecorder), } - sdkInfo := &protocol.SdkInfo{ - Name: client.sdkIdentifier, - Version: client.sdkVersion, - } - - client.telemetryProcessor = telemetry.NewProcessor(buffers, transport, &client.dsn.Dsn, sdkInfo, client.reportRecorder) + client.telemetryProcessor = telemetry.NewProcessor(buffers, transport, client.dsn, client.sdkInfo, client.reportRecorder) } func (client *Client) setupIntegrations() { diff --git a/vendor/github.com/getsentry/sentry-go/go.work b/vendor/github.com/getsentry/sentry-go/go.work new file mode 100644 index 0000000000..07e2fba241 --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/go.work @@ -0,0 +1,19 @@ +go 1.25.0 + +use ( + . + ./crosstest + ./echo + ./fasthttp + ./fiber + ./gin + ./grpc + ./iris + ./logrus + ./negroni + ./otel + ./otel/otlp + ./slog + ./zap + ./zerolog +) diff --git a/vendor/github.com/getsentry/sentry-go/go.work.sum b/vendor/github.com/getsentry/sentry-go/go.work.sum new file mode 100644 index 0000000000..5eb0ae294c --- /dev/null +++ b/vendor/github.com/getsentry/sentry-go/go.work.sum @@ -0,0 +1,189 @@ +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +codeberg.org/go-fonts/liberation v0.5.0 h1:SsKoMO1v1OZmzkG2DY+7ZkCL9U+rrWI09niOLfQ5Bo0= +codeberg.org/go-fonts/liberation v0.5.0/go.mod h1:zS/2e1354/mJ4pGzIIaEtm/59VFCFnYC7YV6YdGl5GU= +codeberg.org/go-latex/latex v0.1.0 h1:hoGO86rIbWVyjtlDLzCqZPjNykpWQ9YuTZqAzPcfL3c= +codeberg.org/go-latex/latex v0.1.0/go.mod h1:LA0q/AyWIYrqVd+A9Upkgsb+IqPcmSTKc9Dny04MHMw= +codeberg.org/go-pdf/fpdf v0.10.0 h1:u+w669foDDx5Ds43mpiiayp40Ov6sZalgcPMDBcZRd4= +codeberg.org/go-pdf/fpdf v0.10.0/go.mod h1:Y0DGRAdZ0OmnZPvjbMp/1bYxmIPxm0ws4tfoPOc4LjU= +git.sr.ht/~sbinet/gg v0.6.0 h1:RIzgkizAk+9r7uPzf/VfbJHBMKUr0F5hRFxTUGMnt38= +git.sr.ht/~sbinet/gg v0.6.0/go.mod h1:uucygbfC9wVPQIfrmwM2et0imr8L7KQWywX0xpFMm94= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= +github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= +github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= +github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= +github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY= +github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 h1:SKI1/fuSdodxmNNyVBR8d7X/HuLnRpvvFO0AgyQk764= +github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= +github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/djherbis/atime v1.1.0 h1:rgwVbP/5by8BvvjBNrbh64Qz33idKT3pSnMSJsxhi0g= +github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= +github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= +github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= +github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= +github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= +github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= +github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= +github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= +github.com/goccmack/gocc v1.0.2 h1:PHv20lcM1Erz+kovS+c07DnDFp6X5cvghndtTXuEyfE= +github.com/goccmack/gocc v1.0.2/go.mod h1:LXX2tFVUggS/Zgx/ICPOr3MLyusuM7EcbfkPvNsjdO8= +github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I= +github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= +github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/iris-contrib/go.uuid v2.0.0+incompatible h1:XZubAYg61/JwnJNbZilGjf3b3pB80+OQg2qf6c8BfWE= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/kataras/jwt v0.1.8 h1:u71baOsYD22HWeSOg32tCHbczPjdCk7V4MMeJqTtmGk= +github.com/kataras/jwt v0.1.8/go.mod h1:Q5j2IkcIHnfwy+oNY3TVWuEBJNw0ADgCcXK9CaZwV4o= +github.com/kataras/neffos v0.0.21 h1:UwN/F44jlqdtgFI29y3VhA7IlJ4JbK3UjCbTDg1pYoo= +github.com/kataras/neffos v0.0.21/go.mod h1:FeGka8lu8cjD2H+0OpBvW8c6xXawy3fj5VX6xcIJ1Fg= +github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lyft/protoc-gen-star/v2 v2.0.4-0.20230330145011-496ad1ac90a4 h1:sIXJOMrYnQZJu7OB7ANSF4MYri2fTEGIsRLz6LwI4xE= +github.com/lyft/protoc-gen-star/v2 v2.0.4-0.20230330145011-496ad1ac90a4/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= +github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2 h1:JAEbJn3j/FrhdWA9jW8B5ajsLIjeuEHLi8xE4fk997o= +github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= +github.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M= +github.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/nats-io/jwt/v2 v2.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI= +github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= +github.com/nats-io/nats.go v1.23.0 h1:lR28r7IX44WjYgdiKz9GmUeW0uh/m33uD3yEjLZ2cOE= +github.com/nats-io/nats.go v1.23.0/go.mod h1:ki/Scsa23edbh8IRZbCuNXR9TDcbvfaSijKtaqQgw+Q= +github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= +github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +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/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/shirou/gopsutil/v3 v3.23.2 h1:PAWSuiAszn7IhPMBtXsbSCafej7PqUOvY6YywlQUExU= +github.com/shirou/gopsutil/v3 v3.23.2/go.mod h1:gv0aQw33GLo3pG8SiWKiQrbDzbRY1K80RyZJ7V4Th1M= +github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= +github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502 h1:34icjjmqJ2HPjrSuJYEkdZ+0ItmGQAQ75cRHIiftIyE= +github.com/tdewolff/argp v0.0.0-20240126212256-acdb2fb50090 h1:ok0U1tNDp9ICD93bMrZuFtHLwDoW+5nfSfF8e/x36Y0= +github.com/tdewolff/argp v0.0.0-20240126212256-acdb2fb50090/go.mod h1:fF+gnKbmf3iMG+ErLiF+orMU/InyZIEnKVVigUjfriw= +github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po= +github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= +github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= +github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= +github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= +github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= +github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= +github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= +go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE= +go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk= +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.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ= +golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= +golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 h1:bTLqdHv7xrGlFbvf5/TXNxy/iUwwdkjhqQTJDjW7aj0= +golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4/go.mod h1:g5NllXBEermZrmR51cJDQxmJUHUOfRAaNyWBM+R+548= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +gonum.org/v1/plot v0.15.2 h1:Tlfh/jBk2tqjLZ4/P8ZIwGrLEWQSPDLRm/SNWKNXiGI= +gonum.org/v1/plot v0.15.2/go.mod h1:DX+x+DWso3LTha+AdkJEv5Txvi+Tql3KAGkehP0/Ubg= +gonum.org/v1/tools v0.0.0-20200318103217-c168b003ce8c h1:cJWOvXtcaFSGXz2F4z2AMM0VV7edDDGrxb5GLQH7ayQ= +gonum.org/v1/tools v0.0.0-20200318103217-c168b003ce8c/go.mod h1:fy6Otjqbk477ELp8IXTpw1cObQtLbRCBVonY+bTTfcM= +google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:p3MLuOwURrGBRoEyFHBT3GjUwaCQVKeNqqWxlcISGdw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260401001100-f93e5f3e9f0f/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc/examples v0.0.0-20250407062114-b368379ef8f6 h1:ExN12ndbJ608cboPYflpTny6mXSzPrDLh0iTaVrRrds= +google.golang.org/grpc/examples v0.0.0-20250407062114-b368379ef8f6/go.mod h1:6ytKWczdvnpnO+m+JiG9NjEDzR1FJfsnmJdG7B8QVZ8= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= +rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= diff --git a/vendor/github.com/getsentry/sentry-go/hub.go b/vendor/github.com/getsentry/sentry-go/hub.go index 6d14536840..15420a6471 100644 --- a/vendor/github.com/getsentry/sentry-go/hub.go +++ b/vendor/github.com/getsentry/sentry-go/hub.go @@ -390,23 +390,22 @@ func (hub *Hub) FlushWithContext(ctx context.Context) bool { // on the current span, or the scope's propagation context. func (hub *Hub) GetTraceparent() string { scope := hub.Scope() - - if scope.span != nil { - return scope.span.ToSentryTrace() + if span := scope.GetSpan(); span != nil { + return span.ToSentryTrace() } - - return fmt.Sprintf("%s-%s", scope.propagationContext.TraceID, scope.propagationContext.SpanID) + propagationContext := scope.propagationContextSnapshot() + return fmt.Sprintf("%s-%s", propagationContext.TraceID, propagationContext.SpanID) } // GetTraceparentW3C returns the current traceparent string in W3C format. // This is intended for propagation to downstream services that expect the W3C header. func (hub *Hub) GetTraceparentW3C() string { scope := hub.Scope() - if scope.span != nil { - return scope.span.ToTraceparent() + if span := scope.GetSpan(); span != nil { + return span.ToTraceparent() } - - return fmt.Sprintf("00-%s-%s-00", scope.propagationContext.TraceID, scope.propagationContext.SpanID) + propagationContext := scope.propagationContextSnapshot() + return fmt.Sprintf("00-%s-%s-00", propagationContext.TraceID, propagationContext.SpanID) } // GetBaggage returns the current Sentry baggage string, to be used as a HTTP header value @@ -415,12 +414,10 @@ func (hub *Hub) GetTraceparentW3C() string { // on the current span or the scope's propagation context. func (hub *Hub) GetBaggage() string { scope := hub.Scope() - - if scope.span != nil { - return scope.span.ToBaggage() + if span := scope.GetSpan(); span != nil { + return span.ToBaggage() } - - return scope.propagationContext.DynamicSamplingContext.String() + return scope.propagationContextSnapshot().DynamicSamplingContext.String() } // HasHubOnContext checks whether Hub instance is bound to a given Context struct. diff --git a/vendor/github.com/getsentry/sentry-go/interfaces.go b/vendor/github.com/getsentry/sentry-go/interfaces.go index 7916b1fe7b..304bf08ae5 100644 --- a/vendor/github.com/getsentry/sentry-go/interfaces.go +++ b/vendor/github.com/getsentry/sentry-go/interfaces.go @@ -247,11 +247,11 @@ type Request struct { Env map[string]string `json:"env,omitempty"` } -// NewRequest returns a new Sentry Request from the given http.Request. -// -// NewRequest avoids operations that depend on network access. In particular, it -// does not read r.Body. -func NewRequest(r *http.Request) *Request { +func sendDefaultPIIEnabled(client *Client) bool { + return client != nil && client.options.SendDefaultPII +} + +func newRequest(r *http.Request, sendDefaultPII bool) *Request { prot := protocol.SchemeHTTP if r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https" { prot = protocol.SchemeHTTPS @@ -262,7 +262,7 @@ func NewRequest(r *http.Request) *Request { var env map[string]string headers := map[string]string{} - if client := CurrentHub().Client(); client != nil && client.options.SendDefaultPII { + if sendDefaultPII { // We read only the first Cookie header because of the specification: // https://tools.ietf.org/html/rfc6265#section-5.4 // When the user agent generates an HTTP request, the user agent MUST NOT @@ -297,6 +297,14 @@ func NewRequest(r *http.Request) *Request { } } +// NewRequest returns a new Sentry Request from the given http.Request. +// +// NewRequest avoids operations that depend on network access. In particular, it +// does not read r.Body. +func NewRequest(r *http.Request) *Request { + return newRequest(r, sendDefaultPIIEnabled(CurrentHub().Client())) +} + // Mechanism is the mechanism by which an exception was generated and handled. type Mechanism struct { Type string `json:"type"` @@ -373,30 +381,29 @@ type Context = map[string]interface{} // Event is the fundamental data structure that is sent to Sentry. type Event struct { - Breadcrumbs []*Breadcrumb `json:"breadcrumbs,omitempty"` - Contexts map[string]Context `json:"contexts,omitempty"` - Dist string `json:"dist,omitempty"` - Environment string `json:"environment,omitempty"` - EventID EventID `json:"event_id,omitempty"` - Extra map[string]interface{} `json:"extra,omitempty"` - Fingerprint []string `json:"fingerprint,omitempty"` - Level Level `json:"level,omitempty"` - Message string `json:"message,omitempty"` - Platform string `json:"platform,omitempty"` - Release string `json:"release,omitempty"` - Sdk SdkInfo `json:"sdk,omitempty"` - ServerName string `json:"server_name,omitempty"` - Threads []Thread `json:"threads,omitempty"` - Tags map[string]string `json:"tags,omitempty"` - Timestamp time.Time `json:"timestamp,omitzero"` - Transaction string `json:"transaction,omitempty"` - User User `json:"user,omitempty"` - Logger string `json:"logger,omitempty"` - Modules map[string]string `json:"modules,omitempty"` - Request *Request `json:"request,omitempty"` - Exception []Exception `json:"exception,omitempty"` - DebugMeta *DebugMeta `json:"debug_meta,omitempty"` - Attachments []*Attachment `json:"-"` + Breadcrumbs []*Breadcrumb `json:"breadcrumbs,omitempty"` + Contexts map[string]Context `json:"contexts,omitempty"` + Dist string `json:"dist,omitempty"` + Environment string `json:"environment,omitempty"` + EventID EventID `json:"event_id,omitempty"` + Fingerprint []string `json:"fingerprint,omitempty"` + Level Level `json:"level,omitempty"` + Message string `json:"message,omitempty"` + Platform string `json:"platform,omitempty"` + Release string `json:"release,omitempty"` + Sdk SdkInfo `json:"sdk,omitempty"` + ServerName string `json:"server_name,omitempty"` + Threads []Thread `json:"threads,omitempty"` + Tags map[string]string `json:"tags,omitempty"` + Timestamp time.Time `json:"timestamp,omitzero"` + Transaction string `json:"transaction,omitempty"` + User User `json:"user,omitempty"` + Logger string `json:"logger,omitempty"` + Modules map[string]string `json:"modules,omitempty"` + Request *Request `json:"request,omitempty"` + Exception []Exception `json:"exception,omitempty"` + DebugMeta *DebugMeta `json:"debug_meta,omitempty"` + Attachments []*Attachment `json:"-"` // The fields below are only relevant for transactions. @@ -421,11 +428,12 @@ type Event struct { sdkMetaData SDKMetaData // Pre-serialized copies of mutable fields, set by MakeSerializationSafe. - serializedExtra json.RawMessage + serializedTags json.RawMessage serializedContexts json.RawMessage serializedBreadcrumbs json.RawMessage serializedException json.RawMessage serializedUser json.RawMessage + serializationSafe bool } // SetException appends the unwrapped errors to the event's exception list. @@ -463,28 +471,7 @@ func (e *Event) safeMarshal() (b []byte, err error) { func (e *Event) ToEnvelopeItem() (item *protocol.EnvelopeItem, err error) { eventBody, err := e.safeMarshal() if err != nil { - // Try fallback: remove problematic fields and retry. - // Clear both original and pre-serialized versions. - e.Breadcrumbs = nil - e.serializedBreadcrumbs = nil - e.Contexts = nil - e.serializedContexts = nil - e.serializedException = nil - e.serializedUser = nil - e.serializedExtra = nil - e.Extra = map[string]interface{}{ - "info": fmt.Sprintf("Could not encode original event as JSON. "+ - "Succeeded by removing Breadcrumbs, Contexts and Extra. "+ - "Please verify the data you attach to the scope. "+ - "Error: %s", err), - } - - eventBody, err = json.Marshal(e) - if err != nil { - return nil, fmt.Errorf("event could not be marshaled even with fallback: %w", err) - } - - DebugLogger.Printf("Event marshaling succeeded with fallback after removing problematic fields") + return nil, fmt.Errorf("could not encode event as JSON, skipping delivery: %w", err) } // TODO: all event types should be abstracted to implement EnvelopeItemConvertible and convert themselves. @@ -504,6 +491,24 @@ func (e *Event) ToEnvelopeItem() (item *protocol.EnvelopeItem, err error) { return item, nil } +// ToEnvelope converts the Event to a Sentry envelope. +func (e *Event) ToEnvelope(header *protocol.EnvelopeHeader) (*protocol.Envelope, error) { + item, err := e.ToEnvelopeItem() + if err != nil { + return nil, err + } + + envelope := protocol.NewEnvelope(header) + envelope.AddItem(item) + + for _, attachment := range e.Attachments { + attachmentItem := protocol.NewAttachmentItem(attachment.Filename, attachment.ContentType, attachment.Payload) + envelope.AddItem(attachmentItem) + } + + return envelope, nil +} + // GetCategory returns the rate limit category for this event. func (e *Event) GetCategory() ratelimit.Category { return e.toCategory() @@ -618,11 +623,7 @@ func (e *Event) defaultMarshalJSON() ([]byte, error) { } func (e *Event) hasPreSerializedFields() bool { - return e.serializedExtra != nil || - e.serializedContexts != nil || - e.serializedBreadcrumbs != nil || - e.serializedException != nil || - e.serializedUser != nil + return e.serializationSafe } // preSerializedMarshalJSON handles marshaling when MakeSerializationSafe has @@ -634,7 +635,7 @@ func (e *Event) preSerializedMarshalJSON() ([]byte, error) { if e.Type == transactionType { type safeTransaction struct { *event - Extra json.RawMessage `json:"extra,omitempty"` + Tags json.RawMessage `json:"tags,omitempty"` Contexts json.RawMessage `json:"contexts,omitempty"` Breadcrumbs json.RawMessage `json:"breadcrumbs,omitempty"` Exception json.RawMessage `json:"exception,omitempty"` @@ -642,7 +643,7 @@ func (e *Event) preSerializedMarshalJSON() ([]byte, error) { } return json.Marshal(safeTransaction{ event: (*event)(e), - Extra: e.serializedExtra, + Tags: e.serializedTags, Contexts: e.serializedContexts, Breadcrumbs: e.serializedBreadcrumbs, Exception: e.serializedException, @@ -653,7 +654,7 @@ func (e *Event) preSerializedMarshalJSON() ([]byte, error) { // Error event: also shadow transaction-only fields to exclude them. type safeErrorEvent struct { *event - Extra json.RawMessage `json:"extra,omitempty"` + Tags json.RawMessage `json:"tags,omitempty"` Contexts json.RawMessage `json:"contexts,omitempty"` Breadcrumbs json.RawMessage `json:"breadcrumbs,omitempty"` Exception json.RawMessage `json:"exception,omitempty"` @@ -665,7 +666,7 @@ func (e *Event) preSerializedMarshalJSON() ([]byte, error) { } return json.Marshal(safeErrorEvent{ event: (*event)(e), - Extra: e.serializedExtra, + Tags: e.serializedTags, Contexts: e.serializedContexts, Breadcrumbs: e.serializedBreadcrumbs, Exception: e.serializedException, @@ -719,7 +720,6 @@ func (e *Event) toCategory() ratelimit.Category { func NewEvent() *Event { return &Event{ Contexts: make(map[string]Context), - Extra: make(map[string]interface{}), Tags: make(map[string]string), Modules: make(map[string]string), } @@ -897,9 +897,9 @@ func (v MetricValue) MarshalJSON() ([]byte, error) { // MakeSerializationSafe pre-serializes all fields containing user mutable data to json.RawMessage, preventing race // conditions when the event is later serialized on a background goroutine. func (e *Event) MakeSerializationSafe() { - if len(e.Extra) > 0 { - if b, err := json.Marshal(e.Extra); err == nil { - e.serializedExtra = b + if len(e.Tags) > 0 { + if b, err := json.Marshal(e.Tags); err == nil { + e.serializedTags = b } } @@ -926,4 +926,10 @@ func (e *Event) MakeSerializationSafe() { e.serializedUser = b } } + + for _, span := range e.Spans { + span.makeSerializationSafe() + } + + e.serializationSafe = true } diff --git a/vendor/github.com/getsentry/sentry-go/internal/http/transport.go b/vendor/github.com/getsentry/sentry-go/internal/http/transport.go index a8425859d8..8dbfe03499 100644 --- a/vendor/github.com/getsentry/sentry-go/internal/http/transport.go +++ b/vendor/github.com/getsentry/sentry-go/internal/http/transport.go @@ -42,6 +42,7 @@ type TransportOptions struct { CaCerts *x509.CertPool Recorder report.ClientReportRecorder Provider report.ClientReportProvider + SdkInfo func() *protocol.SdkInfo } func getProxyConfig(options TransportOptions) func(*http.Request) (*url.URL, error) { @@ -74,8 +75,11 @@ func getTLSConfig(options TransportOptions) *tls.Config { func getSentryRequestFromEnvelope(ctx context.Context, dsn *protocol.Dsn, envelope *protocol.Envelope) (r *http.Request, err error) { defer func() { if r != nil { - sdkName := envelope.Header.Sdk.Name - sdkVersion := envelope.Header.Sdk.Version + var sdkName, sdkVersion string + if envelope.Header.Sdk != nil { + sdkVersion = envelope.Header.Sdk.Version + sdkName = envelope.Header.Sdk.Name + } r.Header.Set("User-Agent", fmt.Sprintf("%s/%s", sdkName, sdkVersion)) r.Header.Set("Content-Type", "application/x-sentry-envelope") @@ -151,6 +155,7 @@ type SyncTransport struct { transport http.RoundTripper recorder report.ClientReportRecorder provider report.ClientReportProvider + sdkInfo func() *protocol.SdkInfo mu sync.Mutex limits ratelimit.Map @@ -180,6 +185,7 @@ func NewSyncTransport(options TransportOptions) protocol.TelemetryTransport { dsn: dsn, recorder: recorder, provider: provider, + sdkInfo: options.SdkInfo, } if options.HTTPTransport != nil { @@ -288,6 +294,7 @@ type AsyncTransport struct { transport http.RoundTripper recorder report.ClientReportRecorder provider report.ClientReportProvider + sdkInfo func() *protocol.SdkInfo queue chan *protocol.Envelope @@ -299,6 +306,8 @@ type AsyncTransport struct { flushRequest chan chan struct{} + closeMu sync.RWMutex + QueueSize int Timeout time.Duration @@ -330,6 +339,7 @@ func NewAsyncTransport(options TransportOptions) protocol.TelemetryTransport { dsn: dsn, recorder: recorder, provider: provider, + sdkInfo: options.SdkInfo, } transport.queue = make(chan *protocol.Envelope, transport.QueueSize) @@ -384,6 +394,9 @@ func (t *AsyncTransport) HasCapacity() bool { } func (t *AsyncTransport) SendEnvelope(envelope *protocol.Envelope) error { + t.closeMu.RLock() + defer t.closeMu.RUnlock() + select { case <-t.done: return ErrTransportClosed @@ -403,6 +416,8 @@ func (t *AsyncTransport) SendEnvelope(envelope *protocol.Envelope) error { identifier := util.EnvelopeIdentifier(envelope) select { + case <-t.done: + return ErrTransportClosed case t.queue <- envelope: debuglog.Printf( "Sending %s to %s project: %s", @@ -424,8 +439,14 @@ func (t *AsyncTransport) Flush(timeout time.Duration) bool { } func (t *AsyncTransport) FlushWithContext(ctx context.Context) bool { + t.closeMu.RLock() + defer t.closeMu.RUnlock() + flushResponse := make(chan struct{}) select { + case <-t.done: + debuglog.Println("Failed to flush, transport is closed.") + return false case t.flushRequest <- flushResponse: select { case <-flushResponse: @@ -443,9 +464,10 @@ func (t *AsyncTransport) FlushWithContext(ctx context.Context) bool { func (t *AsyncTransport) Close() { t.closeOnce.Do(func() { + t.closeMu.Lock() + defer t.closeMu.Unlock() + close(t.done) - close(t.queue) - close(t.flushRequest) t.wg.Wait() }) } @@ -454,6 +476,13 @@ func (t *AsyncTransport) IsRateLimited(category ratelimit.Category) bool { return t.isRateLimited(category) } +func (t *AsyncTransport) resolveSdkInfo() *protocol.SdkInfo { + if t.sdkInfo == nil { + return &protocol.SdkInfo{} + } + return t.sdkInfo() +} + func (t *AsyncTransport) worker() { defer t.wg.Done() @@ -494,7 +523,8 @@ func (t *AsyncTransport) sendClientReport() { } header := &protocol.EnvelopeHeader{ SentAt: time.Now(), - Dsn: t.dsn.String(), + Dsn: t.dsn, + Sdk: t.resolveSdkInfo(), } envelope := protocol.NewEnvelope(header) envelope.AddItem(item) diff --git a/vendor/github.com/getsentry/sentry-go/internal/protocol/envelope.go b/vendor/github.com/getsentry/sentry-go/internal/protocol/envelope.go index a7c64a09d5..ae6cdee5b0 100644 --- a/vendor/github.com/getsentry/sentry-go/internal/protocol/envelope.go +++ b/vendor/github.com/getsentry/sentry-go/internal/protocol/envelope.go @@ -26,7 +26,7 @@ type EnvelopeHeader struct { // Dsn can be used for self-authenticated envelopes. // This means that the envelope has all the information necessary to be sent to sentry. // In this case the full DSN must be stored in this key. - Dsn string `json:"dsn,omitempty"` + Dsn *Dsn `json:"dsn,omitempty"` // Sdk carries the same payload as the sdk interface in the event payload but can be carried for all events. // This means that SDK information can be carried for minidumps, session data and other submissions. diff --git a/vendor/github.com/getsentry/sentry-go/internal/protocol/interfaces.go b/vendor/github.com/getsentry/sentry-go/internal/protocol/interfaces.go index d66c726070..f9f8e9a9d2 100644 --- a/vendor/github.com/getsentry/sentry-go/internal/protocol/interfaces.go +++ b/vendor/github.com/getsentry/sentry-go/internal/protocol/interfaces.go @@ -35,6 +35,14 @@ type EnvelopeItemConvertible interface { ToEnvelopeItem() (*EnvelopeItem, error) } +// EnvelopeConvertible represents items that can convert themselves to complete envelopes. +type EnvelopeConvertible interface { + TelemetryItem + + // ToEnvelope converts the item to a Sentry envelope using the provided header. + ToEnvelope(*EnvelopeHeader) (*Envelope, error) +} + // TelemetryTransport represents the envelope-first transport interface. // This interface is designed for the telemetry buffer system and provides // non-blocking sends with backpressure signals. diff --git a/vendor/github.com/getsentry/sentry-go/internal/telemetry/processor.go b/vendor/github.com/getsentry/sentry-go/internal/telemetry/processor.go index 478f2fa67e..1ad3900f03 100644 --- a/vendor/github.com/getsentry/sentry-go/internal/telemetry/processor.go +++ b/vendor/github.com/getsentry/sentry-go/internal/telemetry/processor.go @@ -19,7 +19,7 @@ func NewProcessor( buffers map[ratelimit.Category]Buffer[protocol.TelemetryItem], transport protocol.TelemetryTransport, dsn *protocol.Dsn, - sdkInfo *protocol.SdkInfo, + sdkInfo func() *protocol.SdkInfo, recorder report.ClientReportRecorder, ) *Processor { scheduler := NewScheduler(buffers, transport, dsn, sdkInfo, recorder) diff --git a/vendor/github.com/getsentry/sentry-go/internal/telemetry/scheduler.go b/vendor/github.com/getsentry/sentry-go/internal/telemetry/scheduler.go index 15a27a9cb7..d1fb4ee609 100644 --- a/vendor/github.com/getsentry/sentry-go/internal/telemetry/scheduler.go +++ b/vendor/github.com/getsentry/sentry-go/internal/telemetry/scheduler.go @@ -16,7 +16,7 @@ type Scheduler struct { buffers map[ratelimit.Category]Buffer[protocol.TelemetryItem] transport protocol.TelemetryTransport dsn *protocol.Dsn - sdkInfo *protocol.SdkInfo + sdkInfo func() *protocol.SdkInfo recorder report.ClientReportRecorder currentCycle []ratelimit.Priority @@ -36,14 +36,14 @@ func NewScheduler( buffers map[ratelimit.Category]Buffer[protocol.TelemetryItem], transport protocol.TelemetryTransport, dsn *protocol.Dsn, - sdkInfo *protocol.SdkInfo, + sdkInfo func() *protocol.SdkInfo, recorder report.ClientReportRecorder, ) *Scheduler { if recorder == nil { recorder = report.NoopRecorder() } - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(context.Background()) //nolint:gosec // G118: cancel is stored in s.cancel and called in Shutdown() priorityWeights := map[ratelimit.Priority]int{ ratelimit.PriorityCritical: 5, @@ -85,6 +85,13 @@ func NewScheduler( return s } +func (s *Scheduler) resolveSdkInfo() *protocol.SdkInfo { + if s.sdkInfo == nil { + return &protocol.SdkInfo{} + } + return s.sdkInfo() +} + func (s *Scheduler) Start() { s.startOnce.Do(func() { s.processingWg.Add(1) @@ -237,10 +244,7 @@ func (s *Scheduler) processItems(buffer Buffer[protocol.TelemetryItem], category switch category { case ratelimit.CategoryLog: logs := protocol.Logs(items) - header := &protocol.EnvelopeHeader{EventID: protocol.GenerateEventID(), SentAt: time.Now(), Sdk: s.sdkInfo} - if s.dsn != nil { - header.Dsn = s.dsn.String() - } + header := &protocol.EnvelopeHeader{EventID: protocol.GenerateEventID(), SentAt: time.Now(), Dsn: s.dsn, Sdk: s.resolveSdkInfo()} envelope := protocol.NewEnvelope(header) item, err := logs.ToEnvelopeItem() if err != nil { @@ -254,10 +258,7 @@ func (s *Scheduler) processItems(buffer Buffer[protocol.TelemetryItem], category return case ratelimit.CategoryTraceMetric: metrics := protocol.Metrics(items) - header := &protocol.EnvelopeHeader{EventID: protocol.GenerateEventID(), SentAt: time.Now(), Sdk: s.sdkInfo} - if s.dsn != nil { - header.Dsn = s.dsn.String() - } + header := &protocol.EnvelopeHeader{EventID: protocol.GenerateEventID(), SentAt: time.Now(), Dsn: s.dsn, Sdk: s.resolveSdkInfo()} envelope := protocol.NewEnvelope(header) item, err := metrics.ToEnvelopeItem() if err != nil { @@ -287,22 +288,39 @@ func (s *Scheduler) sendItem(item protocol.EnvelopeItemConvertible) { header := &protocol.EnvelopeHeader{ EventID: item.GetEventID(), SentAt: time.Now(), + Dsn: s.dsn, Trace: item.GetDynamicSamplingContext(), - Sdk: s.sdkInfo, + Sdk: s.resolveSdkInfo(), } if header.EventID == "" { header.EventID = protocol.GenerateEventID() } - if s.dsn != nil { - header.Dsn = s.dsn.String() - } - envelope := protocol.NewEnvelope(header) - envItem, err := item.ToEnvelopeItem() - if err != nil { - debuglog.Printf("error while converting to envelope item: %v", err) - return + + // TODO: need to restructure the abstraction layer to actually return envelopes instead of + // envelope items. The problem with envelope items, is that for events and batches we might + // need envelopes (eg. attachments need to be added separately and splitting the `Add` path + // is problematic). Currently this is a workaround for adding attachments to events without + // adding a circular dependency on telemetry/scheduler. + var envelope *protocol.Envelope + if convertible, ok := item.(protocol.EnvelopeConvertible); ok { + var err error + envelope, err = convertible.ToEnvelope(header) + if err != nil { + debuglog.Printf("error while converting to envelope: %v", err) + s.recorder.RecordItem(report.ReasonInternalError, item) + return + } + } else { + envItem, err := item.ToEnvelopeItem() + if err != nil { + debuglog.Printf("error while converting to envelope item: %v", err) + s.recorder.RecordItem(report.ReasonInternalError, item) + return + } + envelope = protocol.NewEnvelope(header) + envelope.AddItem(envItem) } - envelope.AddItem(envItem) + if err := s.transport.SendEnvelope(envelope); err != nil { debuglog.Printf("error sending envelope: %v", err) } diff --git a/vendor/github.com/getsentry/sentry-go/internal/util/util.go b/vendor/github.com/getsentry/sentry-go/internal/util/util.go index 4d5d2e1cc9..534e77c98f 100644 --- a/vendor/github.com/getsentry/sentry-go/internal/util/util.go +++ b/vendor/github.com/getsentry/sentry-go/internal/util/util.go @@ -99,7 +99,7 @@ func (r *SendResult) IsSendError() bool { // DoSendRequest executes an HTTP request, handles response logging, extracts rate limits, and // drains+closes the response body. func DoSendRequest(client *http.Client, request *http.Request, identifier string) (*SendResult, error) { - response, err := client.Do(request) + response, err := client.Do(request) //nolint:gosec // G704: request is constructed internally, not from user input if err != nil { return nil, err } diff --git a/vendor/github.com/getsentry/sentry-go/scope.go b/vendor/github.com/getsentry/sentry-go/scope.go index a9c51969c1..118fb8dbc5 100644 --- a/vendor/github.com/getsentry/sentry-go/scope.go +++ b/vendor/github.com/getsentry/sentry-go/scope.go @@ -37,7 +37,6 @@ type Scope struct { user User tags map[string]string contexts map[string]Context - extra map[string]interface{} fingerprint []string level Level request *http.Request @@ -64,7 +63,6 @@ func NewScope() *Scope { attachments: make([]*Attachment, 0), tags: make(map[string]string), contexts: make(map[string]Context), - extra: make(map[string]interface{}), fingerprint: make([]string, 0), propagationContext: NewPropagationContext(), } @@ -282,44 +280,6 @@ func (scope *Scope) RemoveContext(key string) { delete(scope.contexts, key) } -// SetExtra adds an extra to the current scope. -// -// Deprecated: Use [Scope.SetAttributes] instead, which attaches typed key-value -// pairs to logs and metrics. Note that attributes do not attach to error events; -// if you only capture errors, use [Scope.SetTag] or [Scope.SetContext] to enrich them. -func (scope *Scope) SetExtra(key string, value interface{}) { - scope.mu.Lock() - defer scope.mu.Unlock() - - scope.extra[key] = value -} - -// SetExtras assigns multiple extras to the current scope. -// -// Deprecated: Use [Scope.SetAttributes] instead, which attaches typed key-value -// pairs to logs and metrics. Note that attributes do not attach to error events; -// if you only capture errors, use [Scope.SetTag] or [Scope.SetContext] to enrich them. -func (scope *Scope) SetExtras(extra map[string]interface{}) { - scope.mu.Lock() - defer scope.mu.Unlock() - - for k, v := range extra { - scope.extra[k] = v - } -} - -// RemoveExtra removes a extra from the current scope. -// -// Deprecated: Use [Scope.RemoveAttribute] instead. Note that attributes only -// attach to logs and metrics, not error events. If you only capture errors, -// use [Scope.RemoveTag] or [Scope.RemoveContext] instead. -func (scope *Scope) RemoveExtra(key string) { - scope.mu.Lock() - defer scope.mu.Unlock() - - delete(scope.extra, key) -} - // SetFingerprint sets new fingerprint for the current scope. func (scope *Scope) SetFingerprint(fingerprint []string) { scope.mu.Lock() @@ -344,6 +304,13 @@ func (scope *Scope) SetPropagationContext(propagationContext PropagationContext) scope.propagationContext = propagationContext } +func (scope *Scope) propagationContextSnapshot() PropagationContext { + scope.mu.RLock() + defer scope.mu.RUnlock() + + return scope.propagationContext +} + // GetSpan returns the span from the current scope. func (scope *Scope) GetSpan() *Span { scope.mu.RLock() @@ -374,7 +341,6 @@ func (scope *Scope) Clone() *Scope { clone.attributes = maps.Clone(scope.attributes) clone.contexts = maps.Clone(scope.contexts) clone.tags = maps.Clone(scope.tags) - clone.extra = maps.Clone(scope.extra) clone.fingerprint = make([]string, len(scope.fingerprint)) copy(clone.fingerprint, scope.fingerprint) clone.level = scope.level @@ -484,16 +450,6 @@ func (scope *Scope) ApplyToEvent(event *Event, hint *EventHint, client *Client) } } - if len(scope.extra) > 0 { - if event.Extra == nil { - event.Extra = make(map[string]interface{}, len(scope.extra)) - } - - for key, value := range scope.extra { - event.Extra[key] = value - } - } - if event.User.IsEmpty() { event.User = scope.user } @@ -507,7 +463,7 @@ func (scope *Scope) ApplyToEvent(event *Event, hint *EventHint, client *Client) } if event.Request == nil && scope.request != nil { - event.Request = NewRequest(scope.request) + event.Request = newRequest(scope.request, sendDefaultPIIEnabled(client)) // NOTE: The SDK does not attempt to send partial request body data. // // The reason being that Sentry's ingest pipeline and UI are optimized @@ -516,7 +472,7 @@ func (scope *Scope) ApplyToEvent(event *Event, hint *EventHint, client *Client) // invalid payloads that are more prone to leaking PII data. // // Users can still send more data along their events if they want to, - // for example using Event.Extra. + // for example using Event.Contexts. if scope.requestBody != nil && !scope.requestBody.Overflow() { event.Request.Data = string(scope.requestBody.Bytes()) } diff --git a/vendor/github.com/getsentry/sentry-go/sentry.go b/vendor/github.com/getsentry/sentry-go/sentry.go index cb87609589..9694f52ba5 100644 --- a/vendor/github.com/getsentry/sentry-go/sentry.go +++ b/vendor/github.com/getsentry/sentry-go/sentry.go @@ -6,12 +6,15 @@ import ( ) // The version of the SDK. -const SDKVersion = "0.45.1" +const SDKVersion = "0.46.2" // apiVersion is the minimum version of the Sentry API compatible with the // sentry-go SDK. const apiVersion = "7" +// DefaultFlushTimeout is the default timeout used for flushing events. +const DefaultFlushTimeout = 2 * time.Second + // Init initializes the SDK with options. The returned error is non-nil if // options is invalid, for instance if a malformed DSN is provided. func Init(options ClientOptions) error { diff --git a/vendor/github.com/getsentry/sentry-go/tracing.go b/vendor/github.com/getsentry/sentry-go/tracing.go index 0ac680a417..475a8a4a11 100644 --- a/vendor/github.com/getsentry/sentry-go/tracing.go +++ b/vendor/github.com/getsentry/sentry-go/tracing.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "maps" "net/http" "regexp" "strconv" @@ -36,6 +37,7 @@ const ( SpanOriginStdLib = "auto.http.stdlib" SpanOriginIris = "auto.http.iris" SpanOriginNegroni = "auto.http.negroni" + SpanOriginGrpc = "auto.rpc.grpc" ) // A Span is the building block of a Sentry transaction. Spans build up a tree @@ -44,22 +46,20 @@ const ( // // Spans must be started with either StartSpan or Span.StartChild. type Span struct { //nolint: maligned // prefer readability over optimal memory layout (see note below *) - TraceID TraceID `json:"trace_id"` - SpanID SpanID `json:"span_id"` - ParentSpanID SpanID `json:"parent_span_id,omitzero"` - Name string `json:"name,omitempty"` - Op string `json:"op,omitempty"` - Description string `json:"description,omitempty"` - Status SpanStatus `json:"status,omitempty"` - Tags map[string]string `json:"tags,omitempty"` - StartTime time.Time `json:"start_timestamp,omitzero"` - EndTime time.Time `json:"timestamp,omitzero"` - // Deprecated: use Data instead. To be removed in 0.33.0 - Extra map[string]interface{} `json:"-"` - Data map[string]interface{} `json:"data,omitempty"` - Sampled Sampled `json:"-"` - Source TransactionSource `json:"-"` - Origin SpanOrigin `json:"origin,omitempty"` + TraceID TraceID `json:"trace_id"` + SpanID SpanID `json:"span_id"` + ParentSpanID SpanID `json:"parent_span_id,omitzero"` + Name string `json:"name,omitempty"` + Op string `json:"op,omitempty"` + Description string `json:"description,omitempty"` + Status SpanStatus `json:"status,omitempty"` + Tags map[string]string `json:"tags,omitempty"` + StartTime time.Time `json:"start_timestamp,omitzero"` + EndTime time.Time `json:"timestamp,omitzero"` + Data map[string]interface{} `json:"data,omitempty"` + Sampled Sampled `json:"-"` + Source TransactionSource `json:"-"` + Origin SpanOrigin `json:"origin,omitempty"` // mu protects concurrent writes to map fields mu sync.RWMutex @@ -80,6 +80,10 @@ type Span struct { //nolint: maligned // prefer readability over optimal memory finishOnce sync.Once // explicitSampled is a flag for configuring sampling by using `WithSpanSampled` option. explicitSampled Sampled + // Pre-serialized copies of mutable fields, set by MakeSerializationSafe. + serializedTags json.RawMessage + serializedData json.RawMessage + serializationSafe bool } // TraceParentContext describes the context of a (remote) parent span. @@ -229,6 +233,51 @@ func (s *Span) StartChild(operation string, options ...SpanOption) *Span { return StartSpan(s.Context(), operation, options...) } +// makeSerializationSafe pre-serializes user mutable fields. +func (s *Span) makeSerializationSafe() { + s.mu.Lock() + defer s.mu.Unlock() + + if len(s.Tags) > 0 { + if b, err := json.Marshal(s.Tags); err == nil { + s.serializedTags = b + } + } + if len(s.Data) > 0 { + if b, err := json.Marshal(s.Data); err == nil { + s.serializedData = b + } + } + + s.serializationSafe = true +} + +// MarshalJSON emits the span using pre-serialized fields if available. +func (s *Span) MarshalJSON() ([]byte, error) { + s.mu.RLock() + serializationSafe := s.serializationSafe + serializedTags := s.serializedTags + serializedData := s.serializedData + s.mu.RUnlock() + + if !serializationSafe { + type span Span + return json.Marshal((*span)(s)) + } + + type span Span + type safeSpan struct { + *span + Tags json.RawMessage `json:"tags,omitempty"` + Data json.RawMessage `json:"data,omitempty"` + } + return json.Marshal(safeSpan{ + span: (*span)(s), + Tags: serializedTags, + Data: serializedData, + }) +} + // SetTag sets a tag on the span. It is recommended to use SetTag instead of // accessing the tags map directly as SetTag takes care of initializing the map // when necessary. @@ -646,7 +695,7 @@ func (s *Span) toEvent() *Event { for k, v := range s.contexts { contexts[k] = cloneContext(v) } - contexts["trace"] = s.traceContext().Map() + contexts["trace"] = s.traceContextLockFree().Map() // Make sure that the transaction source is valid transactionSource := s.Source @@ -658,7 +707,7 @@ func (s *Span) toEvent() *Event { Type: transactionType, Transaction: s.Name, Contexts: contexts, - Tags: s.Tags, + Tags: maps.Clone(s.Tags), Timestamp: s.EndTime, StartTime: s.StartTime, Spans: finished, @@ -671,7 +720,27 @@ func (s *Span) toEvent() *Event { } } +// traceContext returns a TraceContext snapshot for an active span. +// +// It needs to clone span Data to avoid holding any user mutable state. func (s *Span) traceContext() *TraceContext { + s.mu.RLock() + defer s.mu.RUnlock() + return &TraceContext{ + TraceID: s.TraceID, + SpanID: s.SpanID, + ParentSpanID: s.ParentSpanID, + Op: s.Op, + Data: maps.Clone(s.Data), + Description: s.Description, + Status: s.Status, + } +} + +// traceContextLockFree is the lock-free variant of traceContext. +// +// This is supposed to be used only when span Finish() was already called. +func (s *Span) traceContextLockFree() *TraceContext { return &TraceContext{ TraceID: s.TraceID, SpanID: s.SpanID, diff --git a/vendor/github.com/getsentry/sentry-go/transport.go b/vendor/github.com/getsentry/sentry-go/transport.go index e813c0c80e..4d487d9523 100644 --- a/vendor/github.com/getsentry/sentry-go/transport.go +++ b/vendor/github.com/getsentry/sentry-go/transport.go @@ -63,34 +63,26 @@ func getTLSConfig(options ClientOptions) *tls.Config { return nil } +// eventDebugContext returns a panic-safe summary of an event for debug logging. +// It only touches scalar fields and len() of collections, which do not trigger +// the runtime concurrent-map-access fatal nor invoke user-defined Stringers. +func eventDebugContext(event *Event) string { + return fmt.Sprintf( + "event=%s type=%s level=%s "+ + "breadcrumbs=%d exceptions=%d tags=%d contexts=%d attachments=%d", + event.EventID, event.Type, event.Level, + len(event.Breadcrumbs), len(event.Exception), + len(event.Tags), len(event.Contexts), len(event.Attachments), + ) +} + func getRequestBodyFromEvent(event *Event) []byte { body, err := json.Marshal(event) - if err == nil { - return body - } - - msg := fmt.Sprintf("Could not encode original event as JSON. "+ - "Succeeded by removing Breadcrumbs, Contexts and Extra. "+ - "Please verify the data you attach to the scope. "+ - "Error: %s", err) - // Try to serialize the event, with all the contextual data that allows for interface{} stripped. - event.Breadcrumbs = nil - event.Contexts = nil - event.Extra = map[string]interface{}{ - "info": msg, - } - body, err = json.Marshal(event) - if err == nil { - debuglog.Println(msg) - return body + if err != nil { + debuglog.Printf("Could not encode event as JSON, skipping delivery. %s: %v", eventDebugContext(event), err) + return nil } - - // This should _only_ happen when Event.Exception[0].Stacktrace.Frames[0].Vars is unserializable - // Which won't ever happen, as we don't use it now (although it's the part of public interface accepted by Sentry) - // Juuust in case something, somehow goes utterly wrong. - debuglog.Println("Event couldn't be marshaled, even with stripped contextual data. Skipping delivery. " + - "Please notify the SDK owners with possibly broken payload.") - return nil + return body } func encodeAttachment(enc *json.Encoder, b io.Writer, attachment *Attachment) error { @@ -227,7 +219,7 @@ func recordForBatchItem(recorder report.ClientReportRecorder, reason report.Disc type envelopeHeader struct { EventID EventID `json:"event_id,omitempty"` SentAt time.Time `json:"sent_at"` - Dsn string `json:"dsn,omitempty"` + Dsn *Dsn `json:"dsn,omitempty"` Sdk map[string]string `json:"sdk,omitempty"` Trace map[string]string `json:"trace,omitempty"` } @@ -253,7 +245,7 @@ func envelopeFromBody(event *Event, dsn *Dsn, sentAt time.Time, body json.RawMes EventID: event.EventID, SentAt: sentAt, Trace: trace, - Dsn: dsn.String(), + Dsn: dsn, Sdk: map[string]string{ "name": event.Sdk.Name, "version": event.Sdk.Version, @@ -462,6 +454,7 @@ func (t *HTTPTransport) SendEventWithContext(ctx context.Context, event *Event) } envelope, err := envelopeFromBody(event, t.dsn, time.Now(), body) if err != nil { + debuglog.Printf("Failed to build envelope, skipping delivery. %s: %v", eventDebugContext(event), err) recordForEvent(t.recorder, report.ReasonInternalError, event) return } @@ -500,7 +493,7 @@ func (t *HTTPTransport) SendEventWithContext(ctx context.Context, event *Event) t.dsn.GetProjectID(), ) default: - debuglog.Println("Event dropped due to transport buffer being full.") + debuglog.Printf("Event dropped due to transport buffer being full. %s", eventDebugContext(event)) recordForEvent(t.recorder, report.ReasonQueueOverflow, event) } @@ -611,7 +604,7 @@ func (t *HTTPTransport) worker() { enc := json.NewEncoder(&buf) if err := encodeEnvelopeHeader(enc, &envelopeHeader{ SentAt: time.Now(), - Dsn: t.dsn.String(), + Dsn: t.dsn, Sdk: map[string]string{ "name": sdkIdentifier, "version": SDKVersion, @@ -799,6 +792,7 @@ func (t *HTTPSyncTransport) SendEventWithContext(ctx context.Context, event *Eve envelope, err := envelopeFromBody(event, t.dsn, time.Now(), body) if err != nil { + debuglog.Printf("Failed to build envelope, skipping delivery. %s: %v", eventDebugContext(event), err) recordForEvent(t.recorder, report.ReasonInternalError, event) return } @@ -918,6 +912,12 @@ func (a *internalAsyncTransportAdapter) Configure(options ClientOptions) { CaCerts: options.CaCerts, Recorder: a.recorder, Provider: a.provider, + SdkInfo: func() *protocol.SdkInfo { + return &protocol.SdkInfo{ + Name: sdkIdentifier, + Version: SDKVersion, + } + }, } a.transport = httpinternal.NewAsyncTransport(transportOptions) @@ -933,28 +933,18 @@ func (a *internalAsyncTransportAdapter) Configure(options ClientOptions) { } func (a *internalAsyncTransportAdapter) SendEvent(event *Event) { - header := &protocol.EnvelopeHeader{EventID: string(event.EventID), SentAt: time.Now(), Sdk: &protocol.SdkInfo{Name: event.Sdk.Name, Version: event.Sdk.Version}} - if a.dsn != nil { - header.Dsn = a.dsn.String() - } + header := &protocol.EnvelopeHeader{EventID: string(event.EventID), SentAt: time.Now(), Dsn: a.dsn, Sdk: &protocol.SdkInfo{Name: event.Sdk.Name, Version: event.Sdk.Version}} if header.EventID == "" { header.EventID = protocol.GenerateEventID() } - envelope := protocol.NewEnvelope(header) - item, err := event.ToEnvelopeItem() + envelope, err := event.ToEnvelope(header) if err != nil { - debuglog.Printf("Failed to convert event to envelope item: %v", err) + debuglog.Printf("Failed to convert event to envelope, skipping delivery. %s: %v", eventDebugContext(event), err) if a.recorder != nil { recordForEvent(a.recorder, report.ReasonInternalError, event) } return } - envelope.AddItem(item) - - for _, attachment := range event.Attachments { - attachmentItem := protocol.NewAttachmentItem(attachment.Filename, attachment.ContentType, attachment.Payload) - envelope.AddItem(attachmentItem) - } if err := a.transport.SendEnvelope(envelope); err != nil { debuglog.Printf("Error sending envelope: %v", err) diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/align.go b/vendor/github.com/jedib0t/go-pretty/v6/text/align.go index 33512ee89d..18cc59e539 100644 --- a/vendor/github.com/jedib0t/go-pretty/v6/text/align.go +++ b/vendor/github.com/jedib0t/go-pretty/v6/text/align.go @@ -127,6 +127,13 @@ func justifyText(text string, textLength int, maxLength int) string { // get the number of spaces to insert into the text numSpacesNeeded := maxLength - textLength + strings.Count(text, " ") + if numSpacesNeeded < 0 { + // textLength (display-width) exceeds maxLength; this can happen + // when the cell contains wide Unicode characters (e.g. CJK) whose + // display width is greater than their rune count. Return the text + // as-is; truncation is the caller's responsibility. + return text + } numSpacesNeededBetweenWords := 0 if len(words) > 1 { numSpacesNeededBetweenWords = numSpacesNeeded / (len(words) - 1) diff --git a/vendor/github.com/klauspost/compress/.gitattributes b/vendor/github.com/klauspost/compress/.gitattributes index 402433593c..57aa6487c5 100644 --- a/vendor/github.com/klauspost/compress/.gitattributes +++ b/vendor/github.com/klauspost/compress/.gitattributes @@ -1,2 +1,3 @@ * -text *.bin -text -diff +*.md text eol=lf diff --git a/vendor/github.com/klauspost/compress/README.md b/vendor/github.com/klauspost/compress/README.md index e839fe9c60..fb023f2cf2 100644 --- a/vendor/github.com/klauspost/compress/README.md +++ b/vendor/github.com/klauspost/compress/README.md @@ -1,700 +1,700 @@ -# compress - -This package provides various compression algorithms. - -* [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression in pure Go. -* [S2](https://github.com/klauspost/compress/tree/master/s2#s2-compression) is a high performance replacement for Snappy. -* Optimized [deflate](https://godoc.org/github.com/klauspost/compress/flate) packages which can be used as a dropin replacement for [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip) and [zlib](https://godoc.org/github.com/klauspost/compress/zlib). -* [snappy](https://github.com/klauspost/compress/tree/master/snappy) is a drop-in replacement for `github.com/golang/snappy` offering better compression and concurrent streams. -* [huff0](https://github.com/klauspost/compress/tree/master/huff0) and [FSE](https://github.com/klauspost/compress/tree/master/fse) implementations for raw entropy encoding. -* [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp) Provides client and server wrappers for handling gzipped/zstd HTTP requests efficiently. -* [pgzip](https://github.com/klauspost/pgzip) is a separate package that provides a very fast parallel gzip implementation. - -[![Go Reference](https://pkg.go.dev/badge/klauspost/compress.svg)](https://pkg.go.dev/github.com/klauspost/compress?tab=subdirectories) -[![Go](https://github.com/klauspost/compress/actions/workflows/go.yml/badge.svg)](https://github.com/klauspost/compress/actions/workflows/go.yml) -[![Sourcegraph Badge](https://sourcegraph.com/github.com/klauspost/compress/-/badge.svg)](https://sourcegraph.com/github.com/klauspost/compress?badge) - -# package usage - -Use `go get github.com/klauspost/compress@latest` to add it to your project. - -This package will support the current Go version and 2 versions back. - -* Use the `nounsafe` tag to disable all use of the "unsafe" package. -* Use the `noasm` tag to disable all assembly across packages. - -Use the links above for more information on each. - -# changelog - -* Feb 9th, 2026 [1.18.4](https://github.com/klauspost/compress/releases/tag/v1.18.4) - * gzhttp: Add zstandard to server handler wrapper https://github.com/klauspost/compress/pull/1121 - * zstd: Add ResetWithOptions to encoder/decoder https://github.com/klauspost/compress/pull/1122 - * gzhttp: preserve qvalue when extra parameters follow in Accept-Encoding by @analytically in https://github.com/klauspost/compress/pull/1116 - -* Jan 16th, 2026 [1.18.3](https://github.com/klauspost/compress/releases/tag/v1.18.3) - * Downstream CVE-2025-61728. See [golang/go#77102](https://github.com/golang/go/issues/77102). - -* Dec 1st, 2025 - [1.18.2](https://github.com/klauspost/compress/releases/tag/v1.18.2) - * flate: Fix invalid encoding on level 9 with single value input in https://github.com/klauspost/compress/pull/1115 - * flate: reduce stateless allocations by @RXamzin in https://github.com/klauspost/compress/pull/1106 - -* Oct 20, 2025 - [1.18.1](https://github.com/klauspost/compress/releases/tag/v1.18.1) - RETRACTED - * zstd: Add simple zstd EncodeTo/DecodeTo functions https://github.com/klauspost/compress/pull/1079 - * zstd: Fix incorrect buffer size in dictionary encodes https://github.com/klauspost/compress/pull/1059 - * s2: check for cap, not len of buffer in EncodeBetter/Best by @vdarulis in https://github.com/klauspost/compress/pull/1080 - * zlib: Avoiding extra allocation in zlib.reader.Reset by @travelpolicy in https://github.com/klauspost/compress/pull/1086 - * gzhttp: remove redundant err check in zstdReader by @ryanfowler in https://github.com/klauspost/compress/pull/1090 - * flate: Faster load+store https://github.com/klauspost/compress/pull/1104 - * flate: Simplify matchlen https://github.com/klauspost/compress/pull/1101 - * flate: Use exact sizes for huffman tables https://github.com/klauspost/compress/pull/1103 - -* Feb 19th, 2025 - [1.18.0](https://github.com/klauspost/compress/releases/tag/v1.18.0) - * Add unsafe little endian loaders https://github.com/klauspost/compress/pull/1036 - * fix: check `r.err != nil` but return a nil value error `err` by @alingse in https://github.com/klauspost/compress/pull/1028 - * flate: Simplify L4-6 loading https://github.com/klauspost/compress/pull/1043 - * flate: Simplify matchlen (remove asm) https://github.com/klauspost/compress/pull/1045 - * s2: Improve small block compression speed w/o asm https://github.com/klauspost/compress/pull/1048 - * flate: Fix matchlen L5+L6 https://github.com/klauspost/compress/pull/1049 - * flate: Cleanup & reduce casts https://github.com/klauspost/compress/pull/1050 - -
- See changes to v1.17.x - -* Oct 11th, 2024 - [1.17.11](https://github.com/klauspost/compress/releases/tag/v1.17.11) - * zstd: Fix extra CRC written with multiple Close calls https://github.com/klauspost/compress/pull/1017 - * s2: Don't use stack for index tables https://github.com/klauspost/compress/pull/1014 - * gzhttp: No content-type on no body response code by @juliens in https://github.com/klauspost/compress/pull/1011 - * gzhttp: Do not set the content-type when response has no body by @kevinpollet in https://github.com/klauspost/compress/pull/1013 - -* Sep 23rd, 2024 - [1.17.10](https://github.com/klauspost/compress/releases/tag/v1.17.10) - * gzhttp: Add TransportAlwaysDecompress option. https://github.com/klauspost/compress/pull/978 - * gzhttp: Add supported decompress request body by @mirecl in https://github.com/klauspost/compress/pull/1002 - * s2: Add EncodeBuffer buffer recycling callback https://github.com/klauspost/compress/pull/982 - * zstd: Improve memory usage on small streaming encodes https://github.com/klauspost/compress/pull/1007 - * flate: read data written with partial flush by @vajexal in https://github.com/klauspost/compress/pull/996 - -* Jun 12th, 2024 - [1.17.9](https://github.com/klauspost/compress/releases/tag/v1.17.9) - * s2: Reduce ReadFrom temporary allocations https://github.com/klauspost/compress/pull/949 - * flate, zstd: Shave some bytes off amd64 matchLen by @greatroar in https://github.com/klauspost/compress/pull/963 - * Upgrade zip/zlib to 1.22.4 upstream https://github.com/klauspost/compress/pull/970 https://github.com/klauspost/compress/pull/971 - * zstd: BuildDict fails with RLE table https://github.com/klauspost/compress/pull/951 - -* Apr 9th, 2024 - [1.17.8](https://github.com/klauspost/compress/releases/tag/v1.17.8) - * zstd: Reject blocks where reserved values are not 0 https://github.com/klauspost/compress/pull/885 - * zstd: Add RLE detection+encoding https://github.com/klauspost/compress/pull/938 - -* Feb 21st, 2024 - [1.17.7](https://github.com/klauspost/compress/releases/tag/v1.17.7) - * s2: Add AsyncFlush method: Complete the block without flushing by @Jille in https://github.com/klauspost/compress/pull/927 - * s2: Fix literal+repeat exceeds dst crash https://github.com/klauspost/compress/pull/930 - -* Feb 5th, 2024 - [1.17.6](https://github.com/klauspost/compress/releases/tag/v1.17.6) - * zstd: Fix incorrect repeat coding in best mode https://github.com/klauspost/compress/pull/923 - * s2: Fix DecodeConcurrent deadlock on errors https://github.com/klauspost/compress/pull/925 - -* Jan 26th, 2024 - [v1.17.5](https://github.com/klauspost/compress/releases/tag/v1.17.5) - * flate: Fix reset with dictionary on custom window encodes https://github.com/klauspost/compress/pull/912 - * zstd: Add Frame header encoding and stripping https://github.com/klauspost/compress/pull/908 - * zstd: Limit better/best default window to 8MB https://github.com/klauspost/compress/pull/913 - * zstd: Speed improvements by @greatroar in https://github.com/klauspost/compress/pull/896 https://github.com/klauspost/compress/pull/910 - * s2: Fix callbacks for skippable blocks and disallow 0xfe (Padding) by @Jille in https://github.com/klauspost/compress/pull/916 https://github.com/klauspost/compress/pull/917 -https://github.com/klauspost/compress/pull/919 https://github.com/klauspost/compress/pull/918 - -* Dec 1st, 2023 - [v1.17.4](https://github.com/klauspost/compress/releases/tag/v1.17.4) - * huff0: Speed up symbol counting by @greatroar in https://github.com/klauspost/compress/pull/887 - * huff0: Remove byteReader by @greatroar in https://github.com/klauspost/compress/pull/886 - * gzhttp: Allow overriding decompression on transport https://github.com/klauspost/compress/pull/892 - * gzhttp: Clamp compression level https://github.com/klauspost/compress/pull/890 - * gzip: Error out if reserved bits are set https://github.com/klauspost/compress/pull/891 - -* Nov 15th, 2023 - [v1.17.3](https://github.com/klauspost/compress/releases/tag/v1.17.3) - * fse: Fix max header size https://github.com/klauspost/compress/pull/881 - * zstd: Improve better/best compression https://github.com/klauspost/compress/pull/877 - * gzhttp: Fix missing content type on Close https://github.com/klauspost/compress/pull/883 - -* Oct 22nd, 2023 - [v1.17.2](https://github.com/klauspost/compress/releases/tag/v1.17.2) - * zstd: Fix rare *CORRUPTION* output in "best" mode. See https://github.com/klauspost/compress/pull/876 - -* Oct 14th, 2023 - [v1.17.1](https://github.com/klauspost/compress/releases/tag/v1.17.1) - * s2: Fix S2 "best" dictionary wrong encoding https://github.com/klauspost/compress/pull/871 - * flate: Reduce allocations in decompressor and minor code improvements by @fakefloordiv in https://github.com/klauspost/compress/pull/869 - * s2: Fix EstimateBlockSize on 6&7 length input https://github.com/klauspost/compress/pull/867 - -* Sept 19th, 2023 - [v1.17.0](https://github.com/klauspost/compress/releases/tag/v1.17.0) - * Add experimental dictionary builder https://github.com/klauspost/compress/pull/853 - * Add xerial snappy read/writer https://github.com/klauspost/compress/pull/838 - * flate: Add limited window compression https://github.com/klauspost/compress/pull/843 - * s2: Do 2 overlapping match checks https://github.com/klauspost/compress/pull/839 - * flate: Add amd64 assembly matchlen https://github.com/klauspost/compress/pull/837 - * gzip: Copy bufio.Reader on Reset by @thatguystone in https://github.com/klauspost/compress/pull/860 - -
-
- See changes to v1.16.x - - -* July 1st, 2023 - [v1.16.7](https://github.com/klauspost/compress/releases/tag/v1.16.7) - * zstd: Fix default level first dictionary encode https://github.com/klauspost/compress/pull/829 - * s2: add GetBufferCapacity() method by @GiedriusS in https://github.com/klauspost/compress/pull/832 - -* June 13, 2023 - [v1.16.6](https://github.com/klauspost/compress/releases/tag/v1.16.6) - * zstd: correctly ignore WithEncoderPadding(1) by @ianlancetaylor in https://github.com/klauspost/compress/pull/806 - * zstd: Add amd64 match length assembly https://github.com/klauspost/compress/pull/824 - * gzhttp: Handle informational headers by @rtribotte in https://github.com/klauspost/compress/pull/815 - * s2: Improve Better compression slightly https://github.com/klauspost/compress/pull/663 - -* Apr 16, 2023 - [v1.16.5](https://github.com/klauspost/compress/releases/tag/v1.16.5) - * zstd: readByte needs to use io.ReadFull by @jnoxon in https://github.com/klauspost/compress/pull/802 - * gzip: Fix WriterTo after initial read https://github.com/klauspost/compress/pull/804 - -* Apr 5, 2023 - [v1.16.4](https://github.com/klauspost/compress/releases/tag/v1.16.4) - * zstd: Improve zstd best efficiency by @greatroar and @klauspost in https://github.com/klauspost/compress/pull/784 - * zstd: Respect WithAllLitEntropyCompression https://github.com/klauspost/compress/pull/792 - * zstd: Fix amd64 not always detecting corrupt data https://github.com/klauspost/compress/pull/785 - * zstd: Various minor improvements by @greatroar in https://github.com/klauspost/compress/pull/788 https://github.com/klauspost/compress/pull/794 https://github.com/klauspost/compress/pull/795 - * s2: Fix huge block overflow https://github.com/klauspost/compress/pull/779 - * s2: Allow CustomEncoder fallback https://github.com/klauspost/compress/pull/780 - * gzhttp: Support ResponseWriter Unwrap() in gzhttp handler by @jgimenez in https://github.com/klauspost/compress/pull/799 - -* Mar 13, 2023 - [v1.16.1](https://github.com/klauspost/compress/releases/tag/v1.16.1) - * zstd: Speed up + improve best encoder by @greatroar in https://github.com/klauspost/compress/pull/776 - * gzhttp: Add optional [BREACH mitigation](https://github.com/klauspost/compress/tree/master/gzhttp#breach-mitigation). https://github.com/klauspost/compress/pull/762 https://github.com/klauspost/compress/pull/768 https://github.com/klauspost/compress/pull/769 https://github.com/klauspost/compress/pull/770 https://github.com/klauspost/compress/pull/767 - * s2: Add Intel LZ4s converter https://github.com/klauspost/compress/pull/766 - * zstd: Minor bug fixes https://github.com/klauspost/compress/pull/771 https://github.com/klauspost/compress/pull/772 https://github.com/klauspost/compress/pull/773 - * huff0: Speed up compress1xDo by @greatroar in https://github.com/klauspost/compress/pull/774 - -* Feb 26, 2023 - [v1.16.0](https://github.com/klauspost/compress/releases/tag/v1.16.0) - * s2: Add [Dictionary](https://github.com/klauspost/compress/tree/master/s2#dictionaries) support. https://github.com/klauspost/compress/pull/685 - * s2: Add Compression Size Estimate. https://github.com/klauspost/compress/pull/752 - * s2: Add support for custom stream encoder. https://github.com/klauspost/compress/pull/755 - * s2: Add LZ4 block converter. https://github.com/klauspost/compress/pull/748 - * s2: Support io.ReaderAt in ReadSeeker. https://github.com/klauspost/compress/pull/747 - * s2c/s2sx: Use concurrent decoding. https://github.com/klauspost/compress/pull/746 -
- -
- See changes to v1.15.x - -* Jan 21st, 2023 (v1.15.15) - * deflate: Improve level 7-9 https://github.com/klauspost/compress/pull/739 - * zstd: Add delta encoding support by @greatroar in https://github.com/klauspost/compress/pull/728 - * zstd: Various speed improvements by @greatroar https://github.com/klauspost/compress/pull/741 https://github.com/klauspost/compress/pull/734 https://github.com/klauspost/compress/pull/736 https://github.com/klauspost/compress/pull/744 https://github.com/klauspost/compress/pull/743 https://github.com/klauspost/compress/pull/745 - * gzhttp: Add SuffixETag() and DropETag() options to prevent ETag collisions on compressed responses by @willbicks in https://github.com/klauspost/compress/pull/740 - -* Jan 3rd, 2023 (v1.15.14) - - * flate: Improve speed in big stateless blocks https://github.com/klauspost/compress/pull/718 - * zstd: Minor speed tweaks by @greatroar in https://github.com/klauspost/compress/pull/716 https://github.com/klauspost/compress/pull/720 - * export NoGzipResponseWriter for custom ResponseWriter wrappers by @harshavardhana in https://github.com/klauspost/compress/pull/722 - * s2: Add example for indexing and existing stream https://github.com/klauspost/compress/pull/723 - -* Dec 11, 2022 (v1.15.13) - * zstd: Add [MaxEncodedSize](https://pkg.go.dev/github.com/klauspost/compress@v1.15.13/zstd#Encoder.MaxEncodedSize) to encoder https://github.com/klauspost/compress/pull/691 - * zstd: Various tweaks and improvements https://github.com/klauspost/compress/pull/693 https://github.com/klauspost/compress/pull/695 https://github.com/klauspost/compress/pull/696 https://github.com/klauspost/compress/pull/701 https://github.com/klauspost/compress/pull/702 https://github.com/klauspost/compress/pull/703 https://github.com/klauspost/compress/pull/704 https://github.com/klauspost/compress/pull/705 https://github.com/klauspost/compress/pull/706 https://github.com/klauspost/compress/pull/707 https://github.com/klauspost/compress/pull/708 - -* Oct 26, 2022 (v1.15.12) - - * zstd: Tweak decoder allocs. https://github.com/klauspost/compress/pull/680 - * gzhttp: Always delete `HeaderNoCompression` https://github.com/klauspost/compress/pull/683 - -* Sept 26, 2022 (v1.15.11) - - * flate: Improve level 1-3 compression https://github.com/klauspost/compress/pull/678 - * zstd: Improve "best" compression by @nightwolfz in https://github.com/klauspost/compress/pull/677 - * zstd: Fix+reduce decompression allocations https://github.com/klauspost/compress/pull/668 - * zstd: Fix non-effective noescape tag https://github.com/klauspost/compress/pull/667 - -* Sept 16, 2022 (v1.15.10) - - * zstd: Add [WithDecodeAllCapLimit](https://pkg.go.dev/github.com/klauspost/compress@v1.15.10/zstd#WithDecodeAllCapLimit) https://github.com/klauspost/compress/pull/649 - * Add Go 1.19 - deprecate Go 1.16 https://github.com/klauspost/compress/pull/651 - * flate: Improve level 5+6 compression https://github.com/klauspost/compress/pull/656 - * zstd: Improve "better" compression https://github.com/klauspost/compress/pull/657 - * s2: Improve "best" compression https://github.com/klauspost/compress/pull/658 - * s2: Improve "better" compression. https://github.com/klauspost/compress/pull/635 - * s2: Slightly faster non-assembly decompression https://github.com/klauspost/compress/pull/646 - * Use arrays for constant size copies https://github.com/klauspost/compress/pull/659 - -* July 21, 2022 (v1.15.9) - - * zstd: Fix decoder crash on amd64 (no BMI) on invalid input https://github.com/klauspost/compress/pull/645 - * zstd: Disable decoder extended memory copies (amd64) due to possible crashes https://github.com/klauspost/compress/pull/644 - * zstd: Allow single segments up to "max decoded size" https://github.com/klauspost/compress/pull/643 - -* July 13, 2022 (v1.15.8) - - * gzip: fix stack exhaustion bug in Reader.Read https://github.com/klauspost/compress/pull/641 - * s2: Add Index header trim/restore https://github.com/klauspost/compress/pull/638 - * zstd: Optimize seqdeq amd64 asm by @greatroar in https://github.com/klauspost/compress/pull/636 - * zstd: Improve decoder memcopy https://github.com/klauspost/compress/pull/637 - * huff0: Pass a single bitReader pointer to asm by @greatroar in https://github.com/klauspost/compress/pull/634 - * zstd: Branchless getBits for amd64 w/o BMI2 by @greatroar in https://github.com/klauspost/compress/pull/640 - * gzhttp: Remove header before writing https://github.com/klauspost/compress/pull/639 - -* June 29, 2022 (v1.15.7) - - * s2: Fix absolute forward seeks https://github.com/klauspost/compress/pull/633 - * zip: Merge upstream https://github.com/klauspost/compress/pull/631 - * zip: Re-add zip64 fix https://github.com/klauspost/compress/pull/624 - * zstd: translate fseDecoder.buildDtable into asm by @WojciechMula in https://github.com/klauspost/compress/pull/598 - * flate: Faster histograms https://github.com/klauspost/compress/pull/620 - * deflate: Use compound hcode https://github.com/klauspost/compress/pull/622 - -* June 3, 2022 (v1.15.6) - * s2: Improve coding for long, close matches https://github.com/klauspost/compress/pull/613 - * s2c: Add Snappy/S2 stream recompression https://github.com/klauspost/compress/pull/611 - * zstd: Always use configured block size https://github.com/klauspost/compress/pull/605 - * zstd: Fix incorrect hash table placement for dict encoding in default https://github.com/klauspost/compress/pull/606 - * zstd: Apply default config to ZipDecompressor without options https://github.com/klauspost/compress/pull/608 - * gzhttp: Exclude more common archive formats https://github.com/klauspost/compress/pull/612 - * s2: Add ReaderIgnoreCRC https://github.com/klauspost/compress/pull/609 - * s2: Remove sanity load on index creation https://github.com/klauspost/compress/pull/607 - * snappy: Use dedicated function for scoring https://github.com/klauspost/compress/pull/614 - * s2c+s2d: Use official snappy framed extension https://github.com/klauspost/compress/pull/610 - -* May 25, 2022 (v1.15.5) - * s2: Add concurrent stream decompression https://github.com/klauspost/compress/pull/602 - * s2: Fix final emit oob read crash on amd64 https://github.com/klauspost/compress/pull/601 - * huff0: asm implementation of Decompress1X by @WojciechMula https://github.com/klauspost/compress/pull/596 - * zstd: Use 1 less goroutine for stream decoding https://github.com/klauspost/compress/pull/588 - * zstd: Copy literal in 16 byte blocks when possible https://github.com/klauspost/compress/pull/592 - * zstd: Speed up when WithDecoderLowmem(false) https://github.com/klauspost/compress/pull/599 - * zstd: faster next state update in BMI2 version of decode by @WojciechMula in https://github.com/klauspost/compress/pull/593 - * huff0: Do not check max size when reading table. https://github.com/klauspost/compress/pull/586 - * flate: Inplace hashing for level 7-9 https://github.com/klauspost/compress/pull/590 - - -* May 11, 2022 (v1.15.4) - * huff0: decompress directly into output by @WojciechMula in [#577](https://github.com/klauspost/compress/pull/577) - * inflate: Keep dict on stack [#581](https://github.com/klauspost/compress/pull/581) - * zstd: Faster decoding memcopy in asm [#583](https://github.com/klauspost/compress/pull/583) - * zstd: Fix ignored crc [#580](https://github.com/klauspost/compress/pull/580) - -* May 5, 2022 (v1.15.3) - * zstd: Allow to ignore checksum checking by @WojciechMula [#572](https://github.com/klauspost/compress/pull/572) - * s2: Fix incorrect seek for io.SeekEnd in [#575](https://github.com/klauspost/compress/pull/575) - -* Apr 26, 2022 (v1.15.2) - * zstd: Add x86-64 assembly for decompression on streams and blocks. Contributed by [@WojciechMula](https://github.com/WojciechMula). Typically 2x faster. [#528](https://github.com/klauspost/compress/pull/528) [#531](https://github.com/klauspost/compress/pull/531) [#545](https://github.com/klauspost/compress/pull/545) [#537](https://github.com/klauspost/compress/pull/537) - * zstd: Add options to ZipDecompressor and fixes [#539](https://github.com/klauspost/compress/pull/539) - * s2: Use sorted search for index [#555](https://github.com/klauspost/compress/pull/555) - * Minimum version is Go 1.16, added CI test on 1.18. - -* Mar 11, 2022 (v1.15.1) - * huff0: Add x86 assembly of Decode4X by @WojciechMula in [#512](https://github.com/klauspost/compress/pull/512) - * zstd: Reuse zip decoders in [#514](https://github.com/klauspost/compress/pull/514) - * zstd: Detect extra block data and report as corrupted in [#520](https://github.com/klauspost/compress/pull/520) - * zstd: Handle zero sized frame content size stricter in [#521](https://github.com/klauspost/compress/pull/521) - * zstd: Add stricter block size checks in [#523](https://github.com/klauspost/compress/pull/523) - -* Mar 3, 2022 (v1.15.0) - * zstd: Refactor decoder [#498](https://github.com/klauspost/compress/pull/498) - * zstd: Add stream encoding without goroutines [#505](https://github.com/klauspost/compress/pull/505) - * huff0: Prevent single blocks exceeding 16 bits by @klauspost in[#507](https://github.com/klauspost/compress/pull/507) - * flate: Inline literal emission [#509](https://github.com/klauspost/compress/pull/509) - * gzhttp: Add zstd to transport [#400](https://github.com/klauspost/compress/pull/400) - * gzhttp: Make content-type optional [#510](https://github.com/klauspost/compress/pull/510) - -Both compression and decompression now supports "synchronous" stream operations. This means that whenever "concurrency" is set to 1, they will operate without spawning goroutines. - -Stream decompression is now faster on asynchronous, since the goroutine allocation much more effectively splits the workload. On typical streams this will typically use 2 cores fully for decompression. When a stream has finished decoding no goroutines will be left over, so decoders can now safely be pooled and still be garbage collected. - -While the release has been extensively tested, it is recommended to testing when upgrading. - -
- -
- See changes to v1.14.x - -* Feb 22, 2022 (v1.14.4) - * flate: Fix rare huffman only (-2) corruption. [#503](https://github.com/klauspost/compress/pull/503) - * zip: Update deprecated CreateHeaderRaw to correctly call CreateRaw by @saracen in [#502](https://github.com/klauspost/compress/pull/502) - * zip: don't read data descriptor early by @saracen in [#501](https://github.com/klauspost/compress/pull/501) #501 - * huff0: Use static decompression buffer up to 30% faster [#499](https://github.com/klauspost/compress/pull/499) [#500](https://github.com/klauspost/compress/pull/500) - -* Feb 17, 2022 (v1.14.3) - * flate: Improve fastest levels compression speed ~10% more throughput. [#482](https://github.com/klauspost/compress/pull/482) [#489](https://github.com/klauspost/compress/pull/489) [#490](https://github.com/klauspost/compress/pull/490) [#491](https://github.com/klauspost/compress/pull/491) [#494](https://github.com/klauspost/compress/pull/494) [#478](https://github.com/klauspost/compress/pull/478) - * flate: Faster decompression speed, ~5-10%. [#483](https://github.com/klauspost/compress/pull/483) - * s2: Faster compression with Go v1.18 and amd64 microarch level 3+. [#484](https://github.com/klauspost/compress/pull/484) [#486](https://github.com/klauspost/compress/pull/486) - -* Jan 25, 2022 (v1.14.2) - * zstd: improve header decoder by @dsnet [#476](https://github.com/klauspost/compress/pull/476) - * zstd: Add bigger default blocks [#469](https://github.com/klauspost/compress/pull/469) - * zstd: Remove unused decompression buffer [#470](https://github.com/klauspost/compress/pull/470) - * zstd: Fix logically dead code by @ningmingxiao [#472](https://github.com/klauspost/compress/pull/472) - * flate: Improve level 7-9 [#471](https://github.com/klauspost/compress/pull/471) [#473](https://github.com/klauspost/compress/pull/473) - * zstd: Add noasm tag for xxhash [#475](https://github.com/klauspost/compress/pull/475) - -* Jan 11, 2022 (v1.14.1) - * s2: Add stream index in [#462](https://github.com/klauspost/compress/pull/462) - * flate: Speed and efficiency improvements in [#439](https://github.com/klauspost/compress/pull/439) [#461](https://github.com/klauspost/compress/pull/461) [#455](https://github.com/klauspost/compress/pull/455) [#452](https://github.com/klauspost/compress/pull/452) [#458](https://github.com/klauspost/compress/pull/458) - * zstd: Performance improvement in [#420]( https://github.com/klauspost/compress/pull/420) [#456](https://github.com/klauspost/compress/pull/456) [#437](https://github.com/klauspost/compress/pull/437) [#467](https://github.com/klauspost/compress/pull/467) [#468](https://github.com/klauspost/compress/pull/468) - * zstd: add arm64 xxhash assembly in [#464](https://github.com/klauspost/compress/pull/464) - * Add garbled for binaries for s2 in [#445](https://github.com/klauspost/compress/pull/445) -
- -
- See changes to v1.13.x - -* Aug 30, 2021 (v1.13.5) - * gz/zlib/flate: Alias stdlib errors [#425](https://github.com/klauspost/compress/pull/425) - * s2: Add block support to commandline tools [#413](https://github.com/klauspost/compress/pull/413) - * zstd: pooledZipWriter should return Writers to the same pool [#426](https://github.com/klauspost/compress/pull/426) - * Removed golang/snappy as external dependency for tests [#421](https://github.com/klauspost/compress/pull/421) - -* Aug 12, 2021 (v1.13.4) - * Add [snappy replacement package](https://github.com/klauspost/compress/tree/master/snappy). - * zstd: Fix incorrect encoding in "best" mode [#415](https://github.com/klauspost/compress/pull/415) - -* Aug 3, 2021 (v1.13.3) - * zstd: Improve Best compression [#404](https://github.com/klauspost/compress/pull/404) - * zstd: Fix WriteTo error forwarding [#411](https://github.com/klauspost/compress/pull/411) - * gzhttp: Return http.HandlerFunc instead of http.Handler. Unlikely breaking change. [#406](https://github.com/klauspost/compress/pull/406) - * s2sx: Fix max size error [#399](https://github.com/klauspost/compress/pull/399) - * zstd: Add optional stream content size on reset [#401](https://github.com/klauspost/compress/pull/401) - * zstd: use SpeedBestCompression for level >= 10 [#410](https://github.com/klauspost/compress/pull/410) - -* Jun 14, 2021 (v1.13.1) - * s2: Add full Snappy output support [#396](https://github.com/klauspost/compress/pull/396) - * zstd: Add configurable [Decoder window](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithDecoderMaxWindow) size [#394](https://github.com/klauspost/compress/pull/394) - * gzhttp: Add header to skip compression [#389](https://github.com/klauspost/compress/pull/389) - * s2: Improve speed with bigger output margin [#395](https://github.com/klauspost/compress/pull/395) - -* Jun 3, 2021 (v1.13.0) - * Added [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp#gzip-handler) which allows wrapping HTTP servers and clients with GZIP compressors. - * zstd: Detect short invalid signatures [#382](https://github.com/klauspost/compress/pull/382) - * zstd: Spawn decoder goroutine only if needed. [#380](https://github.com/klauspost/compress/pull/380) -
- - -
- See changes to v1.12.x - -* May 25, 2021 (v1.12.3) - * deflate: Better/faster Huffman encoding [#374](https://github.com/klauspost/compress/pull/374) - * deflate: Allocate less for history. [#375](https://github.com/klauspost/compress/pull/375) - * zstd: Forward read errors [#373](https://github.com/klauspost/compress/pull/373) - -* Apr 27, 2021 (v1.12.2) - * zstd: Improve better/best compression [#360](https://github.com/klauspost/compress/pull/360) [#364](https://github.com/klauspost/compress/pull/364) [#365](https://github.com/klauspost/compress/pull/365) - * zstd: Add helpers to compress/decompress zstd inside zip files [#363](https://github.com/klauspost/compress/pull/363) - * deflate: Improve level 5+6 compression [#367](https://github.com/klauspost/compress/pull/367) - * s2: Improve better/best compression [#358](https://github.com/klauspost/compress/pull/358) [#359](https://github.com/klauspost/compress/pull/358) - * s2: Load after checking src limit on amd64. [#362](https://github.com/klauspost/compress/pull/362) - * s2sx: Limit max executable size [#368](https://github.com/klauspost/compress/pull/368) - -* Apr 14, 2021 (v1.12.1) - * snappy package removed. Upstream added as dependency. - * s2: Better compression in "best" mode [#353](https://github.com/klauspost/compress/pull/353) - * s2sx: Add stdin input and detect pre-compressed from signature [#352](https://github.com/klauspost/compress/pull/352) - * s2c/s2d: Add http as possible input [#348](https://github.com/klauspost/compress/pull/348) - * s2c/s2d/s2sx: Always truncate when writing files [#352](https://github.com/klauspost/compress/pull/352) - * zstd: Reduce memory usage further when using [WithLowerEncoderMem](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithLowerEncoderMem) [#346](https://github.com/klauspost/compress/pull/346) - * s2: Fix potential problem with amd64 assembly and profilers [#349](https://github.com/klauspost/compress/pull/349) -
- -
- See changes to v1.11.x - -* Mar 26, 2021 (v1.11.13) - * zstd: Big speedup on small dictionary encodes [#344](https://github.com/klauspost/compress/pull/344) [#345](https://github.com/klauspost/compress/pull/345) - * zstd: Add [WithLowerEncoderMem](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithLowerEncoderMem) encoder option [#336](https://github.com/klauspost/compress/pull/336) - * deflate: Improve entropy compression [#338](https://github.com/klauspost/compress/pull/338) - * s2: Clean up and minor performance improvement in best [#341](https://github.com/klauspost/compress/pull/341) - -* Mar 5, 2021 (v1.11.12) - * s2: Add `s2sx` binary that creates [self extracting archives](https://github.com/klauspost/compress/tree/master/s2#s2sx-self-extracting-archives). - * s2: Speed up decompression on non-assembly platforms [#328](https://github.com/klauspost/compress/pull/328) - -* Mar 1, 2021 (v1.11.9) - * s2: Add ARM64 decompression assembly. Around 2x output speed. [#324](https://github.com/klauspost/compress/pull/324) - * s2: Improve "better" speed and efficiency. [#325](https://github.com/klauspost/compress/pull/325) - * s2: Fix binaries. - -* Feb 25, 2021 (v1.11.8) - * s2: Fixed occasional out-of-bounds write on amd64. Upgrade recommended. - * s2: Add AMD64 assembly for better mode. 25-50% faster. [#315](https://github.com/klauspost/compress/pull/315) - * s2: Less upfront decoder allocation. [#322](https://github.com/klauspost/compress/pull/322) - * zstd: Faster "compression" of incompressible data. [#314](https://github.com/klauspost/compress/pull/314) - * zip: Fix zip64 headers. [#313](https://github.com/klauspost/compress/pull/313) - -* Jan 14, 2021 (v1.11.7) - * Use Bytes() interface to get bytes across packages. [#309](https://github.com/klauspost/compress/pull/309) - * s2: Add 'best' compression option. [#310](https://github.com/klauspost/compress/pull/310) - * s2: Add ReaderMaxBlockSize, changes `s2.NewReader` signature to include varargs. [#311](https://github.com/klauspost/compress/pull/311) - * s2: Fix crash on small better buffers. [#308](https://github.com/klauspost/compress/pull/308) - * s2: Clean up decoder. [#312](https://github.com/klauspost/compress/pull/312) - -* Jan 7, 2021 (v1.11.6) - * zstd: Make decoder allocations smaller [#306](https://github.com/klauspost/compress/pull/306) - * zstd: Free Decoder resources when Reset is called with a nil io.Reader [#305](https://github.com/klauspost/compress/pull/305) - -* Dec 20, 2020 (v1.11.4) - * zstd: Add Best compression mode [#304](https://github.com/klauspost/compress/pull/304) - * Add header decoder [#299](https://github.com/klauspost/compress/pull/299) - * s2: Add uncompressed stream option [#297](https://github.com/klauspost/compress/pull/297) - * Simplify/speed up small blocks with known max size. [#300](https://github.com/klauspost/compress/pull/300) - * zstd: Always reset literal dict encoder [#303](https://github.com/klauspost/compress/pull/303) - -* Nov 15, 2020 (v1.11.3) - * inflate: 10-15% faster decompression [#293](https://github.com/klauspost/compress/pull/293) - * zstd: Tweak DecodeAll default allocation [#295](https://github.com/klauspost/compress/pull/295) - -* Oct 11, 2020 (v1.11.2) - * s2: Fix out of bounds read in "better" block compression [#291](https://github.com/klauspost/compress/pull/291) - -* Oct 1, 2020 (v1.11.1) - * zstd: Set allLitEntropy true in default configuration [#286](https://github.com/klauspost/compress/pull/286) - -* Sept 8, 2020 (v1.11.0) - * zstd: Add experimental compression [dictionaries](https://github.com/klauspost/compress/tree/master/zstd#dictionaries) [#281](https://github.com/klauspost/compress/pull/281) - * zstd: Fix mixed Write and ReadFrom calls [#282](https://github.com/klauspost/compress/pull/282) - * inflate/gz: Limit variable shifts, ~5% faster decompression [#274](https://github.com/klauspost/compress/pull/274) -
- -
- See changes to v1.10.x - -* July 8, 2020 (v1.10.11) - * zstd: Fix extra block when compressing with ReadFrom. [#278](https://github.com/klauspost/compress/pull/278) - * huff0: Also populate compression table when reading decoding table. [#275](https://github.com/klauspost/compress/pull/275) - -* June 23, 2020 (v1.10.10) - * zstd: Skip entropy compression in fastest mode when no matches. [#270](https://github.com/klauspost/compress/pull/270) - -* June 16, 2020 (v1.10.9): - * zstd: API change for specifying dictionaries. See [#268](https://github.com/klauspost/compress/pull/268) - * zip: update CreateHeaderRaw to handle zip64 fields. [#266](https://github.com/klauspost/compress/pull/266) - * Fuzzit tests removed. The service has been purchased and is no longer available. - -* June 5, 2020 (v1.10.8): - * 1.15x faster zstd block decompression. [#265](https://github.com/klauspost/compress/pull/265) - -* June 1, 2020 (v1.10.7): - * Added zstd decompression [dictionary support](https://github.com/klauspost/compress/tree/master/zstd#dictionaries) - * Increase zstd decompression speed up to 1.19x. [#259](https://github.com/klauspost/compress/pull/259) - * Remove internal reset call in zstd compression and reduce allocations. [#263](https://github.com/klauspost/compress/pull/263) - -* May 21, 2020: (v1.10.6) - * zstd: Reduce allocations while decoding. [#258](https://github.com/klauspost/compress/pull/258), [#252](https://github.com/klauspost/compress/pull/252) - * zstd: Stricter decompression checks. - -* April 12, 2020: (v1.10.5) - * s2-commands: Flush output when receiving SIGINT. [#239](https://github.com/klauspost/compress/pull/239) - -* Apr 8, 2020: (v1.10.4) - * zstd: Minor/special case optimizations. [#251](https://github.com/klauspost/compress/pull/251), [#250](https://github.com/klauspost/compress/pull/250), [#249](https://github.com/klauspost/compress/pull/249), [#247](https://github.com/klauspost/compress/pull/247) -* Mar 11, 2020: (v1.10.3) - * s2: Use S2 encoder in pure Go mode for Snappy output as well. [#245](https://github.com/klauspost/compress/pull/245) - * s2: Fix pure Go block encoder. [#244](https://github.com/klauspost/compress/pull/244) - * zstd: Added "better compression" mode. [#240](https://github.com/klauspost/compress/pull/240) - * zstd: Improve speed of fastest compression mode by 5-10% [#241](https://github.com/klauspost/compress/pull/241) - * zstd: Skip creating encoders when not needed. [#238](https://github.com/klauspost/compress/pull/238) - -* Feb 27, 2020: (v1.10.2) - * Close to 50% speedup in inflate (gzip/zip decompression). [#236](https://github.com/klauspost/compress/pull/236) [#234](https://github.com/klauspost/compress/pull/234) [#232](https://github.com/klauspost/compress/pull/232) - * Reduce deflate level 1-6 memory usage up to 59%. [#227](https://github.com/klauspost/compress/pull/227) - -* Feb 18, 2020: (v1.10.1) - * Fix zstd crash when resetting multiple times without sending data. [#226](https://github.com/klauspost/compress/pull/226) - * deflate: Fix dictionary use on level 1-6. [#224](https://github.com/klauspost/compress/pull/224) - * Remove deflate writer reference when closing. [#224](https://github.com/klauspost/compress/pull/224) - -* Feb 4, 2020: (v1.10.0) - * Add optional dictionary to [stateless deflate](https://pkg.go.dev/github.com/klauspost/compress/flate?tab=doc#StatelessDeflate). Breaking change, send `nil` for previous behaviour. [#216](https://github.com/klauspost/compress/pull/216) - * Fix buffer overflow on repeated small block deflate. [#218](https://github.com/klauspost/compress/pull/218) - * Allow copying content from an existing ZIP file without decompressing+compressing. [#214](https://github.com/klauspost/compress/pull/214) - * Added [S2](https://github.com/klauspost/compress/tree/master/s2#s2-compression) AMD64 assembler and various optimizations. Stream speed >10GB/s. [#186](https://github.com/klauspost/compress/pull/186) - -
- -
- See changes prior to v1.10.0 - -* Jan 20,2020 (v1.9.8) Optimize gzip/deflate with better size estimates and faster table generation. [#207](https://github.com/klauspost/compress/pull/207) by [luyu6056](https://github.com/luyu6056), [#206](https://github.com/klauspost/compress/pull/206). -* Jan 11, 2020: S2 Encode/Decode will use provided buffer if capacity is big enough. [#204](https://github.com/klauspost/compress/pull/204) -* Jan 5, 2020: (v1.9.7) Fix another zstd regression in v1.9.5 - v1.9.6 removed. -* Jan 4, 2020: (v1.9.6) Regression in v1.9.5 fixed causing corrupt zstd encodes in rare cases. -* Jan 4, 2020: Faster IO in [s2c + s2d commandline tools](https://github.com/klauspost/compress/tree/master/s2#commandline-tools) compression/decompression. [#192](https://github.com/klauspost/compress/pull/192) -* Dec 29, 2019: Removed v1.9.5 since fuzz tests showed a compatibility problem with the reference zstandard decoder. -* Dec 29, 2019: (v1.9.5) zstd: 10-20% faster block compression. [#199](https://github.com/klauspost/compress/pull/199) -* Dec 29, 2019: [zip](https://godoc.org/github.com/klauspost/compress/zip) package updated with latest Go features -* Dec 29, 2019: zstd: Single segment flag condintions tweaked. [#197](https://github.com/klauspost/compress/pull/197) -* Dec 18, 2019: s2: Faster compression when ReadFrom is used. [#198](https://github.com/klauspost/compress/pull/198) -* Dec 10, 2019: s2: Fix repeat length output when just above at 16MB limit. -* Dec 10, 2019: zstd: Add function to get decoder as io.ReadCloser. [#191](https://github.com/klauspost/compress/pull/191) -* Dec 3, 2019: (v1.9.4) S2: limit max repeat length. [#188](https://github.com/klauspost/compress/pull/188) -* Dec 3, 2019: Add [WithNoEntropyCompression](https://godoc.org/github.com/klauspost/compress/zstd#WithNoEntropyCompression) to zstd [#187](https://github.com/klauspost/compress/pull/187) -* Dec 3, 2019: Reduce memory use for tests. Check for leaked goroutines. -* Nov 28, 2019 (v1.9.3) Less allocations in stateless deflate. -* Nov 28, 2019: 5-20% Faster huff0 decode. Impacts zstd as well. [#184](https://github.com/klauspost/compress/pull/184) -* Nov 12, 2019 (v1.9.2) Added [Stateless Compression](#stateless-compression) for gzip/deflate. -* Nov 12, 2019: Fixed zstd decompression of large single blocks. [#180](https://github.com/klauspost/compress/pull/180) -* Nov 11, 2019: Set default [s2c](https://github.com/klauspost/compress/tree/master/s2#commandline-tools) block size to 4MB. -* Nov 11, 2019: Reduce inflate memory use by 1KB. -* Nov 10, 2019: Less allocations in deflate bit writer. -* Nov 10, 2019: Fix inconsistent error returned by zstd decoder. -* Oct 28, 2019 (v1.9.1) ztsd: Fix crash when compressing blocks. [#174](https://github.com/klauspost/compress/pull/174) -* Oct 24, 2019 (v1.9.0) zstd: Fix rare data corruption [#173](https://github.com/klauspost/compress/pull/173) -* Oct 24, 2019 zstd: Fix huff0 out of buffer write [#171](https://github.com/klauspost/compress/pull/171) and always return errors [#172](https://github.com/klauspost/compress/pull/172) -* Oct 10, 2019: Big deflate rewrite, 30-40% faster with better compression [#105](https://github.com/klauspost/compress/pull/105) - -
- -
- See changes prior to v1.9.0 - -* Oct 10, 2019: (v1.8.6) zstd: Allow partial reads to get flushed data. [#169](https://github.com/klauspost/compress/pull/169) -* Oct 3, 2019: Fix inconsistent results on broken zstd streams. -* Sep 25, 2019: Added `-rm` (remove source files) and `-q` (no output except errors) to `s2c` and `s2d` [commands](https://github.com/klauspost/compress/tree/master/s2#commandline-tools) -* Sep 16, 2019: (v1.8.4) Add `s2c` and `s2d` [commandline tools](https://github.com/klauspost/compress/tree/master/s2#commandline-tools). -* Sep 10, 2019: (v1.8.3) Fix s2 decoder [Skip](https://godoc.org/github.com/klauspost/compress/s2#Reader.Skip). -* Sep 7, 2019: zstd: Added [WithWindowSize](https://godoc.org/github.com/klauspost/compress/zstd#WithWindowSize), contributed by [ianwilkes](https://github.com/ianwilkes). -* Sep 5, 2019: (v1.8.2) Add [WithZeroFrames](https://godoc.org/github.com/klauspost/compress/zstd#WithZeroFrames) which adds full zero payload block encoding option. -* Sep 5, 2019: Lazy initialization of zstandard predefined en/decoder tables. -* Aug 26, 2019: (v1.8.1) S2: 1-2% compression increase in "better" compression mode. -* Aug 26, 2019: zstd: Check maximum size of Huffman 1X compressed literals while decoding. -* Aug 24, 2019: (v1.8.0) Added [S2 compression](https://github.com/klauspost/compress/tree/master/s2#s2-compression), a high performance replacement for Snappy. -* Aug 21, 2019: (v1.7.6) Fixed minor issues found by fuzzer. One could lead to zstd not decompressing. -* Aug 18, 2019: Add [fuzzit](https://fuzzit.dev/) continuous fuzzing. -* Aug 14, 2019: zstd: Skip incompressible data 2x faster. [#147](https://github.com/klauspost/compress/pull/147) -* Aug 4, 2019 (v1.7.5): Better literal compression. [#146](https://github.com/klauspost/compress/pull/146) -* Aug 4, 2019: Faster zstd compression. [#143](https://github.com/klauspost/compress/pull/143) [#144](https://github.com/klauspost/compress/pull/144) -* Aug 4, 2019: Faster zstd decompression. [#145](https://github.com/klauspost/compress/pull/145) [#143](https://github.com/klauspost/compress/pull/143) [#142](https://github.com/klauspost/compress/pull/142) -* July 15, 2019 (v1.7.4): Fix double EOF block in rare cases on zstd encoder. -* July 15, 2019 (v1.7.3): Minor speedup/compression increase in default zstd encoder. -* July 14, 2019: zstd decoder: Fix decompression error on multiple uses with mixed content. -* July 7, 2019 (v1.7.2): Snappy update, zstd decoder potential race fix. -* June 17, 2019: zstd decompression bugfix. -* June 17, 2019: fix 32 bit builds. -* June 17, 2019: Easier use in modules (less dependencies). -* June 9, 2019: New stronger "default" [zstd](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression mode. Matches zstd default compression ratio. -* June 5, 2019: 20-40% throughput in [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and better compression. -* June 5, 2019: deflate/gzip compression: Reduce memory usage of lower compression levels. -* June 2, 2019: Added [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression! -* May 25, 2019: deflate/gzip: 10% faster bit writer, mostly visible in lower levels. -* Apr 22, 2019: [zstd](https://github.com/klauspost/compress/tree/master/zstd#zstd) decompression added. -* Aug 1, 2018: Added [huff0 README](https://github.com/klauspost/compress/tree/master/huff0#huff0-entropy-compression). -* Jul 8, 2018: Added [Performance Update 2018](#performance-update-2018) below. -* Jun 23, 2018: Merged [Go 1.11 inflate optimizations](https://go-review.googlesource.com/c/go/+/102235). Go 1.9 is now required. Backwards compatible version tagged with [v1.3.0](https://github.com/klauspost/compress/releases/tag/v1.3.0). -* Apr 2, 2018: Added [huff0](https://godoc.org/github.com/klauspost/compress/huff0) en/decoder. Experimental for now, API may change. -* Mar 4, 2018: Added [FSE Entropy](https://godoc.org/github.com/klauspost/compress/fse) en/decoder. Experimental for now, API may change. -* Nov 3, 2017: Add compression [Estimate](https://godoc.org/github.com/klauspost/compress#Estimate) function. -* May 28, 2017: Reduce allocations when resetting decoder. -* Apr 02, 2017: Change back to official crc32, since changes were merged in Go 1.7. -* Jan 14, 2017: Reduce stack pressure due to array copies. See [Issue #18625](https://github.com/golang/go/issues/18625). -* Oct 25, 2016: Level 2-4 have been rewritten and now offers significantly better performance than before. -* Oct 20, 2016: Port zlib changes from Go 1.7 to fix zlib writer issue. Please update. -* Oct 16, 2016: Go 1.7 changes merged. Apples to apples this package is a few percent faster, but has a significantly better balance between speed and compression per level. -* Mar 24, 2016: Always attempt Huffman encoding on level 4-7. This improves base 64 encoded data compression. -* Mar 24, 2016: Small speedup for level 1-3. -* Feb 19, 2016: Faster bit writer, level -2 is 15% faster, level 1 is 4% faster. -* Feb 19, 2016: Handle small payloads faster in level 1-3. -* Feb 19, 2016: Added faster level 2 + 3 compression modes. -* Feb 19, 2016: [Rebalanced compression levels](https://blog.klauspost.com/rebalancing-deflate-compression-levels/), so there is a more even progression in terms of compression. New default level is 5. -* Feb 14, 2016: Snappy: Merge upstream changes. -* Feb 14, 2016: Snappy: Fix aggressive skipping. -* Feb 14, 2016: Snappy: Update benchmark. -* Feb 13, 2016: Deflate: Fixed assembler problem that could lead to sub-optimal compression. -* Feb 12, 2016: Snappy: Added AMD64 SSE 4.2 optimizations to matching, which makes easy to compress material run faster. Typical speedup is around 25%. -* Feb 9, 2016: Added Snappy package fork. This version is 5-7% faster, much more on hard to compress content. -* Jan 30, 2016: Optimize level 1 to 3 by not considering static dictionary or storing uncompressed. ~4-5% speedup. -* Jan 16, 2016: Optimization on deflate level 1,2,3 compression. -* Jan 8 2016: Merge [CL 18317](https://go-review.googlesource.com/#/c/18317): fix reading, writing of zip64 archives. -* Dec 8 2015: Make level 1 and -2 deterministic even if write size differs. -* Dec 8 2015: Split encoding functions, so hashing and matching can potentially be inlined. 1-3% faster on AMD64. 5% faster on other platforms. -* Dec 8 2015: Fixed rare [one byte out-of bounds read](https://github.com/klauspost/compress/issues/20). Please update! -* Nov 23 2015: Optimization on token writer. ~2-4% faster. Contributed by [@dsnet](https://github.com/dsnet). -* Nov 20 2015: Small optimization to bit writer on 64 bit systems. -* Nov 17 2015: Fixed out-of-bound errors if the underlying Writer returned an error. See [#15](https://github.com/klauspost/compress/issues/15). -* Nov 12 2015: Added [io.WriterTo](https://golang.org/pkg/io/#WriterTo) support to gzip/inflate. -* Nov 11 2015: Merged [CL 16669](https://go-review.googlesource.com/#/c/16669/4): archive/zip: enable overriding (de)compressors per file -* Oct 15 2015: Added skipping on uncompressible data. Random data speed up >5x. - -
- -# deflate usage - -The packages are drop-in replacements for standard library [deflate](https://godoc.org/github.com/klauspost/compress/flate), [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip), and [zlib](https://godoc.org/github.com/klauspost/compress/zlib). Simply replace the import path to use them: - -Typical speed is about 2x of the standard library packages. - -| old import | new import | Documentation | -|------------------|---------------------------------------|-------------------------------------------------------------------------| -| `compress/gzip` | `github.com/klauspost/compress/gzip` | [gzip](https://pkg.go.dev/github.com/klauspost/compress/gzip?tab=doc) | -| `compress/zlib` | `github.com/klauspost/compress/zlib` | [zlib](https://pkg.go.dev/github.com/klauspost/compress/zlib?tab=doc) | -| `archive/zip` | `github.com/klauspost/compress/zip` | [zip](https://pkg.go.dev/github.com/klauspost/compress/zip?tab=doc) | -| `compress/flate` | `github.com/klauspost/compress/flate` | [flate](https://pkg.go.dev/github.com/klauspost/compress/flate?tab=doc) | - -You may also be interested in [pgzip](https://github.com/klauspost/pgzip), which is a drop-in replacement for gzip, which support multithreaded compression on big files and the optimized [crc32](https://github.com/klauspost/crc32) package used by these packages. - -The packages implement the same API as the standard library, so you can use the original godoc documentation: [gzip](http://golang.org/pkg/compress/gzip/), [zip](http://golang.org/pkg/archive/zip/), [zlib](http://golang.org/pkg/compress/zlib/), [flate](http://golang.org/pkg/compress/flate/). - -Currently there is only minor speedup on decompression (mostly CRC32 calculation). - -Memory usage is typically 1MB for a Writer. stdlib is in the same range. -If you expect to have a lot of concurrently allocated Writers consider using -the stateless compression described below. - -For compression performance, see: [this spreadsheet](https://docs.google.com/spreadsheets/d/1nuNE2nPfuINCZJRMt6wFWhKpToF95I47XjSsc-1rbPQ/edit?usp=sharing). - -To disable all assembly add `-tags=noasm`. This works across all packages. - -# Stateless compression - -This package offers stateless compression as a special option for gzip/deflate. -It will do compression but without maintaining any state between Write calls. - -This means there will be no memory kept between Write calls, but compression and speed will be suboptimal. - -This is only relevant in cases where you expect to run many thousands of compressors concurrently, -but with very little activity. This is *not* intended for regular web servers serving individual requests. - -Because of this, the size of actual Write calls will affect output size. - -In gzip, specify level `-3` / `gzip.StatelessCompression` to enable. - -For direct deflate use, NewStatelessWriter and StatelessDeflate are available. See [documentation](https://godoc.org/github.com/klauspost/compress/flate#NewStatelessWriter) - -A `bufio.Writer` can of course be used to control write sizes. For example, to use a 4KB buffer: - -```go - // replace 'ioutil.Discard' with your output. - gzw, err := gzip.NewWriterLevel(ioutil.Discard, gzip.StatelessCompression) - if err != nil { - return err - } - defer gzw.Close() - - w := bufio.NewWriterSize(gzw, 4096) - defer w.Flush() - - // Write to 'w' -``` - -This will only use up to 4KB in memory when the writer is idle. - -Compression is almost always worse than the fastest compression level -and each write will allocate (a little) memory. - - -# Other packages - -Here are other packages of good quality and pure Go (no cgo wrappers or autoconverted code): - -* [github.com/pierrec/lz4](https://github.com/pierrec/lz4) - strong multithreaded LZ4 compression. -* [github.com/cosnicolaou/pbzip2](https://github.com/cosnicolaou/pbzip2) - multithreaded bzip2 decompression. -* [github.com/dsnet/compress](https://github.com/dsnet/compress) - brotli decompression, bzip2 writer. -* [github.com/ronanh/intcomp](https://github.com/ronanh/intcomp) - Integer compression. -* [github.com/spenczar/fpc](https://github.com/spenczar/fpc) - Float compression. -* [github.com/minio/zipindex](https://github.com/minio/zipindex) - External ZIP directory index. -* [github.com/ybirader/pzip](https://github.com/ybirader/pzip) - Fast concurrent zip archiver and extractor. - -# license - -This code is licensed under the same conditions as the original Go code. See LICENSE file. - - - - - +# compress + +This package provides various compression algorithms. + +* [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression in pure Go. +* [S2](https://github.com/klauspost/compress/tree/master/s2#s2-compression) is a high performance replacement for Snappy. +* Optimized [deflate](https://godoc.org/github.com/klauspost/compress/flate) packages which can be used as a dropin replacement for [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip) and [zlib](https://godoc.org/github.com/klauspost/compress/zlib). +* [snappy](https://github.com/klauspost/compress/tree/master/snappy) is a drop-in replacement for `github.com/golang/snappy` offering better compression and concurrent streams. +* [huff0](https://github.com/klauspost/compress/tree/master/huff0) and [FSE](https://github.com/klauspost/compress/tree/master/fse) implementations for raw entropy encoding. +* [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp) Provides client and server wrappers for handling gzipped/zstd HTTP requests efficiently. +* [pgzip](https://github.com/klauspost/pgzip) is a separate package that provides a very fast parallel gzip implementation. + +[![Go Reference](https://pkg.go.dev/badge/klauspost/compress.svg)](https://pkg.go.dev/github.com/klauspost/compress?tab=subdirectories) +[![Go](https://github.com/klauspost/compress/actions/workflows/go.yml/badge.svg)](https://github.com/klauspost/compress/actions/workflows/go.yml) +[![Sourcegraph Badge](https://sourcegraph.com/github.com/klauspost/compress/-/badge.svg)](https://sourcegraph.com/github.com/klauspost/compress?badge) + +# package usage + +Use `go get github.com/klauspost/compress@latest` to add it to your project. + +This package will support the current Go version and 2 versions back. + +* Use the `nounsafe` tag to disable all use of the "unsafe" package. +* Use the `noasm` tag to disable all assembly across packages. + +Use the links above for more information on each. + +# changelog + +* Feb 9th, 2026 [1.18.4](https://github.com/klauspost/compress/releases/tag/v1.18.4) + * gzhttp: Add zstandard to server handler wrapper https://github.com/klauspost/compress/pull/1121 + * zstd: Add ResetWithOptions to encoder/decoder https://github.com/klauspost/compress/pull/1122 + * gzhttp: preserve qvalue when extra parameters follow in Accept-Encoding by @analytically in https://github.com/klauspost/compress/pull/1116 + +* Jan 16th, 2026 [1.18.3](https://github.com/klauspost/compress/releases/tag/v1.18.3) + * Downstream CVE-2025-61728. See [golang/go#77102](https://github.com/golang/go/issues/77102). + +* Dec 1st, 2025 - [1.18.2](https://github.com/klauspost/compress/releases/tag/v1.18.2) + * flate: Fix invalid encoding on level 9 with single value input in https://github.com/klauspost/compress/pull/1115 + * flate: reduce stateless allocations by @RXamzin in https://github.com/klauspost/compress/pull/1106 + +* Oct 20, 2025 - [1.18.1](https://github.com/klauspost/compress/releases/tag/v1.18.1) - RETRACTED + * zstd: Add simple zstd EncodeTo/DecodeTo functions https://github.com/klauspost/compress/pull/1079 + * zstd: Fix incorrect buffer size in dictionary encodes https://github.com/klauspost/compress/pull/1059 + * s2: check for cap, not len of buffer in EncodeBetter/Best by @vdarulis in https://github.com/klauspost/compress/pull/1080 + * zlib: Avoiding extra allocation in zlib.reader.Reset by @travelpolicy in https://github.com/klauspost/compress/pull/1086 + * gzhttp: remove redundant err check in zstdReader by @ryanfowler in https://github.com/klauspost/compress/pull/1090 + * flate: Faster load+store https://github.com/klauspost/compress/pull/1104 + * flate: Simplify matchlen https://github.com/klauspost/compress/pull/1101 + * flate: Use exact sizes for huffman tables https://github.com/klauspost/compress/pull/1103 + +* Feb 19th, 2025 - [1.18.0](https://github.com/klauspost/compress/releases/tag/v1.18.0) + * Add unsafe little endian loaders https://github.com/klauspost/compress/pull/1036 + * fix: check `r.err != nil` but return a nil value error `err` by @alingse in https://github.com/klauspost/compress/pull/1028 + * flate: Simplify L4-6 loading https://github.com/klauspost/compress/pull/1043 + * flate: Simplify matchlen (remove asm) https://github.com/klauspost/compress/pull/1045 + * s2: Improve small block compression speed w/o asm https://github.com/klauspost/compress/pull/1048 + * flate: Fix matchlen L5+L6 https://github.com/klauspost/compress/pull/1049 + * flate: Cleanup & reduce casts https://github.com/klauspost/compress/pull/1050 + +
+ See changes to v1.17.x + +* Oct 11th, 2024 - [1.17.11](https://github.com/klauspost/compress/releases/tag/v1.17.11) + * zstd: Fix extra CRC written with multiple Close calls https://github.com/klauspost/compress/pull/1017 + * s2: Don't use stack for index tables https://github.com/klauspost/compress/pull/1014 + * gzhttp: No content-type on no body response code by @juliens in https://github.com/klauspost/compress/pull/1011 + * gzhttp: Do not set the content-type when response has no body by @kevinpollet in https://github.com/klauspost/compress/pull/1013 + +* Sep 23rd, 2024 - [1.17.10](https://github.com/klauspost/compress/releases/tag/v1.17.10) + * gzhttp: Add TransportAlwaysDecompress option. https://github.com/klauspost/compress/pull/978 + * gzhttp: Add supported decompress request body by @mirecl in https://github.com/klauspost/compress/pull/1002 + * s2: Add EncodeBuffer buffer recycling callback https://github.com/klauspost/compress/pull/982 + * zstd: Improve memory usage on small streaming encodes https://github.com/klauspost/compress/pull/1007 + * flate: read data written with partial flush by @vajexal in https://github.com/klauspost/compress/pull/996 + +* Jun 12th, 2024 - [1.17.9](https://github.com/klauspost/compress/releases/tag/v1.17.9) + * s2: Reduce ReadFrom temporary allocations https://github.com/klauspost/compress/pull/949 + * flate, zstd: Shave some bytes off amd64 matchLen by @greatroar in https://github.com/klauspost/compress/pull/963 + * Upgrade zip/zlib to 1.22.4 upstream https://github.com/klauspost/compress/pull/970 https://github.com/klauspost/compress/pull/971 + * zstd: BuildDict fails with RLE table https://github.com/klauspost/compress/pull/951 + +* Apr 9th, 2024 - [1.17.8](https://github.com/klauspost/compress/releases/tag/v1.17.8) + * zstd: Reject blocks where reserved values are not 0 https://github.com/klauspost/compress/pull/885 + * zstd: Add RLE detection+encoding https://github.com/klauspost/compress/pull/938 + +* Feb 21st, 2024 - [1.17.7](https://github.com/klauspost/compress/releases/tag/v1.17.7) + * s2: Add AsyncFlush method: Complete the block without flushing by @Jille in https://github.com/klauspost/compress/pull/927 + * s2: Fix literal+repeat exceeds dst crash https://github.com/klauspost/compress/pull/930 + +* Feb 5th, 2024 - [1.17.6](https://github.com/klauspost/compress/releases/tag/v1.17.6) + * zstd: Fix incorrect repeat coding in best mode https://github.com/klauspost/compress/pull/923 + * s2: Fix DecodeConcurrent deadlock on errors https://github.com/klauspost/compress/pull/925 + +* Jan 26th, 2024 - [v1.17.5](https://github.com/klauspost/compress/releases/tag/v1.17.5) + * flate: Fix reset with dictionary on custom window encodes https://github.com/klauspost/compress/pull/912 + * zstd: Add Frame header encoding and stripping https://github.com/klauspost/compress/pull/908 + * zstd: Limit better/best default window to 8MB https://github.com/klauspost/compress/pull/913 + * zstd: Speed improvements by @greatroar in https://github.com/klauspost/compress/pull/896 https://github.com/klauspost/compress/pull/910 + * s2: Fix callbacks for skippable blocks and disallow 0xfe (Padding) by @Jille in https://github.com/klauspost/compress/pull/916 https://github.com/klauspost/compress/pull/917 +https://github.com/klauspost/compress/pull/919 https://github.com/klauspost/compress/pull/918 + +* Dec 1st, 2023 - [v1.17.4](https://github.com/klauspost/compress/releases/tag/v1.17.4) + * huff0: Speed up symbol counting by @greatroar in https://github.com/klauspost/compress/pull/887 + * huff0: Remove byteReader by @greatroar in https://github.com/klauspost/compress/pull/886 + * gzhttp: Allow overriding decompression on transport https://github.com/klauspost/compress/pull/892 + * gzhttp: Clamp compression level https://github.com/klauspost/compress/pull/890 + * gzip: Error out if reserved bits are set https://github.com/klauspost/compress/pull/891 + +* Nov 15th, 2023 - [v1.17.3](https://github.com/klauspost/compress/releases/tag/v1.17.3) + * fse: Fix max header size https://github.com/klauspost/compress/pull/881 + * zstd: Improve better/best compression https://github.com/klauspost/compress/pull/877 + * gzhttp: Fix missing content type on Close https://github.com/klauspost/compress/pull/883 + +* Oct 22nd, 2023 - [v1.17.2](https://github.com/klauspost/compress/releases/tag/v1.17.2) + * zstd: Fix rare *CORRUPTION* output in "best" mode. See https://github.com/klauspost/compress/pull/876 + +* Oct 14th, 2023 - [v1.17.1](https://github.com/klauspost/compress/releases/tag/v1.17.1) + * s2: Fix S2 "best" dictionary wrong encoding https://github.com/klauspost/compress/pull/871 + * flate: Reduce allocations in decompressor and minor code improvements by @fakefloordiv in https://github.com/klauspost/compress/pull/869 + * s2: Fix EstimateBlockSize on 6&7 length input https://github.com/klauspost/compress/pull/867 + +* Sept 19th, 2023 - [v1.17.0](https://github.com/klauspost/compress/releases/tag/v1.17.0) + * Add experimental dictionary builder https://github.com/klauspost/compress/pull/853 + * Add xerial snappy read/writer https://github.com/klauspost/compress/pull/838 + * flate: Add limited window compression https://github.com/klauspost/compress/pull/843 + * s2: Do 2 overlapping match checks https://github.com/klauspost/compress/pull/839 + * flate: Add amd64 assembly matchlen https://github.com/klauspost/compress/pull/837 + * gzip: Copy bufio.Reader on Reset by @thatguystone in https://github.com/klauspost/compress/pull/860 + +
+
+ See changes to v1.16.x + + +* July 1st, 2023 - [v1.16.7](https://github.com/klauspost/compress/releases/tag/v1.16.7) + * zstd: Fix default level first dictionary encode https://github.com/klauspost/compress/pull/829 + * s2: add GetBufferCapacity() method by @GiedriusS in https://github.com/klauspost/compress/pull/832 + +* June 13, 2023 - [v1.16.6](https://github.com/klauspost/compress/releases/tag/v1.16.6) + * zstd: correctly ignore WithEncoderPadding(1) by @ianlancetaylor in https://github.com/klauspost/compress/pull/806 + * zstd: Add amd64 match length assembly https://github.com/klauspost/compress/pull/824 + * gzhttp: Handle informational headers by @rtribotte in https://github.com/klauspost/compress/pull/815 + * s2: Improve Better compression slightly https://github.com/klauspost/compress/pull/663 + +* Apr 16, 2023 - [v1.16.5](https://github.com/klauspost/compress/releases/tag/v1.16.5) + * zstd: readByte needs to use io.ReadFull by @jnoxon in https://github.com/klauspost/compress/pull/802 + * gzip: Fix WriterTo after initial read https://github.com/klauspost/compress/pull/804 + +* Apr 5, 2023 - [v1.16.4](https://github.com/klauspost/compress/releases/tag/v1.16.4) + * zstd: Improve zstd best efficiency by @greatroar and @klauspost in https://github.com/klauspost/compress/pull/784 + * zstd: Respect WithAllLitEntropyCompression https://github.com/klauspost/compress/pull/792 + * zstd: Fix amd64 not always detecting corrupt data https://github.com/klauspost/compress/pull/785 + * zstd: Various minor improvements by @greatroar in https://github.com/klauspost/compress/pull/788 https://github.com/klauspost/compress/pull/794 https://github.com/klauspost/compress/pull/795 + * s2: Fix huge block overflow https://github.com/klauspost/compress/pull/779 + * s2: Allow CustomEncoder fallback https://github.com/klauspost/compress/pull/780 + * gzhttp: Support ResponseWriter Unwrap() in gzhttp handler by @jgimenez in https://github.com/klauspost/compress/pull/799 + +* Mar 13, 2023 - [v1.16.1](https://github.com/klauspost/compress/releases/tag/v1.16.1) + * zstd: Speed up + improve best encoder by @greatroar in https://github.com/klauspost/compress/pull/776 + * gzhttp: Add optional [BREACH mitigation](https://github.com/klauspost/compress/tree/master/gzhttp#breach-mitigation). https://github.com/klauspost/compress/pull/762 https://github.com/klauspost/compress/pull/768 https://github.com/klauspost/compress/pull/769 https://github.com/klauspost/compress/pull/770 https://github.com/klauspost/compress/pull/767 + * s2: Add Intel LZ4s converter https://github.com/klauspost/compress/pull/766 + * zstd: Minor bug fixes https://github.com/klauspost/compress/pull/771 https://github.com/klauspost/compress/pull/772 https://github.com/klauspost/compress/pull/773 + * huff0: Speed up compress1xDo by @greatroar in https://github.com/klauspost/compress/pull/774 + +* Feb 26, 2023 - [v1.16.0](https://github.com/klauspost/compress/releases/tag/v1.16.0) + * s2: Add [Dictionary](https://github.com/klauspost/compress/tree/master/s2#dictionaries) support. https://github.com/klauspost/compress/pull/685 + * s2: Add Compression Size Estimate. https://github.com/klauspost/compress/pull/752 + * s2: Add support for custom stream encoder. https://github.com/klauspost/compress/pull/755 + * s2: Add LZ4 block converter. https://github.com/klauspost/compress/pull/748 + * s2: Support io.ReaderAt in ReadSeeker. https://github.com/klauspost/compress/pull/747 + * s2c/s2sx: Use concurrent decoding. https://github.com/klauspost/compress/pull/746 +
+ +
+ See changes to v1.15.x + +* Jan 21st, 2023 (v1.15.15) + * deflate: Improve level 7-9 https://github.com/klauspost/compress/pull/739 + * zstd: Add delta encoding support by @greatroar in https://github.com/klauspost/compress/pull/728 + * zstd: Various speed improvements by @greatroar https://github.com/klauspost/compress/pull/741 https://github.com/klauspost/compress/pull/734 https://github.com/klauspost/compress/pull/736 https://github.com/klauspost/compress/pull/744 https://github.com/klauspost/compress/pull/743 https://github.com/klauspost/compress/pull/745 + * gzhttp: Add SuffixETag() and DropETag() options to prevent ETag collisions on compressed responses by @willbicks in https://github.com/klauspost/compress/pull/740 + +* Jan 3rd, 2023 (v1.15.14) + + * flate: Improve speed in big stateless blocks https://github.com/klauspost/compress/pull/718 + * zstd: Minor speed tweaks by @greatroar in https://github.com/klauspost/compress/pull/716 https://github.com/klauspost/compress/pull/720 + * export NoGzipResponseWriter for custom ResponseWriter wrappers by @harshavardhana in https://github.com/klauspost/compress/pull/722 + * s2: Add example for indexing and existing stream https://github.com/klauspost/compress/pull/723 + +* Dec 11, 2022 (v1.15.13) + * zstd: Add [MaxEncodedSize](https://pkg.go.dev/github.com/klauspost/compress@v1.15.13/zstd#Encoder.MaxEncodedSize) to encoder https://github.com/klauspost/compress/pull/691 + * zstd: Various tweaks and improvements https://github.com/klauspost/compress/pull/693 https://github.com/klauspost/compress/pull/695 https://github.com/klauspost/compress/pull/696 https://github.com/klauspost/compress/pull/701 https://github.com/klauspost/compress/pull/702 https://github.com/klauspost/compress/pull/703 https://github.com/klauspost/compress/pull/704 https://github.com/klauspost/compress/pull/705 https://github.com/klauspost/compress/pull/706 https://github.com/klauspost/compress/pull/707 https://github.com/klauspost/compress/pull/708 + +* Oct 26, 2022 (v1.15.12) + + * zstd: Tweak decoder allocs. https://github.com/klauspost/compress/pull/680 + * gzhttp: Always delete `HeaderNoCompression` https://github.com/klauspost/compress/pull/683 + +* Sept 26, 2022 (v1.15.11) + + * flate: Improve level 1-3 compression https://github.com/klauspost/compress/pull/678 + * zstd: Improve "best" compression by @nightwolfz in https://github.com/klauspost/compress/pull/677 + * zstd: Fix+reduce decompression allocations https://github.com/klauspost/compress/pull/668 + * zstd: Fix non-effective noescape tag https://github.com/klauspost/compress/pull/667 + +* Sept 16, 2022 (v1.15.10) + + * zstd: Add [WithDecodeAllCapLimit](https://pkg.go.dev/github.com/klauspost/compress@v1.15.10/zstd#WithDecodeAllCapLimit) https://github.com/klauspost/compress/pull/649 + * Add Go 1.19 - deprecate Go 1.16 https://github.com/klauspost/compress/pull/651 + * flate: Improve level 5+6 compression https://github.com/klauspost/compress/pull/656 + * zstd: Improve "better" compression https://github.com/klauspost/compress/pull/657 + * s2: Improve "best" compression https://github.com/klauspost/compress/pull/658 + * s2: Improve "better" compression. https://github.com/klauspost/compress/pull/635 + * s2: Slightly faster non-assembly decompression https://github.com/klauspost/compress/pull/646 + * Use arrays for constant size copies https://github.com/klauspost/compress/pull/659 + +* July 21, 2022 (v1.15.9) + + * zstd: Fix decoder crash on amd64 (no BMI) on invalid input https://github.com/klauspost/compress/pull/645 + * zstd: Disable decoder extended memory copies (amd64) due to possible crashes https://github.com/klauspost/compress/pull/644 + * zstd: Allow single segments up to "max decoded size" https://github.com/klauspost/compress/pull/643 + +* July 13, 2022 (v1.15.8) + + * gzip: fix stack exhaustion bug in Reader.Read https://github.com/klauspost/compress/pull/641 + * s2: Add Index header trim/restore https://github.com/klauspost/compress/pull/638 + * zstd: Optimize seqdeq amd64 asm by @greatroar in https://github.com/klauspost/compress/pull/636 + * zstd: Improve decoder memcopy https://github.com/klauspost/compress/pull/637 + * huff0: Pass a single bitReader pointer to asm by @greatroar in https://github.com/klauspost/compress/pull/634 + * zstd: Branchless getBits for amd64 w/o BMI2 by @greatroar in https://github.com/klauspost/compress/pull/640 + * gzhttp: Remove header before writing https://github.com/klauspost/compress/pull/639 + +* June 29, 2022 (v1.15.7) + + * s2: Fix absolute forward seeks https://github.com/klauspost/compress/pull/633 + * zip: Merge upstream https://github.com/klauspost/compress/pull/631 + * zip: Re-add zip64 fix https://github.com/klauspost/compress/pull/624 + * zstd: translate fseDecoder.buildDtable into asm by @WojciechMula in https://github.com/klauspost/compress/pull/598 + * flate: Faster histograms https://github.com/klauspost/compress/pull/620 + * deflate: Use compound hcode https://github.com/klauspost/compress/pull/622 + +* June 3, 2022 (v1.15.6) + * s2: Improve coding for long, close matches https://github.com/klauspost/compress/pull/613 + * s2c: Add Snappy/S2 stream recompression https://github.com/klauspost/compress/pull/611 + * zstd: Always use configured block size https://github.com/klauspost/compress/pull/605 + * zstd: Fix incorrect hash table placement for dict encoding in default https://github.com/klauspost/compress/pull/606 + * zstd: Apply default config to ZipDecompressor without options https://github.com/klauspost/compress/pull/608 + * gzhttp: Exclude more common archive formats https://github.com/klauspost/compress/pull/612 + * s2: Add ReaderIgnoreCRC https://github.com/klauspost/compress/pull/609 + * s2: Remove sanity load on index creation https://github.com/klauspost/compress/pull/607 + * snappy: Use dedicated function for scoring https://github.com/klauspost/compress/pull/614 + * s2c+s2d: Use official snappy framed extension https://github.com/klauspost/compress/pull/610 + +* May 25, 2022 (v1.15.5) + * s2: Add concurrent stream decompression https://github.com/klauspost/compress/pull/602 + * s2: Fix final emit oob read crash on amd64 https://github.com/klauspost/compress/pull/601 + * huff0: asm implementation of Decompress1X by @WojciechMula https://github.com/klauspost/compress/pull/596 + * zstd: Use 1 less goroutine for stream decoding https://github.com/klauspost/compress/pull/588 + * zstd: Copy literal in 16 byte blocks when possible https://github.com/klauspost/compress/pull/592 + * zstd: Speed up when WithDecoderLowmem(false) https://github.com/klauspost/compress/pull/599 + * zstd: faster next state update in BMI2 version of decode by @WojciechMula in https://github.com/klauspost/compress/pull/593 + * huff0: Do not check max size when reading table. https://github.com/klauspost/compress/pull/586 + * flate: Inplace hashing for level 7-9 https://github.com/klauspost/compress/pull/590 + + +* May 11, 2022 (v1.15.4) + * huff0: decompress directly into output by @WojciechMula in [#577](https://github.com/klauspost/compress/pull/577) + * inflate: Keep dict on stack [#581](https://github.com/klauspost/compress/pull/581) + * zstd: Faster decoding memcopy in asm [#583](https://github.com/klauspost/compress/pull/583) + * zstd: Fix ignored crc [#580](https://github.com/klauspost/compress/pull/580) + +* May 5, 2022 (v1.15.3) + * zstd: Allow to ignore checksum checking by @WojciechMula [#572](https://github.com/klauspost/compress/pull/572) + * s2: Fix incorrect seek for io.SeekEnd in [#575](https://github.com/klauspost/compress/pull/575) + +* Apr 26, 2022 (v1.15.2) + * zstd: Add x86-64 assembly for decompression on streams and blocks. Contributed by [@WojciechMula](https://github.com/WojciechMula). Typically 2x faster. [#528](https://github.com/klauspost/compress/pull/528) [#531](https://github.com/klauspost/compress/pull/531) [#545](https://github.com/klauspost/compress/pull/545) [#537](https://github.com/klauspost/compress/pull/537) + * zstd: Add options to ZipDecompressor and fixes [#539](https://github.com/klauspost/compress/pull/539) + * s2: Use sorted search for index [#555](https://github.com/klauspost/compress/pull/555) + * Minimum version is Go 1.16, added CI test on 1.18. + +* Mar 11, 2022 (v1.15.1) + * huff0: Add x86 assembly of Decode4X by @WojciechMula in [#512](https://github.com/klauspost/compress/pull/512) + * zstd: Reuse zip decoders in [#514](https://github.com/klauspost/compress/pull/514) + * zstd: Detect extra block data and report as corrupted in [#520](https://github.com/klauspost/compress/pull/520) + * zstd: Handle zero sized frame content size stricter in [#521](https://github.com/klauspost/compress/pull/521) + * zstd: Add stricter block size checks in [#523](https://github.com/klauspost/compress/pull/523) + +* Mar 3, 2022 (v1.15.0) + * zstd: Refactor decoder [#498](https://github.com/klauspost/compress/pull/498) + * zstd: Add stream encoding without goroutines [#505](https://github.com/klauspost/compress/pull/505) + * huff0: Prevent single blocks exceeding 16 bits by @klauspost in[#507](https://github.com/klauspost/compress/pull/507) + * flate: Inline literal emission [#509](https://github.com/klauspost/compress/pull/509) + * gzhttp: Add zstd to transport [#400](https://github.com/klauspost/compress/pull/400) + * gzhttp: Make content-type optional [#510](https://github.com/klauspost/compress/pull/510) + +Both compression and decompression now supports "synchronous" stream operations. This means that whenever "concurrency" is set to 1, they will operate without spawning goroutines. + +Stream decompression is now faster on asynchronous, since the goroutine allocation much more effectively splits the workload. On typical streams this will typically use 2 cores fully for decompression. When a stream has finished decoding no goroutines will be left over, so decoders can now safely be pooled and still be garbage collected. + +While the release has been extensively tested, it is recommended to testing when upgrading. + +
+ +
+ See changes to v1.14.x + +* Feb 22, 2022 (v1.14.4) + * flate: Fix rare huffman only (-2) corruption. [#503](https://github.com/klauspost/compress/pull/503) + * zip: Update deprecated CreateHeaderRaw to correctly call CreateRaw by @saracen in [#502](https://github.com/klauspost/compress/pull/502) + * zip: don't read data descriptor early by @saracen in [#501](https://github.com/klauspost/compress/pull/501) #501 + * huff0: Use static decompression buffer up to 30% faster [#499](https://github.com/klauspost/compress/pull/499) [#500](https://github.com/klauspost/compress/pull/500) + +* Feb 17, 2022 (v1.14.3) + * flate: Improve fastest levels compression speed ~10% more throughput. [#482](https://github.com/klauspost/compress/pull/482) [#489](https://github.com/klauspost/compress/pull/489) [#490](https://github.com/klauspost/compress/pull/490) [#491](https://github.com/klauspost/compress/pull/491) [#494](https://github.com/klauspost/compress/pull/494) [#478](https://github.com/klauspost/compress/pull/478) + * flate: Faster decompression speed, ~5-10%. [#483](https://github.com/klauspost/compress/pull/483) + * s2: Faster compression with Go v1.18 and amd64 microarch level 3+. [#484](https://github.com/klauspost/compress/pull/484) [#486](https://github.com/klauspost/compress/pull/486) + +* Jan 25, 2022 (v1.14.2) + * zstd: improve header decoder by @dsnet [#476](https://github.com/klauspost/compress/pull/476) + * zstd: Add bigger default blocks [#469](https://github.com/klauspost/compress/pull/469) + * zstd: Remove unused decompression buffer [#470](https://github.com/klauspost/compress/pull/470) + * zstd: Fix logically dead code by @ningmingxiao [#472](https://github.com/klauspost/compress/pull/472) + * flate: Improve level 7-9 [#471](https://github.com/klauspost/compress/pull/471) [#473](https://github.com/klauspost/compress/pull/473) + * zstd: Add noasm tag for xxhash [#475](https://github.com/klauspost/compress/pull/475) + +* Jan 11, 2022 (v1.14.1) + * s2: Add stream index in [#462](https://github.com/klauspost/compress/pull/462) + * flate: Speed and efficiency improvements in [#439](https://github.com/klauspost/compress/pull/439) [#461](https://github.com/klauspost/compress/pull/461) [#455](https://github.com/klauspost/compress/pull/455) [#452](https://github.com/klauspost/compress/pull/452) [#458](https://github.com/klauspost/compress/pull/458) + * zstd: Performance improvement in [#420]( https://github.com/klauspost/compress/pull/420) [#456](https://github.com/klauspost/compress/pull/456) [#437](https://github.com/klauspost/compress/pull/437) [#467](https://github.com/klauspost/compress/pull/467) [#468](https://github.com/klauspost/compress/pull/468) + * zstd: add arm64 xxhash assembly in [#464](https://github.com/klauspost/compress/pull/464) + * Add garbled for binaries for s2 in [#445](https://github.com/klauspost/compress/pull/445) +
+ +
+ See changes to v1.13.x + +* Aug 30, 2021 (v1.13.5) + * gz/zlib/flate: Alias stdlib errors [#425](https://github.com/klauspost/compress/pull/425) + * s2: Add block support to commandline tools [#413](https://github.com/klauspost/compress/pull/413) + * zstd: pooledZipWriter should return Writers to the same pool [#426](https://github.com/klauspost/compress/pull/426) + * Removed golang/snappy as external dependency for tests [#421](https://github.com/klauspost/compress/pull/421) + +* Aug 12, 2021 (v1.13.4) + * Add [snappy replacement package](https://github.com/klauspost/compress/tree/master/snappy). + * zstd: Fix incorrect encoding in "best" mode [#415](https://github.com/klauspost/compress/pull/415) + +* Aug 3, 2021 (v1.13.3) + * zstd: Improve Best compression [#404](https://github.com/klauspost/compress/pull/404) + * zstd: Fix WriteTo error forwarding [#411](https://github.com/klauspost/compress/pull/411) + * gzhttp: Return http.HandlerFunc instead of http.Handler. Unlikely breaking change. [#406](https://github.com/klauspost/compress/pull/406) + * s2sx: Fix max size error [#399](https://github.com/klauspost/compress/pull/399) + * zstd: Add optional stream content size on reset [#401](https://github.com/klauspost/compress/pull/401) + * zstd: use SpeedBestCompression for level >= 10 [#410](https://github.com/klauspost/compress/pull/410) + +* Jun 14, 2021 (v1.13.1) + * s2: Add full Snappy output support [#396](https://github.com/klauspost/compress/pull/396) + * zstd: Add configurable [Decoder window](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithDecoderMaxWindow) size [#394](https://github.com/klauspost/compress/pull/394) + * gzhttp: Add header to skip compression [#389](https://github.com/klauspost/compress/pull/389) + * s2: Improve speed with bigger output margin [#395](https://github.com/klauspost/compress/pull/395) + +* Jun 3, 2021 (v1.13.0) + * Added [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp#gzip-handler) which allows wrapping HTTP servers and clients with GZIP compressors. + * zstd: Detect short invalid signatures [#382](https://github.com/klauspost/compress/pull/382) + * zstd: Spawn decoder goroutine only if needed. [#380](https://github.com/klauspost/compress/pull/380) +
+ + +
+ See changes to v1.12.x + +* May 25, 2021 (v1.12.3) + * deflate: Better/faster Huffman encoding [#374](https://github.com/klauspost/compress/pull/374) + * deflate: Allocate less for history. [#375](https://github.com/klauspost/compress/pull/375) + * zstd: Forward read errors [#373](https://github.com/klauspost/compress/pull/373) + +* Apr 27, 2021 (v1.12.2) + * zstd: Improve better/best compression [#360](https://github.com/klauspost/compress/pull/360) [#364](https://github.com/klauspost/compress/pull/364) [#365](https://github.com/klauspost/compress/pull/365) + * zstd: Add helpers to compress/decompress zstd inside zip files [#363](https://github.com/klauspost/compress/pull/363) + * deflate: Improve level 5+6 compression [#367](https://github.com/klauspost/compress/pull/367) + * s2: Improve better/best compression [#358](https://github.com/klauspost/compress/pull/358) [#359](https://github.com/klauspost/compress/pull/358) + * s2: Load after checking src limit on amd64. [#362](https://github.com/klauspost/compress/pull/362) + * s2sx: Limit max executable size [#368](https://github.com/klauspost/compress/pull/368) + +* Apr 14, 2021 (v1.12.1) + * snappy package removed. Upstream added as dependency. + * s2: Better compression in "best" mode [#353](https://github.com/klauspost/compress/pull/353) + * s2sx: Add stdin input and detect pre-compressed from signature [#352](https://github.com/klauspost/compress/pull/352) + * s2c/s2d: Add http as possible input [#348](https://github.com/klauspost/compress/pull/348) + * s2c/s2d/s2sx: Always truncate when writing files [#352](https://github.com/klauspost/compress/pull/352) + * zstd: Reduce memory usage further when using [WithLowerEncoderMem](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithLowerEncoderMem) [#346](https://github.com/klauspost/compress/pull/346) + * s2: Fix potential problem with amd64 assembly and profilers [#349](https://github.com/klauspost/compress/pull/349) +
+ +
+ See changes to v1.11.x + +* Mar 26, 2021 (v1.11.13) + * zstd: Big speedup on small dictionary encodes [#344](https://github.com/klauspost/compress/pull/344) [#345](https://github.com/klauspost/compress/pull/345) + * zstd: Add [WithLowerEncoderMem](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithLowerEncoderMem) encoder option [#336](https://github.com/klauspost/compress/pull/336) + * deflate: Improve entropy compression [#338](https://github.com/klauspost/compress/pull/338) + * s2: Clean up and minor performance improvement in best [#341](https://github.com/klauspost/compress/pull/341) + +* Mar 5, 2021 (v1.11.12) + * s2: Add `s2sx` binary that creates [self extracting archives](https://github.com/klauspost/compress/tree/master/s2#s2sx-self-extracting-archives). + * s2: Speed up decompression on non-assembly platforms [#328](https://github.com/klauspost/compress/pull/328) + +* Mar 1, 2021 (v1.11.9) + * s2: Add ARM64 decompression assembly. Around 2x output speed. [#324](https://github.com/klauspost/compress/pull/324) + * s2: Improve "better" speed and efficiency. [#325](https://github.com/klauspost/compress/pull/325) + * s2: Fix binaries. + +* Feb 25, 2021 (v1.11.8) + * s2: Fixed occasional out-of-bounds write on amd64. Upgrade recommended. + * s2: Add AMD64 assembly for better mode. 25-50% faster. [#315](https://github.com/klauspost/compress/pull/315) + * s2: Less upfront decoder allocation. [#322](https://github.com/klauspost/compress/pull/322) + * zstd: Faster "compression" of incompressible data. [#314](https://github.com/klauspost/compress/pull/314) + * zip: Fix zip64 headers. [#313](https://github.com/klauspost/compress/pull/313) + +* Jan 14, 2021 (v1.11.7) + * Use Bytes() interface to get bytes across packages. [#309](https://github.com/klauspost/compress/pull/309) + * s2: Add 'best' compression option. [#310](https://github.com/klauspost/compress/pull/310) + * s2: Add ReaderMaxBlockSize, changes `s2.NewReader` signature to include varargs. [#311](https://github.com/klauspost/compress/pull/311) + * s2: Fix crash on small better buffers. [#308](https://github.com/klauspost/compress/pull/308) + * s2: Clean up decoder. [#312](https://github.com/klauspost/compress/pull/312) + +* Jan 7, 2021 (v1.11.6) + * zstd: Make decoder allocations smaller [#306](https://github.com/klauspost/compress/pull/306) + * zstd: Free Decoder resources when Reset is called with a nil io.Reader [#305](https://github.com/klauspost/compress/pull/305) + +* Dec 20, 2020 (v1.11.4) + * zstd: Add Best compression mode [#304](https://github.com/klauspost/compress/pull/304) + * Add header decoder [#299](https://github.com/klauspost/compress/pull/299) + * s2: Add uncompressed stream option [#297](https://github.com/klauspost/compress/pull/297) + * Simplify/speed up small blocks with known max size. [#300](https://github.com/klauspost/compress/pull/300) + * zstd: Always reset literal dict encoder [#303](https://github.com/klauspost/compress/pull/303) + +* Nov 15, 2020 (v1.11.3) + * inflate: 10-15% faster decompression [#293](https://github.com/klauspost/compress/pull/293) + * zstd: Tweak DecodeAll default allocation [#295](https://github.com/klauspost/compress/pull/295) + +* Oct 11, 2020 (v1.11.2) + * s2: Fix out of bounds read in "better" block compression [#291](https://github.com/klauspost/compress/pull/291) + +* Oct 1, 2020 (v1.11.1) + * zstd: Set allLitEntropy true in default configuration [#286](https://github.com/klauspost/compress/pull/286) + +* Sept 8, 2020 (v1.11.0) + * zstd: Add experimental compression [dictionaries](https://github.com/klauspost/compress/tree/master/zstd#dictionaries) [#281](https://github.com/klauspost/compress/pull/281) + * zstd: Fix mixed Write and ReadFrom calls [#282](https://github.com/klauspost/compress/pull/282) + * inflate/gz: Limit variable shifts, ~5% faster decompression [#274](https://github.com/klauspost/compress/pull/274) +
+ +
+ See changes to v1.10.x + +* July 8, 2020 (v1.10.11) + * zstd: Fix extra block when compressing with ReadFrom. [#278](https://github.com/klauspost/compress/pull/278) + * huff0: Also populate compression table when reading decoding table. [#275](https://github.com/klauspost/compress/pull/275) + +* June 23, 2020 (v1.10.10) + * zstd: Skip entropy compression in fastest mode when no matches. [#270](https://github.com/klauspost/compress/pull/270) + +* June 16, 2020 (v1.10.9): + * zstd: API change for specifying dictionaries. See [#268](https://github.com/klauspost/compress/pull/268) + * zip: update CreateHeaderRaw to handle zip64 fields. [#266](https://github.com/klauspost/compress/pull/266) + * Fuzzit tests removed. The service has been purchased and is no longer available. + +* June 5, 2020 (v1.10.8): + * 1.15x faster zstd block decompression. [#265](https://github.com/klauspost/compress/pull/265) + +* June 1, 2020 (v1.10.7): + * Added zstd decompression [dictionary support](https://github.com/klauspost/compress/tree/master/zstd#dictionaries) + * Increase zstd decompression speed up to 1.19x. [#259](https://github.com/klauspost/compress/pull/259) + * Remove internal reset call in zstd compression and reduce allocations. [#263](https://github.com/klauspost/compress/pull/263) + +* May 21, 2020: (v1.10.6) + * zstd: Reduce allocations while decoding. [#258](https://github.com/klauspost/compress/pull/258), [#252](https://github.com/klauspost/compress/pull/252) + * zstd: Stricter decompression checks. + +* April 12, 2020: (v1.10.5) + * s2-commands: Flush output when receiving SIGINT. [#239](https://github.com/klauspost/compress/pull/239) + +* Apr 8, 2020: (v1.10.4) + * zstd: Minor/special case optimizations. [#251](https://github.com/klauspost/compress/pull/251), [#250](https://github.com/klauspost/compress/pull/250), [#249](https://github.com/klauspost/compress/pull/249), [#247](https://github.com/klauspost/compress/pull/247) +* Mar 11, 2020: (v1.10.3) + * s2: Use S2 encoder in pure Go mode for Snappy output as well. [#245](https://github.com/klauspost/compress/pull/245) + * s2: Fix pure Go block encoder. [#244](https://github.com/klauspost/compress/pull/244) + * zstd: Added "better compression" mode. [#240](https://github.com/klauspost/compress/pull/240) + * zstd: Improve speed of fastest compression mode by 5-10% [#241](https://github.com/klauspost/compress/pull/241) + * zstd: Skip creating encoders when not needed. [#238](https://github.com/klauspost/compress/pull/238) + +* Feb 27, 2020: (v1.10.2) + * Close to 50% speedup in inflate (gzip/zip decompression). [#236](https://github.com/klauspost/compress/pull/236) [#234](https://github.com/klauspost/compress/pull/234) [#232](https://github.com/klauspost/compress/pull/232) + * Reduce deflate level 1-6 memory usage up to 59%. [#227](https://github.com/klauspost/compress/pull/227) + +* Feb 18, 2020: (v1.10.1) + * Fix zstd crash when resetting multiple times without sending data. [#226](https://github.com/klauspost/compress/pull/226) + * deflate: Fix dictionary use on level 1-6. [#224](https://github.com/klauspost/compress/pull/224) + * Remove deflate writer reference when closing. [#224](https://github.com/klauspost/compress/pull/224) + +* Feb 4, 2020: (v1.10.0) + * Add optional dictionary to [stateless deflate](https://pkg.go.dev/github.com/klauspost/compress/flate?tab=doc#StatelessDeflate). Breaking change, send `nil` for previous behaviour. [#216](https://github.com/klauspost/compress/pull/216) + * Fix buffer overflow on repeated small block deflate. [#218](https://github.com/klauspost/compress/pull/218) + * Allow copying content from an existing ZIP file without decompressing+compressing. [#214](https://github.com/klauspost/compress/pull/214) + * Added [S2](https://github.com/klauspost/compress/tree/master/s2#s2-compression) AMD64 assembler and various optimizations. Stream speed >10GB/s. [#186](https://github.com/klauspost/compress/pull/186) + +
+ +
+ See changes prior to v1.10.0 + +* Jan 20,2020 (v1.9.8) Optimize gzip/deflate with better size estimates and faster table generation. [#207](https://github.com/klauspost/compress/pull/207) by [luyu6056](https://github.com/luyu6056), [#206](https://github.com/klauspost/compress/pull/206). +* Jan 11, 2020: S2 Encode/Decode will use provided buffer if capacity is big enough. [#204](https://github.com/klauspost/compress/pull/204) +* Jan 5, 2020: (v1.9.7) Fix another zstd regression in v1.9.5 - v1.9.6 removed. +* Jan 4, 2020: (v1.9.6) Regression in v1.9.5 fixed causing corrupt zstd encodes in rare cases. +* Jan 4, 2020: Faster IO in [s2c + s2d commandline tools](https://github.com/klauspost/compress/tree/master/s2#commandline-tools) compression/decompression. [#192](https://github.com/klauspost/compress/pull/192) +* Dec 29, 2019: Removed v1.9.5 since fuzz tests showed a compatibility problem with the reference zstandard decoder. +* Dec 29, 2019: (v1.9.5) zstd: 10-20% faster block compression. [#199](https://github.com/klauspost/compress/pull/199) +* Dec 29, 2019: [zip](https://godoc.org/github.com/klauspost/compress/zip) package updated with latest Go features +* Dec 29, 2019: zstd: Single segment flag condintions tweaked. [#197](https://github.com/klauspost/compress/pull/197) +* Dec 18, 2019: s2: Faster compression when ReadFrom is used. [#198](https://github.com/klauspost/compress/pull/198) +* Dec 10, 2019: s2: Fix repeat length output when just above at 16MB limit. +* Dec 10, 2019: zstd: Add function to get decoder as io.ReadCloser. [#191](https://github.com/klauspost/compress/pull/191) +* Dec 3, 2019: (v1.9.4) S2: limit max repeat length. [#188](https://github.com/klauspost/compress/pull/188) +* Dec 3, 2019: Add [WithNoEntropyCompression](https://godoc.org/github.com/klauspost/compress/zstd#WithNoEntropyCompression) to zstd [#187](https://github.com/klauspost/compress/pull/187) +* Dec 3, 2019: Reduce memory use for tests. Check for leaked goroutines. +* Nov 28, 2019 (v1.9.3) Less allocations in stateless deflate. +* Nov 28, 2019: 5-20% Faster huff0 decode. Impacts zstd as well. [#184](https://github.com/klauspost/compress/pull/184) +* Nov 12, 2019 (v1.9.2) Added [Stateless Compression](#stateless-compression) for gzip/deflate. +* Nov 12, 2019: Fixed zstd decompression of large single blocks. [#180](https://github.com/klauspost/compress/pull/180) +* Nov 11, 2019: Set default [s2c](https://github.com/klauspost/compress/tree/master/s2#commandline-tools) block size to 4MB. +* Nov 11, 2019: Reduce inflate memory use by 1KB. +* Nov 10, 2019: Less allocations in deflate bit writer. +* Nov 10, 2019: Fix inconsistent error returned by zstd decoder. +* Oct 28, 2019 (v1.9.1) ztsd: Fix crash when compressing blocks. [#174](https://github.com/klauspost/compress/pull/174) +* Oct 24, 2019 (v1.9.0) zstd: Fix rare data corruption [#173](https://github.com/klauspost/compress/pull/173) +* Oct 24, 2019 zstd: Fix huff0 out of buffer write [#171](https://github.com/klauspost/compress/pull/171) and always return errors [#172](https://github.com/klauspost/compress/pull/172) +* Oct 10, 2019: Big deflate rewrite, 30-40% faster with better compression [#105](https://github.com/klauspost/compress/pull/105) + +
+ +
+ See changes prior to v1.9.0 + +* Oct 10, 2019: (v1.8.6) zstd: Allow partial reads to get flushed data. [#169](https://github.com/klauspost/compress/pull/169) +* Oct 3, 2019: Fix inconsistent results on broken zstd streams. +* Sep 25, 2019: Added `-rm` (remove source files) and `-q` (no output except errors) to `s2c` and `s2d` [commands](https://github.com/klauspost/compress/tree/master/s2#commandline-tools) +* Sep 16, 2019: (v1.8.4) Add `s2c` and `s2d` [commandline tools](https://github.com/klauspost/compress/tree/master/s2#commandline-tools). +* Sep 10, 2019: (v1.8.3) Fix s2 decoder [Skip](https://godoc.org/github.com/klauspost/compress/s2#Reader.Skip). +* Sep 7, 2019: zstd: Added [WithWindowSize](https://godoc.org/github.com/klauspost/compress/zstd#WithWindowSize), contributed by [ianwilkes](https://github.com/ianwilkes). +* Sep 5, 2019: (v1.8.2) Add [WithZeroFrames](https://godoc.org/github.com/klauspost/compress/zstd#WithZeroFrames) which adds full zero payload block encoding option. +* Sep 5, 2019: Lazy initialization of zstandard predefined en/decoder tables. +* Aug 26, 2019: (v1.8.1) S2: 1-2% compression increase in "better" compression mode. +* Aug 26, 2019: zstd: Check maximum size of Huffman 1X compressed literals while decoding. +* Aug 24, 2019: (v1.8.0) Added [S2 compression](https://github.com/klauspost/compress/tree/master/s2#s2-compression), a high performance replacement for Snappy. +* Aug 21, 2019: (v1.7.6) Fixed minor issues found by fuzzer. One could lead to zstd not decompressing. +* Aug 18, 2019: Add [fuzzit](https://fuzzit.dev/) continuous fuzzing. +* Aug 14, 2019: zstd: Skip incompressible data 2x faster. [#147](https://github.com/klauspost/compress/pull/147) +* Aug 4, 2019 (v1.7.5): Better literal compression. [#146](https://github.com/klauspost/compress/pull/146) +* Aug 4, 2019: Faster zstd compression. [#143](https://github.com/klauspost/compress/pull/143) [#144](https://github.com/klauspost/compress/pull/144) +* Aug 4, 2019: Faster zstd decompression. [#145](https://github.com/klauspost/compress/pull/145) [#143](https://github.com/klauspost/compress/pull/143) [#142](https://github.com/klauspost/compress/pull/142) +* July 15, 2019 (v1.7.4): Fix double EOF block in rare cases on zstd encoder. +* July 15, 2019 (v1.7.3): Minor speedup/compression increase in default zstd encoder. +* July 14, 2019: zstd decoder: Fix decompression error on multiple uses with mixed content. +* July 7, 2019 (v1.7.2): Snappy update, zstd decoder potential race fix. +* June 17, 2019: zstd decompression bugfix. +* June 17, 2019: fix 32 bit builds. +* June 17, 2019: Easier use in modules (less dependencies). +* June 9, 2019: New stronger "default" [zstd](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression mode. Matches zstd default compression ratio. +* June 5, 2019: 20-40% throughput in [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and better compression. +* June 5, 2019: deflate/gzip compression: Reduce memory usage of lower compression levels. +* June 2, 2019: Added [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression! +* May 25, 2019: deflate/gzip: 10% faster bit writer, mostly visible in lower levels. +* Apr 22, 2019: [zstd](https://github.com/klauspost/compress/tree/master/zstd#zstd) decompression added. +* Aug 1, 2018: Added [huff0 README](https://github.com/klauspost/compress/tree/master/huff0#huff0-entropy-compression). +* Jul 8, 2018: Added [Performance Update 2018](#performance-update-2018) below. +* Jun 23, 2018: Merged [Go 1.11 inflate optimizations](https://go-review.googlesource.com/c/go/+/102235). Go 1.9 is now required. Backwards compatible version tagged with [v1.3.0](https://github.com/klauspost/compress/releases/tag/v1.3.0). +* Apr 2, 2018: Added [huff0](https://godoc.org/github.com/klauspost/compress/huff0) en/decoder. Experimental for now, API may change. +* Mar 4, 2018: Added [FSE Entropy](https://godoc.org/github.com/klauspost/compress/fse) en/decoder. Experimental for now, API may change. +* Nov 3, 2017: Add compression [Estimate](https://godoc.org/github.com/klauspost/compress#Estimate) function. +* May 28, 2017: Reduce allocations when resetting decoder. +* Apr 02, 2017: Change back to official crc32, since changes were merged in Go 1.7. +* Jan 14, 2017: Reduce stack pressure due to array copies. See [Issue #18625](https://github.com/golang/go/issues/18625). +* Oct 25, 2016: Level 2-4 have been rewritten and now offers significantly better performance than before. +* Oct 20, 2016: Port zlib changes from Go 1.7 to fix zlib writer issue. Please update. +* Oct 16, 2016: Go 1.7 changes merged. Apples to apples this package is a few percent faster, but has a significantly better balance between speed and compression per level. +* Mar 24, 2016: Always attempt Huffman encoding on level 4-7. This improves base 64 encoded data compression. +* Mar 24, 2016: Small speedup for level 1-3. +* Feb 19, 2016: Faster bit writer, level -2 is 15% faster, level 1 is 4% faster. +* Feb 19, 2016: Handle small payloads faster in level 1-3. +* Feb 19, 2016: Added faster level 2 + 3 compression modes. +* Feb 19, 2016: [Rebalanced compression levels](https://blog.klauspost.com/rebalancing-deflate-compression-levels/), so there is a more even progression in terms of compression. New default level is 5. +* Feb 14, 2016: Snappy: Merge upstream changes. +* Feb 14, 2016: Snappy: Fix aggressive skipping. +* Feb 14, 2016: Snappy: Update benchmark. +* Feb 13, 2016: Deflate: Fixed assembler problem that could lead to sub-optimal compression. +* Feb 12, 2016: Snappy: Added AMD64 SSE 4.2 optimizations to matching, which makes easy to compress material run faster. Typical speedup is around 25%. +* Feb 9, 2016: Added Snappy package fork. This version is 5-7% faster, much more on hard to compress content. +* Jan 30, 2016: Optimize level 1 to 3 by not considering static dictionary or storing uncompressed. ~4-5% speedup. +* Jan 16, 2016: Optimization on deflate level 1,2,3 compression. +* Jan 8 2016: Merge [CL 18317](https://go-review.googlesource.com/#/c/18317): fix reading, writing of zip64 archives. +* Dec 8 2015: Make level 1 and -2 deterministic even if write size differs. +* Dec 8 2015: Split encoding functions, so hashing and matching can potentially be inlined. 1-3% faster on AMD64. 5% faster on other platforms. +* Dec 8 2015: Fixed rare [one byte out-of bounds read](https://github.com/klauspost/compress/issues/20). Please update! +* Nov 23 2015: Optimization on token writer. ~2-4% faster. Contributed by [@dsnet](https://github.com/dsnet). +* Nov 20 2015: Small optimization to bit writer on 64 bit systems. +* Nov 17 2015: Fixed out-of-bound errors if the underlying Writer returned an error. See [#15](https://github.com/klauspost/compress/issues/15). +* Nov 12 2015: Added [io.WriterTo](https://golang.org/pkg/io/#WriterTo) support to gzip/inflate. +* Nov 11 2015: Merged [CL 16669](https://go-review.googlesource.com/#/c/16669/4): archive/zip: enable overriding (de)compressors per file +* Oct 15 2015: Added skipping on uncompressible data. Random data speed up >5x. + +
+ +# deflate usage + +The packages are drop-in replacements for standard library [deflate](https://godoc.org/github.com/klauspost/compress/flate), [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip), and [zlib](https://godoc.org/github.com/klauspost/compress/zlib). Simply replace the import path to use them: + +Typical speed is about 2x of the standard library packages. + +| old import | new import | Documentation | +|------------------|---------------------------------------|-------------------------------------------------------------------------| +| `compress/gzip` | `github.com/klauspost/compress/gzip` | [gzip](https://pkg.go.dev/github.com/klauspost/compress/gzip?tab=doc) | +| `compress/zlib` | `github.com/klauspost/compress/zlib` | [zlib](https://pkg.go.dev/github.com/klauspost/compress/zlib?tab=doc) | +| `archive/zip` | `github.com/klauspost/compress/zip` | [zip](https://pkg.go.dev/github.com/klauspost/compress/zip?tab=doc) | +| `compress/flate` | `github.com/klauspost/compress/flate` | [flate](https://pkg.go.dev/github.com/klauspost/compress/flate?tab=doc) | + +You may also be interested in [pgzip](https://github.com/klauspost/pgzip), which is a drop-in replacement for gzip, which support multithreaded compression on big files and the optimized [crc32](https://github.com/klauspost/crc32) package used by these packages. + +The packages implement the same API as the standard library, so you can use the original godoc documentation: [gzip](http://golang.org/pkg/compress/gzip/), [zip](http://golang.org/pkg/archive/zip/), [zlib](http://golang.org/pkg/compress/zlib/), [flate](http://golang.org/pkg/compress/flate/). + +Currently there is only minor speedup on decompression (mostly CRC32 calculation). + +Memory usage is typically 1MB for a Writer. stdlib is in the same range. +If you expect to have a lot of concurrently allocated Writers consider using +the stateless compression described below. + +For compression performance, see: [this spreadsheet](https://docs.google.com/spreadsheets/d/1nuNE2nPfuINCZJRMt6wFWhKpToF95I47XjSsc-1rbPQ/edit?usp=sharing). + +To disable all assembly add `-tags=noasm`. This works across all packages. + +# Stateless compression + +This package offers stateless compression as a special option for gzip/deflate. +It will do compression but without maintaining any state between Write calls. + +This means there will be no memory kept between Write calls, but compression and speed will be suboptimal. + +This is only relevant in cases where you expect to run many thousands of compressors concurrently, +but with very little activity. This is *not* intended for regular web servers serving individual requests. + +Because of this, the size of actual Write calls will affect output size. + +In gzip, specify level `-3` / `gzip.StatelessCompression` to enable. + +For direct deflate use, NewStatelessWriter and StatelessDeflate are available. See [documentation](https://godoc.org/github.com/klauspost/compress/flate#NewStatelessWriter) + +A `bufio.Writer` can of course be used to control write sizes. For example, to use a 4KB buffer: + +```go + // replace 'ioutil.Discard' with your output. + gzw, err := gzip.NewWriterLevel(ioutil.Discard, gzip.StatelessCompression) + if err != nil { + return err + } + defer gzw.Close() + + w := bufio.NewWriterSize(gzw, 4096) + defer w.Flush() + + // Write to 'w' +``` + +This will only use up to 4KB in memory when the writer is idle. + +Compression is almost always worse than the fastest compression level +and each write will allocate (a little) memory. + + +# Other packages + +Here are other packages of good quality and pure Go (no cgo wrappers or autoconverted code): + +* [github.com/pierrec/lz4](https://github.com/pierrec/lz4) - strong multithreaded LZ4 compression. +* [github.com/cosnicolaou/pbzip2](https://github.com/cosnicolaou/pbzip2) - multithreaded bzip2 decompression. +* [github.com/dsnet/compress](https://github.com/dsnet/compress) - brotli decompression, bzip2 writer. +* [github.com/ronanh/intcomp](https://github.com/ronanh/intcomp) - Integer compression. +* [github.com/spenczar/fpc](https://github.com/spenczar/fpc) - Float compression. +* [github.com/minio/zipindex](https://github.com/minio/zipindex) - External ZIP directory index. +* [github.com/ybirader/pzip](https://github.com/ybirader/pzip) - Fast concurrent zip archiver and extractor. + +# license + +This code is licensed under the same conditions as the original Go code. See LICENSE file. + + + + + diff --git a/vendor/github.com/klauspost/compress/fse/README.md b/vendor/github.com/klauspost/compress/fse/README.md index ea7324da67..27d8ed56fc 100644 --- a/vendor/github.com/klauspost/compress/fse/README.md +++ b/vendor/github.com/klauspost/compress/fse/README.md @@ -1,79 +1,79 @@ -# Finite State Entropy - -This package provides Finite State Entropy encoding and decoding. - -Finite State Entropy (also referenced as [tANS](https://en.wikipedia.org/wiki/Asymmetric_numeral_systems#tANS)) -encoding provides a fast near-optimal symbol encoding/decoding -for byte blocks as implemented in [zstandard](https://github.com/facebook/zstd). - -This can be used for compressing input with a lot of similar input values to the smallest number of bytes. -This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders, -but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding. - -* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/fse) - -## News - - * Feb 2018: First implementation released. Consider this beta software for now. - -# Usage - -This package provides a low level interface that allows to compress single independent blocks. - -Each block is separate, and there is no built in integrity checks. -This means that the caller should keep track of block sizes and also do checksums if needed. - -Compressing a block is done via the [`Compress`](https://godoc.org/github.com/klauspost/compress/fse#Compress) function. -You must provide input and will receive the output and maybe an error. - -These error values can be returned: - -| Error | Description | -|---------------------|-----------------------------------------------------------------------------| -| `` | Everything ok, output is returned | -| `ErrIncompressible` | Returned when input is judged to be too hard to compress | -| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated | -| `(error)` | An internal error occurred. | - -As can be seen above there are errors that will be returned even under normal operation so it is important to handle these. - -To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/fse#Scratch) object -that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same -object can be used for both. - -Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this -you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output. - -Decompressing is done by calling the [`Decompress`](https://godoc.org/github.com/klauspost/compress/fse#Decompress) function. -You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back -your input was likely corrupted. - -It is important to note that a successful decoding does *not* mean your output matches your original input. -There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid. - -For more detailed usage, see examples in the [godoc documentation](https://godoc.org/github.com/klauspost/compress/fse#pkg-examples). - -# Performance - -A lot of factors are affecting speed. Block sizes and compressibility of the material are primary factors. -All compression functions are currently only running on the calling goroutine so only one core will be used per block. - -The compressor is significantly faster if symbols are kept as small as possible. The highest byte value of the input -is used to reduce some of the processing, so if all your input is above byte value 64 for instance, it may be -beneficial to transpose all your input values down by 64. - -With moderate block sizes around 64k speed are typically 200MB/s per core for compression and -around 300MB/s decompression speed. - -The same hardware typically does Huffman (deflate) encoding at 125MB/s and decompression at 100MB/s. - -# Plans - -At one point, more internals will be exposed to facilitate more "expert" usage of the components. - -A streaming interface is also likely to be implemented. Likely compatible with [FSE stream format](https://github.com/Cyan4973/FiniteStateEntropy/blob/dev/programs/fileio.c#L261). - -# Contributing - -Contributions are always welcome. Be aware that adding public functions will require good justification and breaking +# Finite State Entropy + +This package provides Finite State Entropy encoding and decoding. + +Finite State Entropy (also referenced as [tANS](https://en.wikipedia.org/wiki/Asymmetric_numeral_systems#tANS)) +encoding provides a fast near-optimal symbol encoding/decoding +for byte blocks as implemented in [zstandard](https://github.com/facebook/zstd). + +This can be used for compressing input with a lot of similar input values to the smallest number of bytes. +This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders, +but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding. + +* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/fse) + +## News + + * Feb 2018: First implementation released. Consider this beta software for now. + +# Usage + +This package provides a low level interface that allows to compress single independent blocks. + +Each block is separate, and there is no built in integrity checks. +This means that the caller should keep track of block sizes and also do checksums if needed. + +Compressing a block is done via the [`Compress`](https://godoc.org/github.com/klauspost/compress/fse#Compress) function. +You must provide input and will receive the output and maybe an error. + +These error values can be returned: + +| Error | Description | +|---------------------|-----------------------------------------------------------------------------| +| `` | Everything ok, output is returned | +| `ErrIncompressible` | Returned when input is judged to be too hard to compress | +| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated | +| `(error)` | An internal error occurred. | + +As can be seen above there are errors that will be returned even under normal operation so it is important to handle these. + +To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/fse#Scratch) object +that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same +object can be used for both. + +Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this +you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output. + +Decompressing is done by calling the [`Decompress`](https://godoc.org/github.com/klauspost/compress/fse#Decompress) function. +You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back +your input was likely corrupted. + +It is important to note that a successful decoding does *not* mean your output matches your original input. +There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid. + +For more detailed usage, see examples in the [godoc documentation](https://godoc.org/github.com/klauspost/compress/fse#pkg-examples). + +# Performance + +A lot of factors are affecting speed. Block sizes and compressibility of the material are primary factors. +All compression functions are currently only running on the calling goroutine so only one core will be used per block. + +The compressor is significantly faster if symbols are kept as small as possible. The highest byte value of the input +is used to reduce some of the processing, so if all your input is above byte value 64 for instance, it may be +beneficial to transpose all your input values down by 64. + +With moderate block sizes around 64k speed are typically 200MB/s per core for compression and +around 300MB/s decompression speed. + +The same hardware typically does Huffman (deflate) encoding at 125MB/s and decompression at 100MB/s. + +# Plans + +At one point, more internals will be exposed to facilitate more "expert" usage of the components. + +A streaming interface is also likely to be implemented. Likely compatible with [FSE stream format](https://github.com/Cyan4973/FiniteStateEntropy/blob/dev/programs/fileio.c#L261). + +# Contributing + +Contributions are always welcome. Be aware that adding public functions will require good justification and breaking changes will likely not be accepted. If in doubt open an issue before writing the PR. \ No newline at end of file diff --git a/vendor/github.com/klauspost/compress/huff0/README.md b/vendor/github.com/klauspost/compress/huff0/README.md index 8b6e5c6638..26d5101b36 100644 --- a/vendor/github.com/klauspost/compress/huff0/README.md +++ b/vendor/github.com/klauspost/compress/huff0/README.md @@ -1,89 +1,89 @@ -# Huff0 entropy compression - -This package provides Huff0 encoding and decoding as used in zstd. - -[Huff0](https://github.com/Cyan4973/FiniteStateEntropy#new-generation-entropy-coders), -a Huffman codec designed for modern CPU, featuring OoO (Out of Order) operations on multiple ALU -(Arithmetic Logic Unit), achieving extremely fast compression and decompression speeds. - -This can be used for compressing input with a lot of similar input values to the smallest number of bytes. -This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders, -but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding. - -* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/huff0) - -## News - -This is used as part of the [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression package. - -This ensures that most functionality is well tested. - -# Usage - -This package provides a low level interface that allows to compress single independent blocks. - -Each block is separate, and there is no built in integrity checks. -This means that the caller should keep track of block sizes and also do checksums if needed. - -Compressing a block is done via the [`Compress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress1X) and -[`Compress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress4X) functions. -You must provide input and will receive the output and maybe an error. - -These error values can be returned: - -| Error | Description | -|---------------------|-----------------------------------------------------------------------------| -| `` | Everything ok, output is returned | -| `ErrIncompressible` | Returned when input is judged to be too hard to compress | -| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated | -| `ErrTooBig` | Returned if the input block exceeds the maximum allowed size (128 Kib) | -| `(error)` | An internal error occurred. | - - -As can be seen above some of there are errors that will be returned even under normal operation so it is important to handle these. - -To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object -that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same -object can be used for both. - -Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this -you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output. - -The `Scratch` object will retain state that allows to re-use previous tables for encoding and decoding. - -## Tables and re-use - -Huff0 allows for reusing tables from the previous block to save space if that is expected to give better/faster results. - -The Scratch object allows you to set a [`ReusePolicy`](https://godoc.org/github.com/klauspost/compress/huff0#ReusePolicy) -that controls this behaviour. See the documentation for details. This can be altered between each block. - -Do however note that this information is *not* stored in the output block and it is up to the users of the package to -record whether [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable) should be called, -based on the boolean reported back from the CompressXX call. - -If you want to store the table separate from the data, you can access them as `OutData` and `OutTable` on the -[`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object. - -## Decompressing - -The first part of decoding is to initialize the decoding table through [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable). -This will initialize the decoding tables. -You can supply the complete block to `ReadTable` and it will return the data part of the block -which can be given to the decompressor. - -Decompressing is done by calling the [`Decompress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress1X) -or [`Decompress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress4X) function. - -For concurrently decompressing content with a fixed table a stateless [`Decoder`](https://godoc.org/github.com/klauspost/compress/huff0#Decoder) can be requested which will remain correct as long as the scratch is unchanged. The capacity of the provided slice indicates the expected output size. - -You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back -your input was likely corrupted. - -It is important to note that a successful decoding does *not* mean your output matches your original input. -There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid. - -# Contributing - -Contributions are always welcome. Be aware that adding public functions will require good justification and breaking -changes will likely not be accepted. If in doubt open an issue before writing the PR. +# Huff0 entropy compression + +This package provides Huff0 encoding and decoding as used in zstd. + +[Huff0](https://github.com/Cyan4973/FiniteStateEntropy#new-generation-entropy-coders), +a Huffman codec designed for modern CPU, featuring OoO (Out of Order) operations on multiple ALU +(Arithmetic Logic Unit), achieving extremely fast compression and decompression speeds. + +This can be used for compressing input with a lot of similar input values to the smallest number of bytes. +This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders, +but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding. + +* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/huff0) + +## News + +This is used as part of the [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression package. + +This ensures that most functionality is well tested. + +# Usage + +This package provides a low level interface that allows to compress single independent blocks. + +Each block is separate, and there is no built in integrity checks. +This means that the caller should keep track of block sizes and also do checksums if needed. + +Compressing a block is done via the [`Compress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress1X) and +[`Compress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress4X) functions. +You must provide input and will receive the output and maybe an error. + +These error values can be returned: + +| Error | Description | +|---------------------|-----------------------------------------------------------------------------| +| `` | Everything ok, output is returned | +| `ErrIncompressible` | Returned when input is judged to be too hard to compress | +| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated | +| `ErrTooBig` | Returned if the input block exceeds the maximum allowed size (128 Kib) | +| `(error)` | An internal error occurred. | + + +As can be seen above some of there are errors that will be returned even under normal operation so it is important to handle these. + +To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object +that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same +object can be used for both. + +Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this +you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output. + +The `Scratch` object will retain state that allows to re-use previous tables for encoding and decoding. + +## Tables and re-use + +Huff0 allows for reusing tables from the previous block to save space if that is expected to give better/faster results. + +The Scratch object allows you to set a [`ReusePolicy`](https://godoc.org/github.com/klauspost/compress/huff0#ReusePolicy) +that controls this behaviour. See the documentation for details. This can be altered between each block. + +Do however note that this information is *not* stored in the output block and it is up to the users of the package to +record whether [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable) should be called, +based on the boolean reported back from the CompressXX call. + +If you want to store the table separate from the data, you can access them as `OutData` and `OutTable` on the +[`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object. + +## Decompressing + +The first part of decoding is to initialize the decoding table through [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable). +This will initialize the decoding tables. +You can supply the complete block to `ReadTable` and it will return the data part of the block +which can be given to the decompressor. + +Decompressing is done by calling the [`Decompress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress1X) +or [`Decompress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress4X) function. + +For concurrently decompressing content with a fixed table a stateless [`Decoder`](https://godoc.org/github.com/klauspost/compress/huff0#Decoder) can be requested which will remain correct as long as the scratch is unchanged. The capacity of the provided slice indicates the expected output size. + +You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back +your input was likely corrupted. + +It is important to note that a successful decoding does *not* mean your output matches your original input. +There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid. + +# Contributing + +Contributions are always welcome. Be aware that adding public functions will require good justification and breaking +changes will likely not be accepted. If in doubt open an issue before writing the PR. diff --git a/vendor/github.com/klauspost/compress/s2/decode_amd64.s b/vendor/github.com/klauspost/compress/s2/decode_amd64.s index 9b105e03c5..1216df78f5 100644 --- a/vendor/github.com/klauspost/compress/s2/decode_amd64.s +++ b/vendor/github.com/klauspost/compress/s2/decode_amd64.s @@ -51,7 +51,7 @@ // // The d variable is implicitly R_DST - R_DBASE, and len(dst)-d is R_DEND - R_DST. // The s variable is implicitly R_SRC - R_SBASE, and len(src)-s is R_SEND - R_SRC. -TEXT ·s2Decode(SB), NOSPLIT, $48-56 +TEXT ·s2Decode(SB), NOSPLIT, $56-56 // Initialize R_SRC, R_DST and R_DBASE-R_SEND. MOVQ dst_base+0(FP), R_DBASE MOVQ dst_len+8(FP), R_DLEN diff --git a/vendor/github.com/klauspost/compress/s2/encode_best.go b/vendor/github.com/klauspost/compress/s2/encode_best.go index c857c5c283..49f2166ec1 100644 --- a/vendor/github.com/klauspost/compress/s2/encode_best.go +++ b/vendor/github.com/klauspost/compress/s2/encode_best.go @@ -23,12 +23,12 @@ func encodeBlockBest(dst, src []byte, dict *Dict) (d int) { // Initialize the hash tables. const ( // Long hash matches. - lTableBits = 19 - maxLTableSize = 1 << lTableBits + lTableBits = bestLongTableBits + maxLTableSize = bestLongTableSize // Short hash matches. - sTableBits = 16 - maxSTableSize = 1 << sTableBits + sTableBits = bestShortTableBits + maxSTableSize = bestShortTableSize inputMargin = 8 + 2 @@ -44,8 +44,10 @@ func encodeBlockBest(dst, src []byte, dict *Dict) (d int) { } sLimitDict := min(len(src)-inputMargin, MaxDictSrcOffset-inputMargin) - var lTable [maxLTableSize]uint64 - var sTable [maxSTableSize]uint64 + tbl := getBestTables() + lTable := &tbl.lTable + sTable := &tbl.sTable + defer bestTablePool.Put(tbl) // Bail if we can't compress to at least this. dstLimit := len(src) - 5 @@ -456,12 +458,12 @@ func encodeBlockBestSnappy(dst, src []byte) (d int) { // Initialize the hash tables. const ( // Long hash matches. - lTableBits = 19 - maxLTableSize = 1 << lTableBits + lTableBits = bestLongTableBits + maxLTableSize = bestLongTableSize // Short hash matches. - sTableBits = 16 - maxSTableSize = 1 << sTableBits + sTableBits = bestShortTableBits + maxSTableSize = bestShortTableSize inputMargin = 8 + 2 ) @@ -474,8 +476,10 @@ func encodeBlockBestSnappy(dst, src []byte) (d int) { return 0 } - var lTable [maxLTableSize]uint64 - var sTable [maxSTableSize]uint64 + tbl := getBestTables() + lTable := &tbl.lTable + sTable := &tbl.sTable + defer bestTablePool.Put(tbl) // Bail if we can't compress to at least this. dstLimit := len(src) - 5 diff --git a/vendor/github.com/klauspost/compress/s2/encode_better.go b/vendor/github.com/klauspost/compress/s2/encode_better.go index 1e30fb7317..adbc57c4d2 100644 --- a/vendor/github.com/klauspost/compress/s2/encode_better.go +++ b/vendor/github.com/klauspost/compress/s2/encode_better.go @@ -59,16 +59,18 @@ func encodeBlockBetterGo(dst, src []byte) (d int) { // Initialize the hash tables. const ( // Long hash matches. - lTableBits = 17 - maxLTableSize = 1 << lTableBits + lTableBits = betterLongTableBits + maxLTableSize = betterLongTableSize // Short hash matches. - sTableBits = 14 - maxSTableSize = 1 << sTableBits + sTableBits = betterShortTableBits + maxSTableSize = betterShortTableSize ) - var lTable [maxLTableSize]uint32 - var sTable [maxSTableSize]uint32 + tbl := getBetterTables() + lTable := &tbl.lTable + sTable := &tbl.sTable + defer betterTablePool.Put(tbl) // Bail if we can't compress to at least this. dstLimit := len(src) - len(src)>>5 - 6 @@ -317,16 +319,18 @@ func encodeBlockBetterSnappyGo(dst, src []byte) (d int) { // Initialize the hash tables. const ( // Long hash matches. - lTableBits = 16 - maxLTableSize = 1 << lTableBits + lTableBits = betterSnappyLongTableBits + maxLTableSize = betterSnappyLongTableSize // Short hash matches. - sTableBits = 14 - maxSTableSize = 1 << sTableBits + sTableBits = betterShortTableBits + maxSTableSize = betterShortTableSize ) - var lTable [maxLTableSize]uint32 - var sTable [maxSTableSize]uint32 + tbl := getBetterSnappyTables() + lTable := &tbl.lTable + sTable := &tbl.sTable + defer betterSnappyTablePool.Put(tbl) // Bail if we can't compress to at least this. dstLimit := len(src) - len(src)>>5 - 6 @@ -902,12 +906,12 @@ func encodeBlockBetterDict(dst, src []byte, dict *Dict) (d int) { // Initialize the hash tables. const ( // Long hash matches. - lTableBits = 17 - maxLTableSize = 1 << lTableBits + lTableBits = betterLongTableBits + maxLTableSize = betterLongTableSize // Short hash matches. - sTableBits = 14 - maxSTableSize = 1 << sTableBits + sTableBits = betterShortTableBits + maxSTableSize = betterShortTableSize maxAhead = 8 // maximum bytes ahead without checking sLimit @@ -921,8 +925,10 @@ func encodeBlockBetterDict(dst, src []byte, dict *Dict) (d int) { dict.initBetter() - var lTable [maxLTableSize]uint32 - var sTable [maxSTableSize]uint32 + tbl := getBetterTables() + lTable := &tbl.lTable + sTable := &tbl.sTable + defer betterTablePool.Put(tbl) // Bail if we can't compress to at least this. dstLimit := len(src) - len(src)>>5 - 6 diff --git a/vendor/github.com/klauspost/compress/s2/hashtable_pool.go b/vendor/github.com/klauspost/compress/s2/hashtable_pool.go new file mode 100644 index 0000000000..bc7cabd5c5 --- /dev/null +++ b/vendor/github.com/klauspost/compress/s2/hashtable_pool.go @@ -0,0 +1,65 @@ +package s2 + +import "sync" + +// Table size constants +const ( + betterLongTableBits = 17 + betterLongTableSize = 1 << betterLongTableBits // 131072 + + betterShortTableBits = 14 + betterShortTableSize = 1 << betterShortTableBits // 16384 + + betterSnappyLongTableBits = 16 + betterSnappyLongTableSize = 1 << betterSnappyLongTableBits // 65536 + + bestLongTableBits = 19 + bestLongTableSize = 1 << bestLongTableBits // 524288 + + bestShortTableBits = 16 + bestShortTableSize = 1 << bestShortTableBits // 65536 +) + +type betterTables struct { + lTable [betterLongTableSize]uint32 + sTable [betterShortTableSize]uint32 +} + +var betterTablePool = sync.Pool{New: func() interface{} { return &betterTables{} }} + +// betterSnappyTables holds better-snappy compression hash tables. +type betterSnappyTables struct { + lTable [betterSnappyLongTableSize]uint32 + sTable [betterShortTableSize]uint32 +} + +var betterSnappyTablePool = sync.Pool{New: func() interface{} { return &betterSnappyTables{} }} + +// bestTables holds best compression hash tables. +type bestTables struct { + lTable [bestLongTableSize]uint64 + sTable [bestShortTableSize]uint64 +} + +var bestTablePool = sync.Pool{New: func() interface{} { return &bestTables{} }} + +// getBetterTables gets a zeroed betterTables from the pool. +func getBetterTables() *betterTables { + t := betterTablePool.Get().(*betterTables) + *t = betterTables{} + return t +} + +// getBetterSnappyTables gets a zeroed betterSnappyTables from the pool. +func getBetterSnappyTables() *betterSnappyTables { + t := betterSnappyTablePool.Get().(*betterSnappyTables) + *t = betterSnappyTables{} + return t +} + +// getBestTables gets a zeroed bestTables from the pool. +func getBestTables() *bestTables { + t := bestTablePool.Get().(*bestTables) + *t = bestTables{} + return t +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_windows.go b/vendor/github.com/mattn/go-isatty/isatty_windows.go index 41edab0766..5f29c11dd2 100644 --- a/vendor/github.com/mattn/go-isatty/isatty_windows.go +++ b/vendor/github.com/mattn/go-isatty/isatty_windows.go @@ -47,9 +47,10 @@ func IsTerminal(fd uintptr) bool { // Check pipe name is used for cygwin/msys2 pty. // Cygwin/MSYS2 PTY has a name like: // \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master +// On Windows 7 a trailing suffix (e.g. "-nat") may be appended. func isCygwinPipeName(name string) bool { token := strings.Split(name, "-") - if len(token) != 5 { + if len(token) < 5 { return false } @@ -76,6 +77,12 @@ func isCygwinPipeName(name string) bool { return false } + for _, t := range token[5:] { + if t == "" { + return false + } + } + return true } diff --git a/vendor/github.com/pion/ice/v4/agent.go b/vendor/github.com/pion/ice/v4/agent.go index 90b143c4ab..d74cf68e23 100644 --- a/vendor/github.com/pion/ice/v4/agent.go +++ b/vendor/github.com/pion/ice/v4/agent.go @@ -26,7 +26,7 @@ import ( "github.com/pion/transport/v4/packetio" "github.com/pion/transport/v4/stdnet" "github.com/pion/transport/v4/vnet" - "github.com/pion/turn/v4" + "github.com/pion/turn/v5" "golang.org/x/net/proxy" ) @@ -1118,6 +1118,118 @@ func remoteDialIPForLocalInterface(remoteIP, localIP netip.Addr) netip.Addr { return remoteIP } +func copyAtomicValue(dst, src *atomic.Value) { + if value := src.Load(); value != nil { + dst.Store(value) + } +} + +type candidateActivitySetter interface { + setLastReceived(time.Time) + setLastSent(time.Time) +} + +func copyCandidateActivity(dst, src Candidate) { + setter, ok := dst.(candidateActivitySetter) + if !ok { + return + } + + if lastReceived := src.LastReceived(); !lastReceived.IsZero() && dst.LastReceived().IsZero() { + setter.setLastReceived(lastReceived) + } + + if lastSent := src.LastSent(); !lastSent.IsZero() && dst.LastSent().IsZero() { + setter.setLastSent(lastSent) + } +} + +func replacePairRemote(pair *CandidatePair, remote Candidate) *CandidatePair { + replacement := newCandidatePair(pair.Local, remote, pair.iceRoleControlling) + replacement.id = pair.id + replacement.bindingRequestCount = pair.bindingRequestCount + replacement.state = pair.state + replacement.nominated = pair.nominated + replacement.nominateOnBindingSuccess = pair.nominateOnBindingSuccess + + atomic.StoreInt64(&replacement.currentRoundTripTime, atomic.LoadInt64(&pair.currentRoundTripTime)) + atomic.StoreInt64(&replacement.totalRoundTripTime, atomic.LoadInt64(&pair.totalRoundTripTime)) + atomic.StoreUint32(&replacement.packetsSent, atomic.LoadUint32(&pair.packetsSent)) + atomic.StoreUint32(&replacement.packetsReceived, atomic.LoadUint32(&pair.packetsReceived)) + atomic.StoreUint64(&replacement.bytesSent, atomic.LoadUint64(&pair.bytesSent)) + atomic.StoreUint64(&replacement.bytesReceived, atomic.LoadUint64(&pair.bytesReceived)) + atomic.StoreUint64(&replacement.requestsReceived, atomic.LoadUint64(&pair.requestsReceived)) + atomic.StoreUint64(&replacement.requestsSent, atomic.LoadUint64(&pair.requestsSent)) + atomic.StoreUint64(&replacement.responsesReceived, atomic.LoadUint64(&pair.responsesReceived)) + atomic.StoreUint64(&replacement.responsesSent, atomic.LoadUint64(&pair.responsesSent)) + + copyAtomicValue(&replacement.lastPacketSentAt, &pair.lastPacketSentAt) + copyAtomicValue(&replacement.lastPacketReceivedAt, &pair.lastPacketReceivedAt) + copyAtomicValue(&replacement.firstRequestSentAt, &pair.firstRequestSentAt) + copyAtomicValue(&replacement.lastRequestSentAt, &pair.lastRequestSentAt) + copyAtomicValue(&replacement.firstResponseReceivedAt, &pair.firstResponseReceivedAt) + copyAtomicValue(&replacement.lastResponseReceivedAt, &pair.lastResponseReceivedAt) + copyAtomicValue(&replacement.firstRequestReceivedAt, &pair.firstRequestReceivedAt) + copyAtomicValue(&replacement.lastRequestReceivedAt, &pair.lastRequestReceivedAt) + + return replacement +} + +func (a *Agent) retargetKnownPairHolders(oldPair, newPair *CandidatePair) { + selector := a.getSelector() + + switch s := selector.(type) { + case *controllingSelector: + if s.nominatedPair == oldPair { + s.nominatedPair = newPair + } + case *liteSelector: + if cs, ok := s.pairCandidateSelector.(*controllingSelector); ok && cs.nominatedPair == oldPair { + cs.nominatedPair = newPair + } + } +} + +func removeRedundantPrflxFromSet(set []Candidate, cand Candidate) ([]Candidate, []Candidate) { + var replacedPrflx []Candidate + + for i := 0; i < len(set); i++ { + existing := set[i] + if existing.Type() == CandidateTypePeerReflexive && existing.transportAddressEqual(cand) { + replacedPrflx = append(replacedPrflx, existing) + set = append(set[:i], set[i+1:]...) + i-- + } + } + + return set, replacedPrflx +} + +func (a *Agent) replaceRemoteInPairs(oldRemote, newRemote Candidate) { + for i, pair := range a.checklist { + if pair.Remote == oldRemote { + oldPriority := pair.priority() + replacement := replacePairRemote(pair, newRemote) + replacement.setPriorityOverride(oldPriority) + a.checklist[i] = replacement + a.pairsByID[replacement.id] = replacement + a.retargetKnownPairHolders(pair, replacement) + + if a.getSelectedPair() == pair { + a.setSelectedPair(replacement) + } + } + } +} + +func (a *Agent) replaceRemoteInLocalCaches(oldRemote, newRemote Candidate) { + for _, locals := range a.localCandidates { + for _, local := range locals { + local.replaceRemoteCandidateCacheValues(oldRemote, newRemote) + } + } +} + // replaceRedundantPeerReflexiveCandidates removes any peer-reflexive candidates // from the given set that have the same transport address as cand. // It also updates any candidate pairs and local candidate caches that @@ -1125,36 +1237,18 @@ func remoteDialIPForLocalInterface(remoteIP, localIP netip.Addr) netip.Addr { // It is implemented according to RFC 8838 §11.4. // It returns the updated set of candidates. func (a *Agent) replaceRedundantPeerReflexiveCandidates(set []Candidate, cand Candidate) []Candidate { - if cand.Type() != CandidateTypePeerReflexive { - var replacedPrflx []Candidate - - for i := 0; i < len(set); i++ { - existing := set[i] - if existing.Type() == CandidateTypePeerReflexive && existing.transportAddressEqual(cand) { - replacedPrflx = append(replacedPrflx, existing) - set = append(set[:i], set[i+1:]...) - i-- - } - } - - for _, oldRemote := range replacedPrflx { - for _, pair := range a.checklist { - if pair.Remote == oldRemote { - oldPriority := pair.priority() - pair.Remote = cand - pair.setPriorityOverride(oldPriority) - } - } + if cand.Type() == CandidateTypePeerReflexive { + return set + } - for _, locals := range a.localCandidates { - for _, local := range locals { - local.replaceRemoteCandidateCacheValues(oldRemote, cand) - } - } - } + updatedSet, replacedPrflx := removeRedundantPrflxFromSet(set, cand) + for _, oldRemote := range replacedPrflx { + copyCandidateActivity(cand, oldRemote) + a.replaceRemoteInPairs(oldRemote, cand) + a.replaceRemoteInLocalCaches(oldRemote, cand) } - return set + return updatedSet } // addRemoteCandidate assumes you are holding the lock (must be execute using a.run). diff --git a/vendor/github.com/pion/ice/v4/candidate_base.go b/vendor/github.com/pion/ice/v4/candidate_base.go index b713e238a3..249fe10bf6 100644 --- a/vendor/github.com/pion/ice/v4/candidate_base.go +++ b/vendor/github.com/pion/ice/v4/candidate_base.go @@ -45,7 +45,7 @@ type candidateBase struct { relayLocalPreference uint16 - remoteCandidateCaches map[AddrPort]Candidate + remoteCandidateCaches sync.Map // map[AddrPort]Candidate isLocationTracked bool extensions []CandidateExtension } @@ -287,8 +287,13 @@ func (c *candidateBase) recvLoop(initializedCh <-chan struct{}) { } func (c *candidateBase) validateSTUNTrafficCache(addr net.Addr) bool { - if candidate, ok := c.remoteCandidateCaches[toAddrPort(addr)]; ok { - candidate.seen(false) + if candidate, ok := c.remoteCandidateCaches.Load(toAddrPort(addr)); ok { + remoteCandidate, ok := candidate.(Candidate) + if !ok { + return false + } + + remoteCandidate.seen(false) return true } @@ -300,15 +305,18 @@ func (c *candidateBase) addRemoteCandidateCache(candidate Candidate, srcAddr net if c.validateSTUNTrafficCache(srcAddr) { return } - c.remoteCandidateCaches[toAddrPort(srcAddr)] = candidate + c.remoteCandidateCaches.Store(toAddrPort(srcAddr), candidate) } func (c *candidateBase) replaceRemoteCandidateCacheValues(oldRemote, newRemote Candidate) { - for k, v := range c.remoteCandidateCaches { - if v == oldRemote { - c.remoteCandidateCaches[k] = newRemote + c.remoteCandidateCaches.Range(func(key, value any) bool { + candidate, ok := value.(Candidate) + if ok && candidate == oldRemote { + c.remoteCandidateCaches.Store(key, newRemote) } - } + + return true + }) } func (c *candidateBase) handleInboundPacket(buf []byte, srcAddr net.Addr) { diff --git a/vendor/github.com/pion/ice/v4/candidate_host.go b/vendor/github.com/pion/ice/v4/candidate_host.go index d49e5d2cfa..6e48961593 100644 --- a/vendor/github.com/pion/ice/v4/candidate_host.go +++ b/vendor/github.com/pion/ice/v4/candidate_host.go @@ -38,16 +38,15 @@ func NewCandidateHost(config *CandidateHostConfig) (*CandidateHost, error) { candidateHost := &CandidateHost{ candidateBase: candidateBase{ - id: candidateID, - address: config.Address, - candidateType: CandidateTypeHost, - component: config.Component, - port: config.Port, - tcpType: config.TCPType, - foundationOverride: config.Foundation, - priorityOverride: config.Priority, - remoteCandidateCaches: map[AddrPort]Candidate{}, - isLocationTracked: config.IsLocationTracked, + id: candidateID, + address: config.Address, + candidateType: CandidateTypeHost, + component: config.Component, + port: config.Port, + tcpType: config.TCPType, + foundationOverride: config.Foundation, + priorityOverride: config.Priority, + isLocationTracked: config.IsLocationTracked, }, network: config.Network, } diff --git a/vendor/github.com/pion/ice/v4/candidate_peer_reflexive.go b/vendor/github.com/pion/ice/v4/candidate_peer_reflexive.go index e4e06f8280..0cdfa381b1 100644 --- a/vendor/github.com/pion/ice/v4/candidate_peer_reflexive.go +++ b/vendor/github.com/pion/ice/v4/candidate_peer_reflexive.go @@ -60,7 +60,6 @@ func NewCandidatePeerReflexive(config *CandidatePeerReflexiveConfig) (*Candidate Address: config.RelAddr, Port: config.RelPort, }, - remoteCandidateCaches: map[AddrPort]Candidate{}, }, }, nil } diff --git a/vendor/github.com/pion/ice/v4/candidate_relay.go b/vendor/github.com/pion/ice/v4/candidate_relay.go index 3adaebe9ba..efa3482ff6 100644 --- a/vendor/github.com/pion/ice/v4/candidate_relay.go +++ b/vendor/github.com/pion/ice/v4/candidate_relay.go @@ -78,8 +78,7 @@ func NewCandidateRelay(config *CandidateRelayConfig) (*CandidateRelay, error) { Address: config.RelAddr, Port: config.RelPort, }, - relayLocalPreference: relayProtocolPreference(config.RelayProtocol), - remoteCandidateCaches: map[AddrPort]Candidate{}, + relayLocalPreference: relayProtocolPreference(config.RelayProtocol), }, relayProtocol: config.RelayProtocol, onClose: config.OnClose, diff --git a/vendor/github.com/pion/ice/v4/candidate_server_reflexive.go b/vendor/github.com/pion/ice/v4/candidate_server_reflexive.go index 7e2eb9d073..380533ec19 100644 --- a/vendor/github.com/pion/ice/v4/candidate_server_reflexive.go +++ b/vendor/github.com/pion/ice/v4/candidate_server_reflexive.go @@ -62,7 +62,6 @@ func NewCandidateServerReflexive(config *CandidateServerReflexiveConfig) (*Candi Address: config.RelAddr, Port: config.RelPort, }, - remoteCandidateCaches: map[AddrPort]Candidate{}, }, }, nil } diff --git a/vendor/github.com/pion/ice/v4/gather.go b/vendor/github.com/pion/ice/v4/gather.go index b45eb496a7..8f8559328d 100644 --- a/vendor/github.com/pion/ice/v4/gather.go +++ b/vendor/github.com/pion/ice/v4/gather.go @@ -20,7 +20,7 @@ import ( "github.com/pion/logging" "github.com/pion/stun/v3" "github.com/pion/transport/v4/stdnet" - "github.com/pion/turn/v4" + "github.com/pion/turn/v5" ) type turnClient interface { diff --git a/vendor/github.com/pion/interceptor/.golangci.yml b/vendor/github.com/pion/interceptor/.golangci.yml index 43af4c386e..6c7ceed691 100644 --- a/vendor/github.com/pion/interceptor/.golangci.yml +++ b/vendor/github.com/pion/interceptor/.golangci.yml @@ -41,6 +41,7 @@ linters: - maintidx # maintidx measures the maintainability index of each function. - makezero # Finds slice declarations with non-zero initial length - misspell # Finds commonly misspelled English words in comments + - modernize # Replace and suggests simplifications to code - nakedret # Finds naked returns in functions greater than a specified function length - nestif # Reports deeply nested if statements - nilerr # Finds the code that returns nil even if it checks that the error is not nil. @@ -145,3 +146,6 @@ formatters: - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports exclusions: generated: lax +issues: + max-issues-per-linter: 0 + max-same-issues: 0 \ No newline at end of file diff --git a/vendor/github.com/pion/interceptor/README.md b/vendor/github.com/pion/interceptor/README.md index 46a9ca6a2d..d6f254d03a 100644 --- a/vendor/github.com/pion/interceptor/README.md +++ b/vendor/github.com/pion/interceptor/README.md @@ -32,6 +32,8 @@ by anyone. With the following tenets in mind. * [NACK Generator/Responder](https://github.com/pion/interceptor/tree/master/pkg/nack) * [Sender and Receiver Reports](https://github.com/pion/interceptor/tree/master/pkg/report) * [Transport Wide Congestion Control Feedback](https://github.com/pion/interceptor/tree/master/pkg/twcc) +* [RTCP Feedback for Congestion Control](https://github.com/pion/interceptor/tree/master/pkg/rfc8888) as defined by [RFC 8888](https://datatracker.ietf.org/doc/html/rfc8888). +* [JitterBuffer](https://github.com/pion/interceptor/tree/master/pkg/jitterbuffer) Re-order packets and wait for arrival on the remote/inbound RTP path. * [Packet Dump](https://github.com/pion/interceptor/tree/master/pkg/packetdump) * [Google Congestion Control](https://github.com/pion/interceptor/tree/master/pkg/gcc) * [Stats](https://github.com/pion/interceptor/tree/master/pkg/stats) A [webrtc-stats](https://www.w3.org/TR/webrtc-stats/) compliant statistics generation @@ -41,8 +43,6 @@ by anyone. With the following tenets in mind. ### Planned Interceptors * Bandwidth Estimation - [NADA](https://tools.ietf.org/html/rfc8698) -* JitterBuffer, re-order packets and wait for arrival -* [RTCP Feedback for Congestion Control](https://datatracker.ietf.org/doc/html/rfc8888) the standardized alternative to TWCC. ### Interceptor Public API The public interface is defined in [interceptor.go](https://github.com/pion/interceptor/blob/master/interceptor.go). diff --git a/vendor/github.com/pion/interceptor/internal/rtpbuffer/rtpbuffer.go b/vendor/github.com/pion/interceptor/internal/rtpbuffer/rtpbuffer.go index 25016465d1..38121d641b 100644 --- a/vendor/github.com/pion/interceptor/internal/rtpbuffer/rtpbuffer.go +++ b/vendor/github.com/pion/interceptor/internal/rtpbuffer/rtpbuffer.go @@ -28,7 +28,7 @@ type RTPBuffer struct { func NewRTPBuffer(size uint16) (*RTPBuffer, error) { allowedSizes := make([]uint16, 0) correctSize := false - for i := 0; i < 16; i++ { + for i := range 16 { if size == 1< FEC packet I will protect media packet J. - for fecPacketIndex := uint32(0); fecPacketIndex < numFecPackets; fecPacketIndex++ { + for fecPacketIndex := range numFecPackets { // We use an interleaved method to determine coverage. Given N FEC packets, Media packet X will be // covered by FEC packet X % N. coveredMediaPacketIndex := fecPacketIndex @@ -99,7 +99,7 @@ func (p *ProtectionCoverage) UpdateCoverage(mediaPackets []rtp.Packet, numFecPac // ResetCoverage clears the underlying map so that we can reuse it for new batches of RTP packets. func (p *ProtectionCoverage) resetCoverage() { - for i := uint32(0); i < MaxFecPackets; i++ { + for i := range MaxFecPackets { p.packetMasks[i].Reset() } } diff --git a/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_decoder_03.go b/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_decoder_03.go index 2706a2cacc..787d6246bd 100644 --- a/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_decoder_03.go +++ b/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_decoder_03.go @@ -263,7 +263,7 @@ func (d *fecDecoder) recoverPacket(fec *fecPacketState) (rtp.Packet, error) { return rtp.Packet{}, fmt.Errorf("marshal received header: %w", err) } binary.BigEndian.PutUint16(receivedHeader[2:4], uint16(protectedPacket.packet.MarshalSize()-12)) //nolint:gosec - for i := 0; i < 8; i++ { + for i := range 8 { headerRecovery[i] ^= receivedHeader[i] } } else { @@ -312,7 +312,7 @@ func (d *fecDecoder) discardOldRecoveredPackets() { func decodeMask(mask uint64, bitCount uint16, seqNumBase uint16) []uint16 { res := make([]uint16, 0) - for i := uint16(0); i < bitCount; i++ { + for i := range bitCount { if (mask>>(bitCount-1-i))&1 == 1 { res = append(res, seqNumBase+i) } diff --git a/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_encoder.go b/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_encoder.go index cec1d593c4..3406f0fd89 100644 --- a/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_encoder.go +++ b/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_encoder.go @@ -65,7 +65,7 @@ func (flex *FlexEncoder20) EncodeFec(mediaPackets []rtp.Packet, numFecPackets ui // Generate FEC payloads fecPackets := make([]rtp.Packet, numFecPackets) - for fecPacketIndex := uint32(0); fecPacketIndex < numFecPackets; fecPacketIndex++ { + for fecPacketIndex := range numFecPackets { fecPackets[fecPacketIndex] = flex.encodeFlexFecPacket(fecPacketIndex, mediaPackets[0].SequenceNumber) } @@ -200,7 +200,7 @@ func (flex *FlexEncoder20) encodeFlexFecRepairPayload(mediaPackets *util.MediaPa copy(flexFecPayloadTmp, flexFecPayload) flexFecPayload = flexFecPayloadTmp } - for byteIndex := 0; byteIndex < len(mediaPacketPayload); byteIndex++ { + for byteIndex := range mediaPacketPayload { flexFecPayload[byteIndex] ^= mediaPacketPayload[byteIndex] } } diff --git a/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_encoder_03.go b/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_encoder_03.go index b73416a4e3..4684e8fc28 100644 --- a/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_encoder_03.go +++ b/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_encoder_03.go @@ -83,7 +83,7 @@ func (flex *FlexEncoder03) EncodeFec(mediaPackets []rtp.Packet, numFecPackets ui // Generate FEC payloads fecPackets := make([]rtp.Packet, 0, numFecPackets) - for fecPacketIndex := uint32(0); fecPacketIndex < numFecPackets; fecPacketIndex++ { + for fecPacketIndex := range numFecPackets { fecPacket, ok := flex.encodeFlexFecPacket(fecPacketIndex, mediaPackets[0].SequenceNumber) if ok { fecPackets = append(fecPackets, fecPacket) diff --git a/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go b/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go index 4aa624704d..4b0716669f 100644 --- a/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go +++ b/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go @@ -145,8 +145,31 @@ func (n *ResponderInterceptor) BindLocalStream( // UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track. func (n *ResponderInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) { n.streamsMu.Lock() + stream, ok := n.streams[info.SSRC] delete(n.streams, info.SSRC) n.streamsMu.Unlock() + + if ok { + stream.rtpBufferMutex.Lock() + stream.rtpBuffer.Clear() + stream.rtpBufferMutex.Unlock() + } +} + +// Close releases all resources held by the ResponderInterceptor. +func (n *ResponderInterceptor) Close() error { + n.streamsMu.Lock() + streams := n.streams + n.streams = map[uint32]*localStream{} + n.streamsMu.Unlock() + + for _, stream := range streams { + stream.rtpBufferMutex.Lock() + stream.rtpBuffer.Clear() + stream.rtpBufferMutex.Unlock() + } + + return nil } func (n *ResponderInterceptor) resendPackets(nack *rtcp.TransportLayerNack) { diff --git a/vendor/github.com/pion/rtp/.golangci.yml b/vendor/github.com/pion/rtp/.golangci.yml index 43af4c386e..6c7ceed691 100644 --- a/vendor/github.com/pion/rtp/.golangci.yml +++ b/vendor/github.com/pion/rtp/.golangci.yml @@ -41,6 +41,7 @@ linters: - maintidx # maintidx measures the maintainability index of each function. - makezero # Finds slice declarations with non-zero initial length - misspell # Finds commonly misspelled English words in comments + - modernize # Replace and suggests simplifications to code - nakedret # Finds naked returns in functions greater than a specified function length - nestif # Reports deeply nested if statements - nilerr # Finds the code that returns nil even if it checks that the error is not nil. @@ -145,3 +146,6 @@ formatters: - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports exclusions: generated: lax +issues: + max-issues-per-linter: 0 + max-same-issues: 0 \ No newline at end of file diff --git a/vendor/github.com/pion/rtp/codecs/av1/obu/leb128.go b/vendor/github.com/pion/rtp/codecs/av1/obu/leb128.go index 7662bc0826..271dc6e496 100644 --- a/vendor/github.com/pion/rtp/codecs/av1/obu/leb128.go +++ b/vendor/github.com/pion/rtp/codecs/av1/obu/leb128.go @@ -72,7 +72,7 @@ func ReadLeb128(in []byte) (uint, uint, error) { func WriteToLeb128(in uint) []byte { b := make([]byte, 10) - for i := 0; i < len(b); i++ { + for i := range len(b) { b[i] = byte(in & 0x7f) in >>= 7 if in == 0 { diff --git a/vendor/github.com/pion/rtp/codecs/vp8_packet.go b/vendor/github.com/pion/rtp/codecs/vp8_packet.go index 853809871e..569853cbf1 100644 --- a/vendor/github.com/pion/rtp/codecs/vp8_packet.go +++ b/vendor/github.com/pion/rtp/codecs/vp8_packet.go @@ -163,14 +163,17 @@ func (p *VP8Packet) Unmarshal(payload []byte) ([]byte, error) { //nolint:gocogni if payloadIndex >= payloadLen { return nil, errShortPacket } + //nolint:gosec // G602: payloadIndex bounds checked above if payload[payloadIndex]&0x80 > 0 { // M == 1, PID is 16bit if payloadIndex+1 >= payloadLen { return nil, errShortPacket } - p.PictureID = (uint16(payload[payloadIndex]&0x7F) << 8) | uint16(payload[payloadIndex+1]) + //nolint:gosec // G602: payloadIndex bounds checked above + p.PictureID = (uint16(payload[payloadIndex]&0x7F) << 8) | + uint16(payload[payloadIndex+1]) payloadIndex += 2 } else { - p.PictureID = uint16(payload[payloadIndex]) + p.PictureID = uint16(payload[payloadIndex]) //nolint:gosec payloadIndex++ } } else { diff --git a/vendor/github.com/pion/rtp/codecs/vp9_packet.go b/vendor/github.com/pion/rtp/codecs/vp9_packet.go index fbb2d07704..23c1e56e6a 100644 --- a/vendor/github.com/pion/rtp/codecs/vp9_packet.go +++ b/vendor/github.com/pion/rtp/codecs/vp9_packet.go @@ -98,7 +98,7 @@ func (p *VP9Payloader) payloadFlexible(mtu uint16, payload []byte) [][]byte { } out[1] = byte(p.pictureID>>8) | 0x80 - out[2] = byte(p.pictureID) + out[2] = byte(p.pictureID) //nolint:gosec copy(out[headerSize:], payload[payloadDataIndex:payloadDataIndex+currentFragmentSize]) payloads = append(payloads, out) @@ -169,7 +169,7 @@ func (p *VP9Payloader) payloadNonFlexible(mtu uint16, payload []byte) [][]byte { } out[1] = byte(p.pictureID>>8) | 0x80 - out[2] = byte(p.pictureID) + out[2] = byte(p.pictureID) //nolint:gosec off := 3 if !header.NonKeyFrame && payloadDataIndex == 0 { diff --git a/vendor/github.com/pion/rtp/packet.go b/vendor/github.com/pion/rtp/packet.go index d7a5e0834e..6d2c7f3c5d 100644 --- a/vendor/github.com/pion/rtp/packet.go +++ b/vendor/github.com/pion/rtp/packet.go @@ -239,6 +239,9 @@ func (p *Packet) Unmarshal(buf []byte) error { return errTooSmall } p.Header.PaddingSize = buf[end-1] + if p.Header.PaddingSize == 0 { + return errInvalidRTPPadding + } end -= int(p.Header.PaddingSize) } else { p.Header.PaddingSize = 0 @@ -500,6 +503,10 @@ func (h *Header) DelExtension(id uint8) error { for i, extension := range h.Extensions { if extension.id == id { h.Extensions = append(h.Extensions[:i], h.Extensions[i+1:]...) + if len(h.Extensions) == 0 { + h.Extension = false + h.ExtensionProfile = 0 + } return nil } @@ -508,6 +515,13 @@ func (h *Header) DelExtension(id uint8) error { return errHeaderExtensionNotFound } +// ClearExtensions Removes all RTP Header extension. +func (h *Header) ClearExtensions() { + h.Extensions = h.Extensions[:0] + h.Extension = false + h.ExtensionProfile = 0 +} + // Marshal serializes the packet into bytes. func (p Packet) Marshal() (buf []byte, err error) { buf = make([]byte, p.MarshalSize()) diff --git a/vendor/github.com/pion/rtp/playoutdelayextension.go b/vendor/github.com/pion/rtp/playoutdelayextension.go index bb728ffc91..299e52de70 100644 --- a/vendor/github.com/pion/rtp/playoutdelayextension.go +++ b/vendor/github.com/pion/rtp/playoutdelayextension.go @@ -43,8 +43,8 @@ func (p PlayoutDelayExtension) MarshalTo(buf []byte) (int, error) { return 0, io.ErrShortBuffer } buf[0] = byte(p.MinDelay >> 4) - buf[1] = byte(p.MinDelay<<4) | byte(p.MaxDelay>>8) - buf[2] = byte(p.MaxDelay) + buf[1] = byte(p.MinDelay<<4) | byte(p.MaxDelay>>8) //nolint:gosec + buf[2] = byte(p.MaxDelay) //nolint:gosec return playoutDelayExtensionSize, nil } @@ -57,8 +57,8 @@ func (p PlayoutDelayExtension) Marshal() ([]byte, error) { return []byte{ byte(p.MinDelay >> 4), - byte(p.MinDelay<<4) | byte(p.MaxDelay>>8), - byte(p.MaxDelay), + byte(p.MinDelay<<4) | byte(p.MaxDelay>>8), //nolint:gosec + byte(p.MaxDelay), //nolint:gosec }, nil } diff --git a/vendor/github.com/pion/rtp/vlaextension.go b/vendor/github.com/pion/rtp/vlaextension.go index bf69a1a5e5..ba5a2e1966 100644 --- a/vendor/github.com/pion/rtp/vlaextension.go +++ b/vendor/github.com/pion/rtp/vlaextension.go @@ -87,7 +87,7 @@ func (v VLA) preprocessForMashaling(ctx *vlaMarshalingContext) error { //nolint: func (v VLA) calcTargetBitratesSize(ctx *vlaMarshalingContext) { for rtpStreamID := 0; rtpStreamID < v.RTPStreamCount; rtpStreamID++ { - for spatialID := 0; spatialID < 4; spatialID++ { + for spatialID := range 4 { if idx := ctx.slIndices[rtpStreamID][spatialID]; idx >= 0 { for _, kbps := range v.ActiveSpatialLayer[idx].TargetBitrates { ctx.requiredLen += leb128Size(uint(kbps)) //nolint:gosec @@ -158,7 +158,7 @@ func (v VLA) MarshalTo(buf []byte) (int, error) { //nolint:cyclop,gocognit offset := 0 // RID, NS, sl_bm fields - buf[offset] = byte(v.RTPStreamID<<6) | byte(v.RTPStreamCount-1)<<4 | ctx.commonSLBM + buf[offset] = byte(v.RTPStreamID<<6) | byte(v.RTPStreamCount-1)<<4 | ctx.commonSLBM //nolint:gosec // values are small if ctx.commonSLBM == 0 { offset++ @@ -176,12 +176,13 @@ func (v VLA) MarshalTo(buf []byte) (int, error) { //nolint:cyclop,gocognit offset++ var temporalLayerIndex int for rtpStreamID := 0; rtpStreamID < v.RTPStreamCount; rtpStreamID++ { - for spatialID := 0; spatialID < 4; spatialID++ { + for spatialID := range 4 { if idx := ctx.slIndices[rtpStreamID][spatialID]; idx >= 0 { if temporalLayerIndex >= 4 { temporalLayerIndex = 0 offset++ } + //nolint:gosec // values are small buf[offset] |= byte(len(v.ActiveSpatialLayer[idx].TargetBitrates)-1) << (2 * (3 - temporalLayerIndex)) temporalLayerIndex++ } @@ -191,7 +192,7 @@ func (v VLA) MarshalTo(buf []byte) (int, error) { //nolint:cyclop,gocognit // Target bitrate fields offset++ for rtpStreamID := 0; rtpStreamID < v.RTPStreamCount; rtpStreamID++ { - for spatialID := 0; spatialID < 4; spatialID++ { + for spatialID := range 4 { if idx := ctx.slIndices[rtpStreamID][spatialID]; idx >= 0 { for _, kbps := range v.ActiveSpatialLayer[idx].TargetBitrates { offset += writeLeb128To(buf[offset:], uint(kbps)) //nolint:gosec @@ -205,7 +206,7 @@ func (v VLA) MarshalTo(buf []byte) (int, error) { //nolint:cyclop,gocognit for _, sl := range v.ActiveSpatialLayer { binary.BigEndian.PutUint16(buf[offset+0:], uint16(sl.Width-1)) //nolint:gosec binary.BigEndian.PutUint16(buf[offset+2:], uint16(sl.Height-1)) //nolint:gosec - buf[offset+4] = byte(sl.Framerate) + buf[offset+4] = byte(sl.Framerate) //nolint:gosec offset += 5 } } @@ -231,7 +232,7 @@ func (v VLA) Marshal() ([]byte, error) { func commonSLBMValues(slMBs []uint8) uint8 { var common uint8 - for i := 0; i < len(slMBs); i++ { + for i := range slMBs { if slMBs[i] == 0 { continue } @@ -301,7 +302,7 @@ func (v *VLA) unmarshalTemporalLayers(ctx *vlaUnmarshalingContext) error { // no var temporalLayerIndex int for streamID := 0; streamID < v.RTPStreamCount; streamID++ { - for spatialID := 0; spatialID < 4; spatialID++ { + for spatialID := range 4 { if ctx.slBMs[streamID]&(1< +// SPDX-License-Identifier: MIT + +//go:build !unix && !windows + +package reuseport + +import ( + "syscall" +) + +func Control(network, address string, c syscall.RawConn) error { + return nil +} diff --git a/vendor/github.com/pion/transport/v4/reuseport/control_unix.go b/vendor/github.com/pion/transport/v4/reuseport/control_unix.go new file mode 100644 index 0000000000..e74d29115b --- /dev/null +++ b/vendor/github.com/pion/transport/v4/reuseport/control_unix.go @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2026 The Pion community +// SPDX-License-Identifier: MIT + +//go:build unix + +package reuseport + +import ( + "syscall" + + "golang.org/x/sys/unix" +) + +func Control(network, address string, conn syscall.RawConn) error { + return conn.Control(func(fd uintptr) { + err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) + if err != nil { + return + } + _ = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) + }) +} diff --git a/vendor/github.com/pion/transport/v4/reuseport/control_windows.go b/vendor/github.com/pion/transport/v4/reuseport/control_windows.go new file mode 100644 index 0000000000..c8a0557f93 --- /dev/null +++ b/vendor/github.com/pion/transport/v4/reuseport/control_windows.go @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2026 The Pion community +// SPDX-License-Identifier: MIT + +//go:build windows + +package reuseport + +import ( + "syscall" + + "golang.org/x/sys/windows" +) + +func Control(network, address string, c syscall.RawConn) (err error) { + return c.Control(func(fd uintptr) { + _ = windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_REUSEADDR, 1) + }) +} diff --git a/vendor/github.com/pion/transport/v4/reuseport/reuseport.go b/vendor/github.com/pion/transport/v4/reuseport/reuseport.go new file mode 100644 index 0000000000..213de12d10 --- /dev/null +++ b/vendor/github.com/pion/transport/v4/reuseport/reuseport.go @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2026 The Pion community +// SPDX-License-Identifier: MIT + +// Package reuseport implements an OS-dependent reuse-port helper. +package reuseport diff --git a/vendor/github.com/pion/turn/v4/.goreleaser.yml b/vendor/github.com/pion/turn/v4/.goreleaser.yml deleted file mode 100644 index 30093e9d6d..0000000000 --- a/vendor/github.com/pion/turn/v4/.goreleaser.yml +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-FileCopyrightText: 2023 The Pion community -# SPDX-License-Identifier: MIT - -builds: -- skip: true diff --git a/vendor/github.com/pion/turn/v4/internal/allocation/allocation_manager.go b/vendor/github.com/pion/turn/v4/internal/allocation/allocation_manager.go deleted file mode 100644 index 02d11d0a7f..0000000000 --- a/vendor/github.com/pion/turn/v4/internal/allocation/allocation_manager.go +++ /dev/null @@ -1,246 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The Pion community -// SPDX-License-Identifier: MIT - -package allocation - -import ( - "fmt" - "net" - "sync" - "time" - - "github.com/pion/logging" -) - -// ManagerConfig a bag of config params for Manager. -type ManagerConfig struct { - LeveledLogger logging.LeveledLogger - AllocatePacketConn func(network string, requestedPort int) (net.PacketConn, net.Addr, error) - AllocateConn func(network string, requestedPort int) (net.Conn, net.Addr, error) - PermissionHandler func(sourceAddr net.Addr, peerIP net.IP) bool - EventHandler EventHandler -} - -type reservation struct { - token string - port int -} - -// Manager is used to hold active allocations. -type Manager struct { - lock sync.RWMutex - log logging.LeveledLogger - - allocations map[FiveTupleFingerprint]*Allocation - reservations []*reservation - - allocatePacketConn func(network string, requestedPort int) (net.PacketConn, net.Addr, error) - allocateConn func(network string, requestedPort int) (net.Conn, net.Addr, error) - permissionHandler func(sourceAddr net.Addr, peerIP net.IP) bool - EventHandler EventHandler -} - -// NewManager creates a new instance of Manager. -func NewManager(config ManagerConfig) (*Manager, error) { - switch { - case config.AllocatePacketConn == nil: - return nil, errAllocatePacketConnMustBeSet - case config.AllocateConn == nil: - return nil, errAllocateConnMustBeSet - case config.LeveledLogger == nil: - return nil, errLeveledLoggerMustBeSet - } - - return &Manager{ - log: config.LeveledLogger, - allocations: make(map[FiveTupleFingerprint]*Allocation, 64), - allocatePacketConn: config.AllocatePacketConn, - allocateConn: config.AllocateConn, - permissionHandler: config.PermissionHandler, - EventHandler: config.EventHandler, - }, nil -} - -// GetAllocation fetches the allocation matching the passed FiveTuple. -func (m *Manager) GetAllocation(fiveTuple *FiveTuple) *Allocation { - m.lock.RLock() - defer m.lock.RUnlock() - - return m.allocations[fiveTuple.Fingerprint()] -} - -// AllocationCount returns the number of existing allocations. -func (m *Manager) AllocationCount() int { - m.lock.RLock() - defer m.lock.RUnlock() - - return len(m.allocations) -} - -// Close closes the manager and closes all allocations it manages. -func (m *Manager) Close() error { - m.lock.Lock() - defer m.lock.Unlock() - - for _, a := range m.allocations { - if err := a.Close(); err != nil { - return err - } - } - - return nil -} - -// CreateAllocation creates a new allocation and starts relaying. -func (m *Manager) CreateAllocation( - fiveTuple *FiveTuple, - turnSocket net.PacketConn, - requestedPort int, - lifetime time.Duration, - username, realm string, -) (*Allocation, error) { - switch { - case fiveTuple == nil: - return nil, errNilFiveTuple - case fiveTuple.SrcAddr == nil: - return nil, errNilFiveTupleSrcAddr - case fiveTuple.DstAddr == nil: - return nil, errNilFiveTupleDstAddr - case turnSocket == nil: - return nil, errNilTurnSocket - case lifetime == 0: - return nil, errLifetimeZero - } - - if alloc := m.GetAllocation(fiveTuple); alloc != nil { - return nil, fmt.Errorf("%w: %v", errDupeFiveTuple, fiveTuple) - } - alloc := NewAllocation(turnSocket, fiveTuple, m.EventHandler, m.log) - alloc.username = username - alloc.realm = realm - - conn, relayAddr, err := m.allocatePacketConn("udp4", requestedPort) - if err != nil { - return nil, err - } - - alloc.RelaySocket = conn - alloc.RelayAddr = relayAddr - - m.log.Debugf("Listening on relay address: %s", alloc.RelayAddr) - - alloc.lifetimeTimer = time.AfterFunc(lifetime, func() { - m.DeleteAllocation(alloc.fiveTuple) - }) - - m.lock.Lock() - m.allocations[fiveTuple.Fingerprint()] = alloc - m.lock.Unlock() - - if m.EventHandler.OnAllocationCreated != nil { - m.EventHandler.OnAllocationCreated(fiveTuple.SrcAddr, fiveTuple.DstAddr, - fiveTuple.Protocol.String(), username, realm, relayAddr, requestedPort) - } - - go alloc.packetHandler(m) - - return alloc, nil -} - -// DeleteAllocation removes an allocation. -func (m *Manager) DeleteAllocation(fiveTuple *FiveTuple) { - fingerprint := fiveTuple.Fingerprint() - - m.lock.Lock() - allocation := m.allocations[fingerprint] - delete(m.allocations, fingerprint) - m.lock.Unlock() - - if allocation == nil { - return - } - - if err := allocation.Close(); err != nil { - m.log.Errorf("Failed to close allocation: %v", err) - } - - if m.EventHandler.OnAllocationDeleted != nil { - m.EventHandler.OnAllocationDeleted(fiveTuple.SrcAddr, fiveTuple.DstAddr, - fiveTuple.Protocol.String(), allocation.username, allocation.realm) - } -} - -// CreateReservation stores the reservation for the token+port. -func (m *Manager) CreateReservation(reservationToken string, port int) { - time.AfterFunc(30*time.Second, func() { - m.lock.Lock() - defer m.lock.Unlock() - for i := len(m.reservations) - 1; i >= 0; i-- { - if m.reservations[i].token == reservationToken { - m.reservations = append(m.reservations[:i], m.reservations[i+1:]...) - - return - } - } - }) - - m.lock.Lock() - m.reservations = append(m.reservations, &reservation{ - token: reservationToken, - port: port, - }) - m.lock.Unlock() -} - -// GetReservation returns the port for a given reservation if it exists. -func (m *Manager) GetReservation(reservationToken string) (int, bool) { - m.lock.RLock() - defer m.lock.RUnlock() - - for _, r := range m.reservations { - if r.token == reservationToken { - return r.port, true - } - } - - return 0, false -} - -// GetRandomEvenPort returns a random un-allocated udp4 port. -func (m *Manager) GetRandomEvenPort() (int, error) { - for i := 0; i < 128; i++ { - conn, addr, err := m.allocatePacketConn("udp4", 0) - if err != nil { - return 0, err - } - udpAddr, ok := addr.(*net.UDPAddr) - err = conn.Close() - if err != nil { - return 0, err - } - - if !ok { - return 0, errFailedToCastUDPAddr - } - if udpAddr.Port%2 == 0 { - return udpAddr.Port, nil - } - } - - return 0, errFailedToAllocateEvenPort -} - -// GrantPermission handles permission requests by calling the permission handler callback -// associated with the TURN server listener socket. -func (m *Manager) GrantPermission(sourceAddr net.Addr, peerIP net.IP) error { - // No permission handler: open - if m.permissionHandler == nil { - return nil - } - - if m.permissionHandler(sourceAddr, peerIP) { - return nil - } - - return errAdminProhibited -} diff --git a/vendor/github.com/pion/turn/v4/internal/allocation/errors.go b/vendor/github.com/pion/turn/v4/internal/allocation/errors.go deleted file mode 100644 index 584073c937..0000000000 --- a/vendor/github.com/pion/turn/v4/internal/allocation/errors.go +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The Pion community -// SPDX-License-Identifier: MIT - -package allocation - -import "errors" - -var ( - errAllocatePacketConnMustBeSet = errors.New("AllocatePacketConn must be set") - errAllocateConnMustBeSet = errors.New("AllocateConn must be set") - errLeveledLoggerMustBeSet = errors.New("LeveledLogger must be set") - errSameChannelDifferentPeer = errors.New("you cannot use the same channel number with different peer") - errNilFiveTuple = errors.New("allocations must not be created with nil FivTuple") - errNilFiveTupleSrcAddr = errors.New("allocations must not be created with nil FiveTuple.SrcAddr") - errNilFiveTupleDstAddr = errors.New("allocations must not be created with nil FiveTuple.DstAddr") - errNilTurnSocket = errors.New("allocations must not be created with nil turnSocket") - errLifetimeZero = errors.New("allocations must not be created with a lifetime of 0") - errDupeFiveTuple = errors.New("allocation attempt created with duplicate FiveTuple") - errFailedToCastUDPAddr = errors.New("failed to cast net.Addr to *net.UDPAddr") - errFailedToAllocateEvenPort = errors.New("failed to allocate an even port") - errAdminProhibited = errors.New("permission request administratively prohibited") -) diff --git a/vendor/github.com/pion/turn/v4/internal/server/errors.go b/vendor/github.com/pion/turn/v4/internal/server/errors.go deleted file mode 100644 index ea6da834cc..0000000000 --- a/vendor/github.com/pion/turn/v4/internal/server/errors.go +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The Pion community -// SPDX-License-Identifier: MIT - -package server - -import "errors" - -var ( - errFailedToGenerateNonce = errors.New("failed to generate nonce") - errInvalidNonce = errors.New("invalid nonce") - errFailedToSendError = errors.New("failed to send error message") - errNoSuchUser = errors.New("no such user exists") - errUnexpectedClass = errors.New("unexpected class") - errUnexpectedMethod = errors.New("unexpected method") - errFailedToHandle = errors.New("failed to handle") - errUnhandledSTUNPacket = errors.New("unhandled STUN packet") - errUnableToHandleChannelData = errors.New("unable to handle ChannelData") - errFailedToCreateSTUNPacket = errors.New("failed to create stun message from packet") - errFailedToCreateChannelData = errors.New("failed to create channel data from packet") - errRelayAlreadyAllocatedForFiveTuple = errors.New("relay already allocated for 5-TUPLE") - errUnsupportedTransportProtocol = errors.New("RequestedTransport must be UDP or TCP") - errNoDontFragmentSupport = errors.New("no support for DONT-FRAGMENT") - errRequestWithReservationTokenAndEvenPort = errors.New("Request must not contain RESERVATION-TOKEN and EVEN-PORT") - errNoAllocationFound = errors.New("no allocation found") - errNoPermission = errors.New("unable to handle send-indication, no permission added") - errShortWrite = errors.New("packet write smaller than packet") - errNoSuchChannelBind = errors.New("no such channel bind") - errFailedWriteSocket = errors.New("failed writing to socket") -) diff --git a/vendor/github.com/pion/turn/v4/relay_address_generator_none.go b/vendor/github.com/pion/turn/v4/relay_address_generator_none.go deleted file mode 100644 index f549af7def..0000000000 --- a/vendor/github.com/pion/turn/v4/relay_address_generator_none.go +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The Pion community -// SPDX-License-Identifier: MIT - -package turn - -import ( - "fmt" - "net" - "strconv" - - "github.com/pion/transport/v4" - "github.com/pion/transport/v4/stdnet" -) - -// RelayAddressGeneratorNone returns the listener with no modifications. -type RelayAddressGeneratorNone struct { - // Address is passed to Listen/ListenPacket when creating the Relay - Address string - - Net transport.Net -} - -// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured. -func (r *RelayAddressGeneratorNone) Validate() error { - if r.Net == nil { - var err error - r.Net, err = stdnet.NewNet() - if err != nil { - return fmt.Errorf("failed to create network: %w", err) - } - } - - if r.Address == "" { - return errListeningAddressInvalid - } - - return nil -} - -// AllocatePacketConn generates a new PacketConn to receive traffic on and the IP/Port -// to populate the allocation response with. -func (r *RelayAddressGeneratorNone) AllocatePacketConn(network string, requestedPort int) ( - net.PacketConn, - net.Addr, - error, -) { - conn, err := r.Net.ListenPacket(network, r.Address+":"+strconv.Itoa(requestedPort)) // nolint: noctx - if err != nil { - return nil, nil, err - } - - return conn, conn.LocalAddr(), nil -} - -// AllocateConn generates a new Conn to receive traffic on and the IP/Port -// to populate the allocation response with. -func (r *RelayAddressGeneratorNone) AllocateConn(string, int) (net.Conn, net.Addr, error) { - return nil, nil, errTODO -} diff --git a/vendor/github.com/pion/turn/v4/relay_address_generator_range.go b/vendor/github.com/pion/turn/v4/relay_address_generator_range.go deleted file mode 100644 index 38201c6e41..0000000000 --- a/vendor/github.com/pion/turn/v4/relay_address_generator_range.go +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The Pion community -// SPDX-License-Identifier: MIT - -package turn - -import ( - "fmt" - "net" - - "github.com/pion/randutil" - "github.com/pion/transport/v4" - "github.com/pion/transport/v4/stdnet" -) - -// RelayAddressGeneratorPortRange can be used to only allocate connections inside a defined port range. -// Similar to the RelayAddressGeneratorStatic a static ip address can be set. -type RelayAddressGeneratorPortRange struct { - // RelayAddress is the IP returned to the user when the relay is created - RelayAddress net.IP - - // MinPort the minimum port to allocate - MinPort uint16 - // MaxPort the maximum (inclusive) port to allocate - MaxPort uint16 - - // MaxRetries the amount of tries to allocate a random port in the defined range - MaxRetries int - - // Rand the random source of numbers - Rand randutil.MathRandomGenerator - - // Address is passed to Listen/ListenPacket when creating the Relay - Address string - - Net transport.Net -} - -// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured. -func (r *RelayAddressGeneratorPortRange) Validate() error { - if r.Net == nil { - var err error - r.Net, err = stdnet.NewNet() - if err != nil { - return fmt.Errorf("failed to create network: %w", err) - } - } - - if r.Rand == nil { - r.Rand = randutil.NewMathRandomGenerator() - } - - if r.MaxRetries == 0 { - r.MaxRetries = 10 - } - - switch { - case r.MinPort == 0: - return errMinPortNotZero - case r.MaxPort == 0: - return errMaxPortNotZero - case r.RelayAddress == nil: - return errRelayAddressInvalid - case r.Address == "": - return errListeningAddressInvalid - default: - return nil - } -} - -// AllocatePacketConn generates a new PacketConn to receive traffic on and the IP/Port -// to populate the allocation response with. -func (r *RelayAddressGeneratorPortRange) AllocatePacketConn( - network string, - requestedPort int, -) (net.PacketConn, net.Addr, error) { - if requestedPort != 0 { - conn, err := r.Net.ListenPacket(network, fmt.Sprintf("%s:%d", r.Address, requestedPort)) // nolint: noctx - if err != nil { - return nil, nil, err - } - - relayAddr, ok := conn.LocalAddr().(*net.UDPAddr) - if !ok { - return nil, nil, errNilConn - } - - relayAddr.IP = r.RelayAddress - - return conn, relayAddr, nil - } - - for try := 0; try < r.MaxRetries; try++ { - port := r.MinPort + uint16(r.Rand.Intn(int((r.MaxPort+1)-r.MinPort))) // nolint:gosec // G115 false positive - conn, err := r.Net.ListenPacket(network, fmt.Sprintf("%s:%d", r.Address, port)) // nolint: noctx - if err != nil { - continue - } - - relayAddr, ok := conn.LocalAddr().(*net.UDPAddr) - if !ok { - return nil, nil, errNilConn - } - - relayAddr.IP = r.RelayAddress - - return conn, relayAddr, nil - } - - return nil, nil, errMaxRetriesExceeded -} - -// AllocateConn generates a new Conn to receive traffic on and the IP/Port -// to populate the allocation response with. -func (r *RelayAddressGeneratorPortRange) AllocateConn(string, int) (net.Conn, net.Addr, error) { - return nil, nil, errTODO -} diff --git a/vendor/github.com/pion/turn/v4/relay_address_generator_static.go b/vendor/github.com/pion/turn/v4/relay_address_generator_static.go deleted file mode 100644 index a448363947..0000000000 --- a/vendor/github.com/pion/turn/v4/relay_address_generator_static.go +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The Pion community -// SPDX-License-Identifier: MIT - -package turn - -import ( - "fmt" - "net" - "strconv" - - "github.com/pion/transport/v4" - "github.com/pion/transport/v4/stdnet" -) - -// RelayAddressGeneratorStatic can be used to return static IP address each time a relay is created. -// This can be used when you have a single static IP address that you want to use. -type RelayAddressGeneratorStatic struct { - // RelayAddress is the IP returned to the user when the relay is created - RelayAddress net.IP - - // Address is passed to Listen/ListenPacket when creating the Relay - Address string - - Net transport.Net -} - -// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured. -func (r *RelayAddressGeneratorStatic) Validate() error { - if r.Net == nil { - var err error - r.Net, err = stdnet.NewNet() - if err != nil { - return fmt.Errorf("failed to create network: %w", err) - } - } - - switch { - case r.RelayAddress == nil: - return errRelayAddressInvalid - case r.Address == "": - return errListeningAddressInvalid - default: - return nil - } -} - -// AllocatePacketConn generates a new PacketConn to receive traffic on and the IP/Port -// to populate the allocation response with. -func (r *RelayAddressGeneratorStatic) AllocatePacketConn( - network string, - requestedPort int, -) (net.PacketConn, net.Addr, error) { - conn, err := r.Net.ListenPacket(network, r.Address+":"+strconv.Itoa(requestedPort)) // nolint: noctx - if err != nil { - return nil, nil, err - } - - // Replace actual listening IP with the user requested one of RelayAddressGeneratorStatic - relayAddr, ok := conn.LocalAddr().(*net.UDPAddr) - if !ok { - return nil, nil, errNilConn - } - - relayAddr.IP = r.RelayAddress - - return conn, relayAddr, nil -} - -// AllocateConn generates a new Conn to receive traffic on and the IP/Port -// to populate the allocation response with. -func (r *RelayAddressGeneratorStatic) AllocateConn(string, int) (net.Conn, net.Addr, error) { - return nil, nil, errTODO -} diff --git a/vendor/github.com/pion/turn/v4/.gitignore b/vendor/github.com/pion/turn/v5/.gitignore similarity index 84% rename from vendor/github.com/pion/turn/v4/.gitignore rename to vendor/github.com/pion/turn/v5/.gitignore index 6e2f206a9f..23945578f7 100644 --- a/vendor/github.com/pion/turn/v4/.gitignore +++ b/vendor/github.com/pion/turn/v5/.gitignore @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-FileCopyrightText: 2026 The Pion community # SPDX-License-Identifier: MIT ### JetBrains IDE ### diff --git a/vendor/github.com/pion/turn/v4/.golangci.yml b/vendor/github.com/pion/turn/v5/.golangci.yml similarity index 99% rename from vendor/github.com/pion/turn/v4/.golangci.yml rename to vendor/github.com/pion/turn/v5/.golangci.yml index 4b4025fbc8..43af4c386e 100644 --- a/vendor/github.com/pion/turn/v4/.golangci.yml +++ b/vendor/github.com/pion/turn/v5/.golangci.yml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-FileCopyrightText: 2026 The Pion community # SPDX-License-Identifier: MIT version: "2" diff --git a/vendor/github.com/pion/turn/v5/.goreleaser.yml b/vendor/github.com/pion/turn/v5/.goreleaser.yml new file mode 100644 index 0000000000..8577d86de9 --- /dev/null +++ b/vendor/github.com/pion/turn/v5/.goreleaser.yml @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2026 The Pion community +# SPDX-License-Identifier: MIT + +builds: +- skip: true diff --git a/vendor/github.com/pion/turn/v4/LICENSE b/vendor/github.com/pion/turn/v5/LICENSE similarity index 94% rename from vendor/github.com/pion/turn/v4/LICENSE rename to vendor/github.com/pion/turn/v5/LICENSE index 491caf6b0f..d96df053b9 100644 --- a/vendor/github.com/pion/turn/v4/LICENSE +++ b/vendor/github.com/pion/turn/v5/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 The Pion community +Copyright (c) 2026 The Pion community Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/vendor/github.com/pion/turn/v4/README.md b/vendor/github.com/pion/turn/v5/README.md similarity index 86% rename from vendor/github.com/pion/turn/v4/README.md rename to vendor/github.com/pion/turn/v5/README.md index 9c8d7038ea..5b625698ff 100644 --- a/vendor/github.com/pion/turn/v4/README.md +++ b/vendor/github.com/pion/turn/v5/README.md @@ -11,9 +11,9 @@
GitHub Workflow Status - Go Reference + Go Reference Coverage Status - Go Report Card + Go Report Card License: MIT


@@ -54,10 +54,15 @@ Also take a look at the [Pion WebRTC FAQ](https://github.com/pion/webrtc/wiki/FA Yes. #### How do I implement token-based authentication? -Replace the username with a token in the [AuthHandler](https://github.com/pion/turn/blob/6d0ff435910870eb9024b18321b93b61844fcfec/examples/turn-server/simple/main.go#L49). -The password sent by the client can be any non-empty string, as long as it matches that used by the [GenerateAuthKey](https://github.com/pion/turn/blob/6d0ff435910870eb9024b18321b93b61844fcfec/examples/turn-server/simple/main.go#L41) +Replace the username with a token in the [AuthHandler](examples/turn-server/simple/main.go#L53). +The password sent by the client can be any non-empty string, as long as it matches that used by the [GenerateAuthKey](examples/turn-server/simple/main.go#L45) function. +#### How do I implement mTLS (mutual TLS) authentication? +You can use client certificates for authentication by checking the TLS connection state in your [AuthHandler](examples/mutual-tls-auth/turn-server/main.go#L31). +See the [mutual-tls-auth example](examples/mutual-tls-auth) for a complete implementation that validates client certificates +and matches the certificate's Common Name to the TURN username. + #### Will WebRTC prioritize using STUN over TURN? Yes. @@ -65,8 +70,6 @@ Yes. #### Implemented * **RFC 5389**: [Session Traversal Utilities for NAT (STUN)][rfc5389] * **RFC 5766**: [Traversal Using Relays around NAT (TURN): Relay Extensions to Session Traversal Utilities for NAT (STUN)][rfc5766] - -#### Planned * **RFC 6062**: [Traversal Using Relays around NAT (TURN) Extensions for TCP Allocations][rfc6062] * **RFC 6156**: [Traversal Using Relays around NAT (TURN) Extension for IPv6][rfc6156] diff --git a/vendor/github.com/pion/turn/v5/addr_family.go b/vendor/github.com/pion/turn/v5/addr_family.go new file mode 100644 index 0000000000..7ab781d235 --- /dev/null +++ b/vendor/github.com/pion/turn/v5/addr_family.go @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2026 The Pion community +// SPDX-License-Identifier: MIT + +package turn + +import "github.com/pion/turn/v5/internal/proto" + +// RequestedAddressFamily represents the REQUESTED-ADDRESS-FAMILY Attribute as +// defined in RFC 6156 Section 4.1.1. +type RequestedAddressFamily = proto.RequestedAddressFamily + +// Values for RequestedAddressFamily as defined in RFC 6156 Section 4.1.1. +const ( + RequestedAddressFamilyIPv4 = proto.RequestedFamilyIPv4 + RequestedAddressFamilyIPv6 = proto.RequestedFamilyIPv6 +) diff --git a/vendor/github.com/pion/turn/v4/client.go b/vendor/github.com/pion/turn/v5/client.go similarity index 66% rename from vendor/github.com/pion/turn/v4/client.go rename to vendor/github.com/pion/turn/v5/client.go index 4f2bdb28db..6a11e93dd4 100644 --- a/vendor/github.com/pion/turn/v4/client.go +++ b/vendor/github.com/pion/turn/v5/client.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package turn @@ -15,8 +15,8 @@ import ( "github.com/pion/stun/v3" "github.com/pion/transport/v4" "github.com/pion/transport/v4/stdnet" - "github.com/pion/turn/v4/internal/client" - "github.com/pion/turn/v4/internal/proto" + "github.com/pion/turn/v5/internal/client" + "github.com/pion/turn/v5/internal/proto" ) const ( @@ -47,6 +47,19 @@ type ClientConfig struct { Conn net.PacketConn // Listening socket (net.PacketConn) Net transport.Net LoggerFactory logging.LoggerFactory + + // PermissionTimeout sets the refresh interval of permissions. Defaults to 2 minutes. + PermissionRefreshInterval time.Duration + + // RequestedAddressFamily is the address family to request in allocations (IPv4 or IPv6). + // If not specified (zero value), the client will attempt to infer from the PacketConn's + // local address, falling back to IPv4 if inference fails. See RFC 6156. + RequestedAddressFamily RequestedAddressFamily + + evenPort bool // If EVEN-PORT Attribute should be sent in Allocation + reservationToken []byte // If Server responds with RESERVATION-TOKEN or if Client wishes to send one + bindingRefreshInterval time.Duration + bindingCheckInterval time.Duration } // Client is a STUN server client. @@ -70,11 +83,106 @@ type Client struct { mutex sync.RWMutex // Thread-safe mutexTrMap sync.Mutex // Thread-safe log logging.LeveledLogger // Read-only + + // If EVEN-PORT Attribute should be sent in Allocation + evenPort bool + + // If Server responds with RESERVATION-TOKEN or if Client wishes to send one + reservationToken []byte + + // REQUESTED-ADDRESS-FAMILY attribute for allocations (RFC 6156) + requestedAddressFamily proto.RequestedAddressFamily + + permissionRefreshInterval time.Duration + bindingRefreshInterval time.Duration + bindingCheckInterval time.Duration +} + +// inferAddressFamilyFromConn attempts to determine the address +// family (IPv4 or IPv6) from a PacketConn's local address. +// Returns an error if the address type is not IP-based. +func inferAddressFamilyFromConn( + conn net.PacketConn, +) (proto.RequestedAddressFamily, error) { + addr := conn.LocalAddr() + + switch a := addr.(type) { + case *net.UDPAddr: + if a.IP.To4() != nil { + return proto.RequestedFamilyIPv4, nil + } + + return proto.RequestedFamilyIPv6, nil + case *net.TCPAddr: + if a.IP.To4() != nil { + return proto.RequestedFamilyIPv4, nil + } + + return proto.RequestedFamilyIPv6, nil + default: + return 0, fmt.Errorf("cannot infer address family from %T", addr) //nolint:err113 + } +} + +// getRequestedAddressFamily determines the address family to use +// for TURN allocations. It follows this priority: +// 1. Use explicitly configured RequestedAddressFamily if set +// 2. Try to infer from the PacketConn's local address +// 3. Fall back to IPv4 default per RFC 6156 +func getRequestedAddressFamily( + log logging.LeveledLogger, + config *ClientConfig, +) proto.RequestedAddressFamily { + // If explicitly set, use it + if config.RequestedAddressFamily != 0 { + return config.RequestedAddressFamily + } + + // Try to infer from the PacketConn + if inferred, err := inferAddressFamilyFromConn(config.Conn); err == nil { + log.Debugf("Inferred address family %v from connection", inferred) + + return inferred + } + + log.Debugf("Could not infer address family, defaulting to IPv4") + + // Default to IPv4 per RFC 6156 + return proto.RequestedFamilyIPv4 +} + +// appendRequestedAddressFamilyOrReservation adds either RESERVATION-TOKEN or +// REQUESTED-ADDRESS-FAMILY to the provided setters slice, respecting mutual +// exclusivity rules from RFC 6156. +// The REQUESTED-ADDRESS-FAMILY attribute is +// only included when IPv6 is desired. +func appendRequestedAddressFamilyOrReservation( + setters []stun.Setter, + requestedFamily proto.RequestedAddressFamily, + reservationToken []byte, +) []stun.Setter { + // Clients MUST NOT include a REQUESTED-ADDRESS-FAMILY attribute in an + // Allocate request that contains a RESERVATION-TOKEN attribute. + // https://www.rfc-editor.org/rfc/rfc6156#section-4.1 + if len(reservationToken) != 0 { + return append(setters, proto.ReservationToken(reservationToken)) + } + + // Only include the attribute when IPv6 is explicitly requested. + // This indirectly implied by the specification: + // If the REQUESTED-ADDRESS-FAMILY attribute is absent, the server MUST + // allocate an IPv4-relayed transport address for the TURN client. + // https://www.rfc-editor.org/rfc/rfc6156#section-4.2 + if requestedFamily == proto.RequestedFamilyIPv6 { + return append(setters, requestedFamily) + } + + return setters } // NewClient returns a new Client instance. listeningAddress is the address and port to listen on, // default "0.0.0.0:0". -func NewClient(config *ClientConfig) (*Client, error) { +func NewClient(config *ClientConfig) (*Client, error) { //nolint:gocyclo,cyclop loggerFactory := config.LoggerFactory if loggerFactory == nil { loggerFactory = logging.NewDefaultLoggerFactory() @@ -99,11 +207,14 @@ func NewClient(config *ClientConfig) (*Client, error) { config.Net = n } + // Determine the requested address family (RFC 6156) + requestedAddressFamily := getRequestedAddressFamily(log, config) + var stunServ, turnServ net.Addr var err error if len(config.STUNServerAddr) > 0 { - stunServ, err = config.Net.ResolveUDPAddr("udp4", config.STUNServerAddr) + stunServ, err = config.Net.ResolveUDPAddr("udp", config.STUNServerAddr) if err != nil { return nil, err } @@ -112,7 +223,7 @@ func NewClient(config *ClientConfig) (*Client, error) { } if len(config.TURNServerAddr) > 0 { - turnServ, err = config.Net.ResolveUDPAddr("udp4", config.TURNServerAddr) + turnServ, err = config.Net.ResolveUDPAddr("udp", config.TURNServerAddr) if err != nil { return nil, err } @@ -121,17 +232,23 @@ func NewClient(config *ClientConfig) (*Client, error) { } client := &Client{ - conn: config.Conn, - stunServerAddr: stunServ, - turnServerAddr: turnServ, - username: stun.NewUsername(config.Username), - password: config.Password, - realm: stun.NewRealm(config.Realm), - software: stun.NewSoftware(config.Software), - trMap: client.NewTransactionMap(), - net: config.Net, - rto: rto, - log: log, + conn: config.Conn, + stunServerAddr: stunServ, + turnServerAddr: turnServ, + username: stun.NewUsername(config.Username), + password: config.Password, + realm: stun.NewRealm(config.Realm), + software: stun.NewSoftware(config.Software), + trMap: client.NewTransactionMap(), + net: config.Net, + rto: rto, + log: log, + evenPort: config.evenPort, + reservationToken: config.reservationToken, + requestedAddressFamily: requestedAddressFamily, + permissionRefreshInterval: config.PermissionRefreshInterval, + bindingRefreshInterval: config.bindingRefreshInterval, + bindingCheckInterval: config.bindingCheckInterval, } return client, nil @@ -241,45 +358,53 @@ func (c *Client) SendBindingRequest() (net.Addr, error) { } func (c *Client) sendAllocateRequest(protocol proto.Protocol) ( //nolint:cyclop - proto.RelayedAddress, - proto.Lifetime, - stun.Nonce, - error, + relayed proto.RelayedAddress, + lifetime proto.Lifetime, + nonce stun.Nonce, + reservationToken proto.ReservationToken, + err error, ) { - var relayed proto.RelayedAddress - var lifetime proto.Lifetime - var nonce stun.Nonce - - msg, err := stun.Build( + allocationSetters := []stun.Setter{ stun.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassRequest), proto.RequestedTransport{Protocol: protocol}, - stun.Fingerprint, + } + + allocationSetters = appendRequestedAddressFamilyOrReservation( + allocationSetters, c.requestedAddressFamily, c.reservationToken, ) + if c.evenPort { + allocationSetters = append(allocationSetters, proto.EvenPort{ReservePort: true}) + } + + // FINGERPRINT must be the last attribute per RFC 5389 + allocationSetters = append(allocationSetters, stun.Fingerprint) + + msg, err := stun.Build(allocationSetters...) if err != nil { - return relayed, lifetime, nonce, err + return relayed, lifetime, nonce, reservationToken, err } trRes, err := c.PerformTransaction(msg, c.turnServerAddr, false) if err != nil { - return relayed, lifetime, nonce, err + return relayed, lifetime, nonce, reservationToken, err } res := trRes.Msg // Anonymous allocate failed, trying to authenticate. if err = nonce.GetFrom(res); err != nil { - return relayed, lifetime, nonce, err + return relayed, lifetime, nonce, reservationToken, err } if err = c.realm.GetFrom(res); err != nil { - return relayed, lifetime, nonce, err + return relayed, lifetime, nonce, reservationToken, err } c.realm = append([]byte(nil), c.realm...) c.integrity = stun.NewLongTermIntegrity( c.username.String(), c.realm.String(), c.password, ) // Trying to authorize. - msg, err = stun.Build( + allocationSetters = []stun.Setter{ stun.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassRequest), proto.RequestedTransport{Protocol: protocol}, @@ -287,15 +412,26 @@ func (c *Client) sendAllocateRequest(protocol proto.Protocol) ( //nolint:cyclop &c.realm, &nonce, &c.integrity, - stun.Fingerprint, + } + + allocationSetters = appendRequestedAddressFamilyOrReservation( + allocationSetters, c.requestedAddressFamily, c.reservationToken, ) + if c.evenPort { + allocationSetters = append(allocationSetters, proto.EvenPort{ReservePort: true}) + } + + // FINGERPRINT must be the last attribute per RFC 5389 + allocationSetters = append(allocationSetters, stun.Fingerprint) + + msg, err = stun.Build(allocationSetters...) if err != nil { - return relayed, lifetime, nonce, err + return relayed, lifetime, nonce, reservationToken, err } trRes, err = c.PerformTransaction(msg, c.turnServerAddr, false) if err != nil { - return relayed, lifetime, nonce, err + return relayed, lifetime, nonce, reservationToken, err } res = trRes.Msg @@ -307,23 +443,30 @@ func (c *Client) sendAllocateRequest(protocol proto.Protocol) ( //nolint:cyclop ErrorCodeAttr: code, } - return relayed, lifetime, nonce, turnError + return relayed, lifetime, nonce, reservationToken, turnError } - return relayed, lifetime, nonce, fmt.Errorf("%s", res.Type) //nolint:err113 + return relayed, lifetime, nonce, reservationToken, fmt.Errorf("%s", res.Type) //nolint:err113 } // Getting relayed addresses from response. if err := relayed.GetFrom(res); err != nil { - return relayed, lifetime, nonce, err + return relayed, lifetime, nonce, reservationToken, err } // Getting lifetime from response if err := lifetime.GetFrom(res); err != nil { - return relayed, lifetime, nonce, err + return relayed, lifetime, nonce, reservationToken, err } - return relayed, lifetime, nonce, nil + // Getting reservation-token from response + if c.evenPort { + if err := reservationToken.GetFrom(res); err != nil { + return relayed, lifetime, nonce, reservationToken, err + } + } + + return relayed, lifetime, nonce, reservationToken, nil } // Allocate sends a TURN allocation request to the given transport address. @@ -338,7 +481,7 @@ func (c *Client) Allocate() (net.PacketConn, error) { return nil, fmt.Errorf("%w: %s", errAlreadyAllocated, relayedConn.LocalAddr().String()) } - relayed, lifetime, nonce, err := c.sendAllocateRequest(proto.ProtoUDP) + relayed, lifetime, nonce, reservationToken, err := c.sendAllocateRequest(proto.ProtoUDP) if err != nil { return nil, err } @@ -349,18 +492,22 @@ func (c *Client) Allocate() (net.PacketConn, error) { } relayedConn = client.NewUDPConn(&client.AllocationConfig{ - Client: c, - RelayedAddr: relayedAddr, - ServerAddr: c.turnServerAddr, - Realm: c.realm, - Username: c.username, - Integrity: c.integrity, - Nonce: nonce, - Lifetime: lifetime.Duration, - Net: c.net, - Log: c.log, + Client: c, + RelayedAddr: relayedAddr, + ServerAddr: c.turnServerAddr, + Realm: c.realm, + Username: c.username, + Integrity: c.integrity, + Nonce: nonce, + Lifetime: lifetime.Duration, + Net: c.net, + Log: c.log, + PermissionRefreshInterval: c.permissionRefreshInterval, + BindingRefreshInterval: c.bindingRefreshInterval, + BindingCheckInterval: c.bindingCheckInterval, }) c.setRelayedUDPConn(relayedConn) + c.setReservationToken(reservationToken) return relayedConn, nil } @@ -377,7 +524,7 @@ func (c *Client) AllocateTCP() (*client.TCPAllocation, error) { return nil, fmt.Errorf("%w: %s", errAlreadyAllocated, allocation.Addr()) } - relayed, lifetime, nonce, err := c.sendAllocateRequest(proto.ProtoTCP) + relayed, lifetime, nonce, reservationToken, err := c.sendAllocateRequest(proto.ProtoTCP) if err != nil { return nil, err } @@ -401,6 +548,7 @@ func (c *Client) AllocateTCP() (*client.TCPAllocation, error) { }) c.setTCPAllocation(allocation) + c.setReservationToken(reservationToken) return allocation, nil } @@ -709,3 +857,17 @@ func (c *Client) getTCPAllocation() *client.TCPAllocation { return c.tcpAllocation } + +func (c *Client) setReservationToken(reservationToken []byte) { + c.mutex.Lock() + defer c.mutex.Unlock() + + c.reservationToken = reservationToken +} + +func (c *Client) getReservationToken() []byte { + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c.reservationToken +} diff --git a/vendor/github.com/pion/turn/v4/codecov.yml b/vendor/github.com/pion/turn/v5/codecov.yml similarity index 86% rename from vendor/github.com/pion/turn/v4/codecov.yml rename to vendor/github.com/pion/turn/v5/codecov.yml index 263e4d45c6..b9639c2273 100644 --- a/vendor/github.com/pion/turn/v4/codecov.yml +++ b/vendor/github.com/pion/turn/v5/codecov.yml @@ -3,7 +3,7 @@ # # It is automatically copied from https://github.com/pion/.goassets repository. # -# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-FileCopyrightText: 2026 The Pion community # SPDX-License-Identifier: MIT coverage: diff --git a/vendor/github.com/pion/turn/v4/errors.go b/vendor/github.com/pion/turn/v5/errors.go similarity index 94% rename from vendor/github.com/pion/turn/v4/errors.go rename to vendor/github.com/pion/turn/v5/errors.go index 3ebd26ae8a..8e7a6ede31 100644 --- a/vendor/github.com/pion/turn/v4/errors.go +++ b/vendor/github.com/pion/turn/v5/errors.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package turn @@ -16,7 +16,6 @@ var ( errMaxPortNotZero = errors.New("turn: MaxPort must be not 0") errMinPortNotZero = errors.New("turn: MaxPort must be not 0") errNilConn = errors.New("turn: conn cannot not be nil") - errTODO = errors.New("turn: TODO") errAlreadyListening = errors.New("turn: already listening") errFailedToClose = errors.New("turn: Server failed to close") errFailedToRetransmitTransaction = errors.New("turn: failed to retransmit transaction") diff --git a/vendor/github.com/pion/turn/v4/internal/allocation/allocation.go b/vendor/github.com/pion/turn/v5/internal/allocation/allocation.go similarity index 66% rename from vendor/github.com/pion/turn/v4/internal/allocation/allocation.go rename to vendor/github.com/pion/turn/v5/internal/allocation/allocation.go index 8fb475244f..5008e363bf 100644 --- a/vendor/github.com/pion/turn/v4/internal/allocation/allocation.go +++ b/vendor/github.com/pion/turn/v5/internal/allocation/allocation.go @@ -1,10 +1,11 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT // Package allocation contains all CRUD operations for allocations package allocation import ( + "errors" "net" "sync" "sync/atomic" @@ -12,8 +13,8 @@ import ( "github.com/pion/logging" "github.com/pion/stun/v3" - "github.com/pion/turn/v4/internal/ipnet" - "github.com/pion/turn/v4/internal/proto" + "github.com/pion/turn/v5/internal/ipnet" + "github.com/pion/turn/v5/internal/proto" ) type allocationResponse struct { @@ -21,13 +22,19 @@ type allocationResponse struct { responseAttrs []stun.Setter } +type tcpConnection struct { + net.Conn + + isBound atomic.Bool // ConnectionBind has been done for this TCP Connection + bindTimer *time.Timer +} + // Allocation is tied to a FiveTuple and relays traffic // use CreateAllocation and GetAllocation to operate. type Allocation struct { RelayAddr net.Addr Protocol Protocol TurnSocket net.PacketConn - RelaySocket net.PacketConn fiveTuple *FiveTuple permissionsLock sync.RWMutex permissions map[string]*Permission @@ -35,9 +42,18 @@ type Allocation struct { channelBindings []*ChannelBind lifetimeTimer *time.Timer closed chan any - username, realm string + userID, realm string eventHandler EventHandler log logging.LeveledLogger + addressFamily proto.RequestedAddressFamily // RFC 6156 + + // Relay Transport for UDP + relayPacketConn net.PacketConn + + // Relay Transport for TCP + relayListener net.Listener + + tcpConnections map[proto.ConnectionID]*tcpConnection // Guarded by AllocationManager lock // Some clients (Firefox or others using resiprocate's nICE lib) may retry allocation // with same 5 tuple when received 413, for compatible with these clients, @@ -54,12 +70,13 @@ func NewAllocation( log logging.LeveledLogger, ) *Allocation { return &Allocation{ - TurnSocket: turnSocket, - fiveTuple: fiveTuple, - permissions: make(map[string]*Permission, 64), - closed: make(chan any), - eventHandler: eventHandler, - log: log, + TurnSocket: turnSocket, + fiveTuple: fiveTuple, + permissions: make(map[string]*Permission, 64), + closed: make(chan any), + eventHandler: eventHandler, + log: log, + tcpConnections: make(map[proto.ConnectionID]*tcpConnection), } } @@ -80,7 +97,7 @@ func (a *Allocation) AddPermission(perms *Permission) { a.permissionsLock.RUnlock() if ok { - existedPermission.refresh(permissionTimeout) + existedPermission.refresh(perms.timeout) return } @@ -93,12 +110,12 @@ func (a *Allocation) AddPermission(perms *Permission) { if a.eventHandler.OnPermissionCreated != nil { if u, ok := perms.Addr.(*net.UDPAddr); ok { a.eventHandler.OnPermissionCreated(a.fiveTuple.SrcAddr, a.fiveTuple.DstAddr, - a.fiveTuple.Protocol.String(), a.username, a.realm, + a.fiveTuple.Protocol.String(), a.userID, a.realm, a.RelayAddr, u.IP) } } - perms.start(permissionTimeout) + perms.start(perms.timeout) } // RemovePermission removes the net.Addr's fingerprint from the allocation's permissions. @@ -110,7 +127,7 @@ func (a *Allocation) RemovePermission(addr net.Addr) { if a.eventHandler.OnPermissionDeleted != nil { if u, ok := addr.(*net.UDPAddr); ok { a.eventHandler.OnPermissionDeleted(a.fiveTuple.SrcAddr, a.fiveTuple.DstAddr, - a.fiveTuple.Protocol.String(), a.username, a.realm, + a.fiveTuple.Protocol.String(), a.userID, a.realm, a.RelayAddr, u.IP) } } @@ -130,13 +147,20 @@ func (a *Allocation) ListPermissions() []*Permission { // AddChannelBind adds a new ChannelBind to the allocation, it also updates the // permissions needed for this ChannelBind. -func (a *Allocation) AddChannelBind(chanBind *ChannelBind, lifetime time.Duration) error { +func (a *Allocation) AddChannelBind(chanBind *ChannelBind, channelLifetime, permissionLifetime time.Duration) error { // Check that this channel id isn't bound to another transport address, and // that this transport address isn't bound to another channel number. channelByNumber := a.GetChannelByNumber(chanBind.Number) + channelByAddr := a.GetChannelByAddr(chanBind.Peer) + + // Peer already bound to a different channel number. + if channelByAddr != nil && channelByAddr.Number != chanBind.Number { + return ErrSamePeerDifferentChannel + } - if channelByNumber != a.GetChannelByAddr(chanBind.Peer) { - return errSameChannelDifferentPeer + // Channel number already bound to a different peer. + if channelByNumber != nil && !ipnet.AddrEqual(channelByNumber.Peer, chanBind.Peer) { + return ErrSameChannelDifferentPeer } // Add or refresh this channel. @@ -146,21 +170,21 @@ func (a *Allocation) AddChannelBind(chanBind *ChannelBind, lifetime time.Duratio chanBind.allocation = a a.channelBindings = append(a.channelBindings, chanBind) - chanBind.start(lifetime) + chanBind.start(channelLifetime) // Channel binds also refresh permissions. - a.AddPermission(NewPermission(chanBind.Peer, a.log)) + a.AddPermission(NewPermission(chanBind.Peer, a.log, permissionLifetime)) if a.eventHandler.OnChannelCreated != nil { a.eventHandler.OnChannelCreated(a.fiveTuple.SrcAddr, a.fiveTuple.DstAddr, - a.fiveTuple.Protocol.String(), a.username, a.realm, + a.fiveTuple.Protocol.String(), a.userID, a.realm, a.RelayAddr, chanBind.Peer, uint16(chanBind.Number)) } } else { - channelByNumber.refresh(lifetime) + channelByNumber.refresh(channelLifetime) // Channel binds also refresh permissions. - a.AddPermission(NewPermission(channelByNumber.Peer, a.log)) + a.AddPermission(NewPermission(channelByNumber.Peer, a.log, permissionLifetime)) } return nil @@ -175,7 +199,7 @@ func (a *Allocation) RemoveChannelBind(number proto.ChannelNumber) bool { if a.channelBindings[i].Number == number { if a.eventHandler.OnChannelDeleted != nil { a.eventHandler.OnChannelDeleted(a.fiveTuple.SrcAddr, a.fiveTuple.DstAddr, - a.fiveTuple.Protocol.String(), a.username, a.realm, + a.fiveTuple.Protocol.String(), a.userID, a.realm, a.RelayAddr, a.channelBindings[i].Peer, uint16(a.channelBindings[i].Number)) } @@ -231,6 +255,11 @@ func (a *Allocation) Refresh(lifetime time.Duration) { } } +// AddressFamily returns the address family of the allocation (RFC 6156). +func (a *Allocation) AddressFamily() proto.RequestedAddressFamily { + return a.addressFamily +} + // SetResponseCache cache allocation response for retransmit allocation request. func (a *Allocation) SetResponseCache(transactionID [stun.TransactionIDSize]byte, attrs []stun.Setter) { a.responseCache.Store(&allocationResponse{ @@ -248,6 +277,34 @@ func (a *Allocation) GetResponseCache() (id [stun.TransactionIDSize]byte, attrs return } +func (a *Allocation) removeTCPConnection(connectionID proto.ConnectionID) { + if conn, ok := a.tcpConnections[connectionID]; ok { + conn.bindTimer.Stop() + + if err := conn.SetDeadline(time.Now()); err != nil && !errors.Is(err, net.ErrClosed) { + a.log.Errorf("Failed to set deadline on TCP Connection %s %v", + a.fiveTuple, + err) + } + + if err := conn.Close(); err != nil && !errors.Is(err, net.ErrClosed) { + a.log.Errorf("Failed to close TCP Socket for %s %v", + a.fiveTuple, + err) + } + } + + delete(a.tcpConnections, connectionID) +} + +// RemoveTCPConnection closes and removes the TCP Connection. +func (a *Allocation) RemoveTCPConnection(m *Manager, connectionID proto.ConnectionID) { + m.lock.Lock() + defer m.lock.Unlock() + + a.removeTCPConnection(connectionID) +} + // Close closes the allocation. func (a *Allocation) Close() error { select { @@ -259,6 +316,10 @@ func (a *Allocation) Close() error { a.lifetimeTimer.Stop() + for tcpConnection := range a.tcpConnections { + a.removeTCPConnection(tcpConnection) + } + for _, p := range a.ListPermissions() { a.RemovePermission(p.Addr) p.lifetimeTimer.Stop() @@ -269,7 +330,24 @@ func (a *Allocation) Close() error { c.lifetimeTimer.Stop() } - return a.RelaySocket.Close() + if a.relayPacketConn != nil { + return a.relayPacketConn.Close() + } + + if a.relayListener != nil { + return a.relayListener.Close() + } + + return nil +} + +// WriteTo writes a packet with payload p to addr via the Relay socket. +func (a *Allocation) WriteTo(p []byte, addr net.Addr) (n int, err error) { + if a.relayPacketConn == nil { + return 0, errNilRelaySocket + } + + return a.relayPacketConn.WriteTo(p, addr) } // https://tools.ietf.org/html/rfc5766#section-10.3 @@ -294,11 +372,11 @@ func (a *Allocation) Close() error { const rtpMTU = 1600 -func (a *Allocation) packetHandler(manager *Manager) { +func (a *Allocation) packetConnHandler(manager *Manager) { buffer := make([]byte, rtpMTU) for { - n, srcAddr, err := a.RelaySocket.ReadFrom(buffer) + n, srcAddr, err := a.relayPacketConn.ReadFrom(buffer) if err != nil { manager.DeleteAllocation(a.fiveTuple) @@ -306,7 +384,7 @@ func (a *Allocation) packetHandler(manager *Manager) { } a.log.Debugf("Relay socket %s received %d bytes from %s", - a.RelaySocket.LocalAddr(), + a.relayPacketConn.LocalAddr(), n, srcAddr) @@ -353,3 +431,52 @@ func (a *Allocation) packetHandler(manager *Manager) { } } } + +func (a *Allocation) connHandler(manager *Manager) { + for { + conn, err := a.relayListener.Accept() + if err != nil { + manager.DeleteAllocation(a.fiveTuple) + + return + } + + tcpAddr, ok := conn.RemoteAddr().(*net.TCPAddr) + if !ok { + _ = conn.Close() + a.log.Errorf("Failed to accept TCP Connection for Allocation %v %v", a.fiveTuple.SrcAddr, err) + + continue + } + a.log.Debugf("Relay %s accepted connection from %s", a.relayListener.Addr(), tcpAddr) + + cid, err := manager.addTCPConnection(a, conn) + if err != nil { + a.log.Errorf("Failed to create inbound TCP Connection for Allocation %v %v", a.fiveTuple.SrcAddr, err) + + _ = conn.Close() + + continue + } + + msg, err := stun.Build( + stun.TransactionID, + stun.NewType(stun.MethodConnectionAttempt, stun.ClassIndication), + proto.PeerAddress{IP: tcpAddr.IP, Port: tcpAddr.Port}, + cid, + ) + if err != nil { + a.log.Errorf("Failed to ConnectionAttempt for Allocation %v %v", a.fiveTuple.SrcAddr, err) + + a.RemoveTCPConnection(manager, cid) + + continue + } + + if _, err = a.TurnSocket.WriteTo(msg.Raw, a.fiveTuple.SrcAddr); err != nil { + a.log.Errorf("Failed to send ConnectionAttempt for Allocation %v %v", a.fiveTuple.SrcAddr, err) + + a.RemoveTCPConnection(manager, cid) + } + } +} diff --git a/vendor/github.com/pion/turn/v5/internal/allocation/allocation_manager.go b/vendor/github.com/pion/turn/v5/internal/allocation/allocation_manager.go new file mode 100644 index 0000000000..ec37b5ece2 --- /dev/null +++ b/vendor/github.com/pion/turn/v5/internal/allocation/allocation_manager.go @@ -0,0 +1,497 @@ +// SPDX-FileCopyrightText: 2026 The Pion community +// SPDX-License-Identifier: MIT + +package allocation + +import ( + "fmt" + "net" + "sync" + "sync/atomic" + "time" + + "github.com/pion/logging" + "github.com/pion/randutil" + "github.com/pion/turn/v5/internal/proto" +) + +// If no ConnectionBind request associated with this peer data +// connection is received after 30 seconds, the peer data connection +// MUST be closed. +const defaultTCPConnectionBindTimeout = time.Second * 30 + +// AllocateListenerConfig contains the parameters passed to the relay address allocator +// when creating a new UDP or TCP allocation. +type AllocateListenerConfig struct { + // Network specifies the network type for the allocation: "udp4", "udp6", "tcp4", or "tcp6". + Network string + // UserID is the authenticated user's identifier as returned by the AuthHandler. + // + // Note: The UserID is typcally the same as the TURN username, except for authentication + // schemes that overload the username field with additional info (e.g., the lifetime of the + // credential, as in the time-windowed credential mechanism in + // https://datatracker.ietf.org/doc/html/draft-uberti-behave-turn-rest-00. + UserID string + // Realm is the TURN realm for this allocation. + Realm string + // RequestedPort is the port requested by the client in the TURN Allocate request. + // A value of 0 indicates that the client did not request a specific port and any + // available port may be used. + RequestedPort int +} + +// AllocateConnConfig contains the parameters passed to the relay address allocator +// when creating a new outbound TCP connection for RFC 6062 (TURN TCP) Connect requests. +type AllocateConnConfig struct { + // Network specifies the network type for the connection: "tcp4" or "tcp6". + Network string + // UserID is the authenticated user's identifier as returned by the AuthHandler. + // + // Note: The UserID is typcally the same as the TURN username, except for authentication + // schemes that overload the username field with additional info (e.g., the lifetime of the + // credential, as in the time-windowed credential mechanism in + // https://datatracker.ietf.org/doc/html/draft-uberti-behave-turn-rest-00. + UserID string + // Realm is the TURN realm for this allocation. + Realm string + // LocalAddr is the relay address to bind the local side of the connection + // to. Implementations must allocate the local address as requested. + LocalAddr net.Addr + // RemoteAddr is the peer address to connect to. + RemoteAddr net.Addr +} + +// ManagerConfig a bag of config params for Manager. +type ManagerConfig struct { + LeveledLogger logging.LeveledLogger + AllocatePacketConn func(info AllocateListenerConfig) (net.PacketConn, net.Addr, error) + AllocateListener func(info AllocateListenerConfig) (net.Listener, net.Addr, error) + AllocateConn func(info AllocateConnConfig) (net.Conn, error) + PermissionHandler func(sourceAddr net.Addr, peerIP net.IP) bool + EventHandler EventHandler + + tcpConnectionBindTimeout time.Duration +} + +type reservation struct { + token string + port int +} + +// Manager is used to hold active allocations. +type Manager struct { + lock sync.RWMutex + log logging.LeveledLogger + tcpConnectionBindTimeout time.Duration + + allocations map[FiveTupleFingerprint]*Allocation + reservations []*reservation + + allocatePacketConn func(conf AllocateListenerConfig) (net.PacketConn, net.Addr, error) + allocateListener func(conf AllocateListenerConfig) (net.Listener, net.Addr, error) + allocateConn func(conf AllocateConnConfig) (net.Conn, error) + permissionHandler func(sourceAddr net.Addr, peerIP net.IP) bool + EventHandler EventHandler +} + +// NewManager creates a new instance of Manager. +func NewManager(config ManagerConfig) (*Manager, error) { + switch { + case config.AllocatePacketConn == nil: + return nil, errAllocatePacketConnMustBeSet + case config.AllocateListener == nil: + return nil, errAllocateListenerMustBeSet + case config.AllocateConn == nil: + return nil, errAllocateConnMustBeSet + case config.LeveledLogger == nil: + return nil, errLeveledLoggerMustBeSet + } + + tcpConnectionBindTimeout := config.tcpConnectionBindTimeout + if tcpConnectionBindTimeout == 0 { + tcpConnectionBindTimeout = defaultTCPConnectionBindTimeout + } + + return &Manager{ + log: config.LeveledLogger, + allocations: make(map[FiveTupleFingerprint]*Allocation, 64), + allocatePacketConn: config.AllocatePacketConn, + allocateListener: config.AllocateListener, + allocateConn: config.AllocateConn, + permissionHandler: config.PermissionHandler, + EventHandler: config.EventHandler, + tcpConnectionBindTimeout: tcpConnectionBindTimeout, + }, nil +} + +// GetAllocation fetches the allocation matching the passed FiveTuple. +func (m *Manager) GetAllocation(fiveTuple *FiveTuple) *Allocation { + m.lock.RLock() + defer m.lock.RUnlock() + + return m.allocations[fiveTuple.Fingerprint()] +} + +// GetAllocationForUserID fetches the allocation matching the passed FiveTuple and Username. +func (m *Manager) GetAllocationForUserID(fiveTuple *FiveTuple, userID string) *Allocation { + allocation := m.GetAllocation(fiveTuple) + if allocation != nil && allocation.userID == userID { + return allocation + } + + return nil +} + +// AllocationCount returns the number of existing allocations. +func (m *Manager) AllocationCount() int { + m.lock.RLock() + defer m.lock.RUnlock() + + return len(m.allocations) +} + +// Close closes the manager and closes all allocations it manages. +func (m *Manager) Close() error { + m.lock.Lock() + defer m.lock.Unlock() + + for _, a := range m.allocations { + if err := a.Close(); err != nil { + return err + } + } + + return nil +} + +// CreateAllocation creates a new allocation and starts relaying. +func (m *Manager) CreateAllocation( // nolint: cyclop + fiveTuple *FiveTuple, + turnSocket net.PacketConn, + protocol proto.Protocol, + requestedPort int, + lifetime time.Duration, + userID, realm string, + addressFamily proto.RequestedAddressFamily, +) (*Allocation, error) { + switch { + case fiveTuple == nil: + return nil, errNilFiveTuple + case fiveTuple.SrcAddr == nil: + return nil, errNilFiveTupleSrcAddr + case fiveTuple.DstAddr == nil: + return nil, errNilFiveTupleDstAddr + case turnSocket == nil: + return nil, errNilTurnSocket + case lifetime == 0: + return nil, errLifetimeZero + } + + if alloc := m.GetAllocation(fiveTuple); alloc != nil { + return nil, fmt.Errorf("%w: %v", errDupeFiveTuple, fiveTuple) + } + alloc := NewAllocation(turnSocket, fiveTuple, m.EventHandler, m.log) + alloc.userID = userID + alloc.realm = realm + alloc.addressFamily = addressFamily + + switch protocol { + case proto.ProtoUDP: + network := "udp4" + if addressFamily == proto.RequestedFamilyIPv6 { + network = "udp6" + } + conn, relayAddr, err := m.allocatePacketConn(AllocateListenerConfig{ + Network: network, + UserID: userID, + Realm: realm, + RequestedPort: requestedPort, + }) + if err != nil { + return nil, err + } + alloc.relayPacketConn = conn + alloc.RelayAddr = relayAddr + case proto.ProtoTCP: + network := "tcp4" + if addressFamily == proto.RequestedFamilyIPv6 { + network = "tcp6" + } + ln, relayAddr, err := m.allocateListener(AllocateListenerConfig{ + Network: network, + UserID: userID, + Realm: realm, + RequestedPort: requestedPort, + }) + if err != nil { + return nil, err + } + alloc.relayListener = ln + alloc.RelayAddr = relayAddr + } + + m.log.Debugf("Listening on relay address: %s", alloc.RelayAddr) + + alloc.lifetimeTimer = time.AfterFunc(lifetime, func() { + m.DeleteAllocation(alloc.fiveTuple) + }) + + m.lock.Lock() + m.allocations[fiveTuple.Fingerprint()] = alloc + m.lock.Unlock() + + if m.EventHandler.OnAllocationCreated != nil { + m.EventHandler.OnAllocationCreated(fiveTuple.SrcAddr, fiveTuple.DstAddr, + fiveTuple.Protocol.String(), userID, realm, alloc.RelayAddr, requestedPort) + } + + // Only start the UDP relay loop for UDP allocations. + if alloc.relayPacketConn != nil { + go alloc.packetConnHandler(m) + } + // For TCP allocations, accept inbound connections on the relayed listener and notify the client. + if alloc.relayListener != nil { + go alloc.connHandler(m) + } + + return alloc, nil +} + +// DeleteAllocation removes an allocation. +func (m *Manager) DeleteAllocation(fiveTuple *FiveTuple) { + fingerprint := fiveTuple.Fingerprint() + + m.lock.Lock() + allocation := m.allocations[fingerprint] + delete(m.allocations, fingerprint) + m.lock.Unlock() + + if allocation == nil { + return + } + + m.lock.Lock() + if err := allocation.Close(); err != nil { + m.log.Errorf("Failed to close allocation: %v", err) + } + m.lock.Unlock() + + if m.EventHandler.OnAllocationDeleted != nil { + m.EventHandler.OnAllocationDeleted(fiveTuple.SrcAddr, fiveTuple.DstAddr, + fiveTuple.Protocol.String(), allocation.userID, allocation.realm) + } +} + +// CreateReservation stores the reservation for the token+port. +func (m *Manager) CreateReservation(reservationToken string, port int) { + time.AfterFunc(30*time.Second, func() { + m.lock.Lock() + defer m.lock.Unlock() + for i := len(m.reservations) - 1; i >= 0; i-- { + if m.reservations[i].token == reservationToken { + m.reservations = append(m.reservations[:i], m.reservations[i+1:]...) + + return + } + } + }) + + m.lock.Lock() + m.reservations = append(m.reservations, &reservation{ + token: reservationToken, + port: port, + }) + m.lock.Unlock() +} + +// GetReservation returns the port for a given reservation if it exists. +func (m *Manager) GetReservation(reservationToken string) (int, bool) { + m.lock.RLock() + defer m.lock.RUnlock() + + for _, r := range m.reservations { + if r.token == reservationToken { + return r.port, true + } + } + + return 0, false +} + +// GetRandomEvenPort returns a random un-allocated udp4 port. +func (m *Manager) GetRandomEvenPort() (int, error) { + for i := 0; i < 128; i++ { + conn, addr, err := m.allocatePacketConn(AllocateListenerConfig{Network: "udp4"}) + if err != nil { + return 0, err + } + udpAddr, ok := addr.(*net.UDPAddr) + err = conn.Close() + if err != nil { + return 0, err + } + + if !ok { + return 0, errFailedToCastUDPAddr + } + if udpAddr.Port%2 == 0 { + return udpAddr.Port, nil + } + } + + return 0, errFailedToAllocateEvenPort +} + +// GrantPermission handles permission requests by calling the permission handler callback +// associated with the TURN server listener socket. +func (m *Manager) GrantPermission(sourceAddr net.Addr, peerIP net.IP) error { + // No permission handler: open + if m.permissionHandler == nil { + return nil + } + + if m.permissionHandler(sourceAddr, peerIP) { + return nil + } + + return errAdminProhibited +} + +// CreateTCPConnection creates a new outbound TCP Connection and returns the Connection-ID +// if it succeeds. +func (m *Manager) CreateTCPConnection( // nolint: cyclop + allocation *Allocation, + peerAddress proto.PeerAddress, +) (proto.ConnectionID, error) { + if len(peerAddress.IP) == 0 || peerAddress.Port == 0 { + return 0, errInvalidPeerAddress + } + + relayAddr := allocation.RelayAddr + if allocation.RelayAddr == nil { + m.log.Warn("Failed to create TCP Connection: Relay address not available") + + return 0, ErrTCPConnectionTimeoutOrFailure + } + + remoteAddr := &net.TCPAddr{IP: peerAddress.IP, Port: peerAddress.Port} + + m.lock.Lock() + if m.isDupeTCPConnection(allocation, remoteAddr) { + return 0, ErrDupeTCPConnection + } + m.lock.Unlock() + + // RFC 6156: + // "After the request has been successfully authenticated, the TURN + // server allocates a transport address of the type indicated in the + // REQUESTED-ADDRESS-FAMILY attribute." + network := "tcp4" + if allocation.AddressFamily() == proto.RequestedFamilyIPv6 { + network = "tcp6" + } + + conn, err := m.allocateConn(AllocateConnConfig{ + Network: network, + UserID: allocation.userID, + Realm: allocation.realm, + LocalAddr: relayAddr, + RemoteAddr: remoteAddr, + }) // nolint: noctx + if err != nil { + m.log.Warnf("Failed to create TCP Connection: %v", err) + + return 0, ErrTCPConnectionTimeoutOrFailure + } + + connectionID, err := m.addTCPConnection(allocation, conn) + if err != nil { + if closeErr := conn.Close(); closeErr != nil { + m.log.Warnf("Failed to close TCP connection after ConnectionID generation failed: %v", closeErr) + } + } + + return connectionID, err +} + +func (m *Manager) addTCPConnection(allocation *Allocation, conn net.Conn) (proto.ConnectionID, error) { + rand64, err := randutil.CryptoUint64() + if err != nil { + return 0, err + } + + connectionID := proto.ConnectionID(uint32(rand64 >> 32)) // nolint: gosec + + m.lock.Lock() + defer m.lock.Unlock() + + for _, a := range m.allocations { + if _, ok := a.tcpConnections[connectionID]; ok { + return 0, errFailedToGenerateConnectionID + } + } + + newConnAddr, ok := conn.RemoteAddr().(*net.TCPAddr) + if !ok { + return 0, ErrDupeTCPConnection + } + + if m.isDupeTCPConnection(allocation, newConnAddr) { + return 0, ErrDupeTCPConnection + } + + tcpConn := &tcpConnection{conn, atomic.Bool{}, nil} + allocation.tcpConnections[connectionID] = tcpConn + tcpConn.bindTimer = time.AfterFunc(m.tcpConnectionBindTimeout, func() { + if !tcpConn.isBound.Load() { + m.log.Warnf("Removing TCP Connection that was never bound %v %v", connectionID, allocation.fiveTuple) + allocation.RemoveTCPConnection(m, connectionID) + } + }) + + return connectionID, nil +} + +func (m *Manager) isDupeTCPConnection(allocation *Allocation, remoteAddr *net.TCPAddr) bool { + for i := range allocation.tcpConnections { + tcpAddr, ok := allocation.tcpConnections[i].RemoteAddr().(*net.TCPAddr) + if !ok { + return true + } else if tcpAddr.IP.Equal(remoteAddr.IP) && tcpAddr.Port == remoteAddr.Port { + return true + } + } + + return false +} + +// GetTCPConnection returns the TCP Connection for the given ConnectionID. +func (m *Manager) GetTCPConnection(userID string, connectionID proto.ConnectionID) net.Conn { + m.lock.Lock() + defer m.lock.Unlock() + + for _, a := range m.allocations { + if tcpConnection, ok := a.tcpConnections[connectionID]; ok { + if a.userID != userID || tcpConnection.isBound.Swap(true) { + return nil + } + + tcpConnection.bindTimer.Stop() + + return tcpConnection + } + } + + return nil +} + +func (m *Manager) RemoveTCPConnection(connectionID proto.ConnectionID) { + m.lock.Lock() + defer m.lock.Unlock() + + for _, a := range m.allocations { + if _, ok := a.tcpConnections[connectionID]; ok { + a.removeTCPConnection(connectionID) + } + } +} diff --git a/vendor/github.com/pion/turn/v4/internal/allocation/channel_bind.go b/vendor/github.com/pion/turn/v5/internal/allocation/channel_bind.go similarity index 91% rename from vendor/github.com/pion/turn/v4/internal/allocation/channel_bind.go rename to vendor/github.com/pion/turn/v5/internal/allocation/channel_bind.go index 6ad9b46f01..0d8fecd267 100644 --- a/vendor/github.com/pion/turn/v4/internal/allocation/channel_bind.go +++ b/vendor/github.com/pion/turn/v5/internal/allocation/channel_bind.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package allocation @@ -8,7 +8,7 @@ import ( "time" "github.com/pion/logging" - "github.com/pion/turn/v4/internal/proto" + "github.com/pion/turn/v5/internal/proto" ) // ChannelBind represents a TURN Channel diff --git a/vendor/github.com/pion/turn/v5/internal/allocation/errors.go b/vendor/github.com/pion/turn/v5/internal/allocation/errors.go new file mode 100644 index 0000000000..ae84f74729 --- /dev/null +++ b/vendor/github.com/pion/turn/v5/internal/allocation/errors.go @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2026 The Pion community +// SPDX-License-Identifier: MIT + +package allocation + +import "errors" + +var ( + ErrTCPConnectionTimeoutOrFailure = errors.New("failed to create tcp connection") + ErrDupeTCPConnection = errors.New("tcp connection already exists for peer address") + + // ErrSameChannelDifferentPeer is returned when a client attempts to bind a + // channel number that is already bound to a different peer address. + ErrSameChannelDifferentPeer = errors.New("you cannot use the same channel number with different peer") + + // ErrSamePeerDifferentChannel is returned when a client attempts to bind a + // peer address that is already bound to a different channel number. + ErrSamePeerDifferentChannel = errors.New("you cannot use the same peer with different channel number") + + errAllocatePacketConnMustBeSet = errors.New("AllocatePacketConn must be set") + errAllocateListenerMustBeSet = errors.New("AllocateListener must be set") + errAllocateConnMustBeSet = errors.New("AllocateConn must be set") + errLeveledLoggerMustBeSet = errors.New("LeveledLogger must be set") + errNilFiveTuple = errors.New("allocations must not be created with nil FivTuple") + errNilFiveTupleSrcAddr = errors.New("allocations must not be created with nil FiveTuple.SrcAddr") + errNilFiveTupleDstAddr = errors.New("allocations must not be created with nil FiveTuple.DstAddr") + errNilTurnSocket = errors.New("allocations must not be created with nil turnSocket") + errLifetimeZero = errors.New("allocations must not be created with a lifetime of 0") + errDupeFiveTuple = errors.New("allocation attempt created with duplicate FiveTuple") + errFailedToCastUDPAddr = errors.New("failed to cast net.Addr to *net.UDPAddr") + errFailedToAllocateEvenPort = errors.New("failed to allocate an even port") + errAdminProhibited = errors.New("permission request administratively prohibited") + errFailedToGenerateConnectionID = errors.New("failed to generate a unique connection id") + errInvalidPeerAddress = errors.New("invalid peer address") + errNilRelaySocket = errors.New("allocation has no relay socket") +) diff --git a/vendor/github.com/pion/turn/v4/internal/allocation/event_handler.go b/vendor/github.com/pion/turn/v5/internal/allocation/event_handler.go similarity index 84% rename from vendor/github.com/pion/turn/v4/internal/allocation/event_handler.go rename to vendor/github.com/pion/turn/v5/internal/allocation/event_handler.go index 1d9bd3f77d..b5946851d4 100644 --- a/vendor/github.com/pion/turn/v4/internal/allocation/event_handler.go +++ b/vendor/github.com/pion/turn/v5/internal/allocation/event_handler.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package allocation @@ -9,9 +9,10 @@ import ( // EventHandler is a set of callbacks that the server will call at certain hook points during an // allocation's lifecycle. All events are reported with the context that identifies the allocation -// triggering the event (source and destination address, protocol, username and realm used for -// authenticating the allocation), plus additional callback specific parameters. It is OK to handle -// only a subset of the callbacks. +// triggering the event (source and destination address, protocol, user-id (as parsed and returned +// by the authentication handler from the TURN username) and realm used for authenticating the +// allocation), plus additional callback specific parameters. It is OK to handle only a subset of +// the callbacks. type EventHandler struct { // OnAuth is called after an authentication request has been processed with the TURN method // triggering the authentication request (either "Allocate", "Refresh" "CreatePermission", @@ -20,28 +21,28 @@ type EventHandler struct { // OnAllocationCreated is called after a new allocation has been made. The relayAddr // argument specifies the relay address and requestedPort is the port requested by the // client (if any). - OnAllocationCreated func(srcAddr, dstAddr net.Addr, protocol, username, realm string, + OnAllocationCreated func(srcAddr, dstAddr net.Addr, protocol, userID, realm string, relayAddr net.Addr, requestedPort int) // OnAllocationDeleted is called after an allocation has been removed. - OnAllocationDeleted func(srcAddr, dstAddr net.Addr, protocol, username, realm string) + OnAllocationDeleted func(srcAddr, dstAddr net.Addr, protocol, userID, realm string) // OnAllocationError is called when the readloop hdndling an allocation exits with an // error with an error message. OnAllocationError func(srcAddr, dstAddr net.Addr, protocol, message string) // OnPermissionCreated is called after a new permission has been made to an IP address. - OnPermissionCreated func(srcAddr, dstAddr net.Addr, protocol, username, realm string, + OnPermissionCreated func(srcAddr, dstAddr net.Addr, protocol, userID, realm string, relayAddr net.Addr, peer net.IP) // OnPermissionDeleted is called after a permission for a given IP address has been // removed. - OnPermissionDeleted func(srcAddr, dstAddr net.Addr, protocol, username, realm string, + OnPermissionDeleted func(srcAddr, dstAddr net.Addr, protocol, userID, realm string, relayAddr net.Addr, peer net.IP) // OnChannelCreated is called after a new channel has been made. The relay address, the // peer address and the channel number can be used to uniquely identify the channel // created. - OnChannelCreated func(srcAddr, dstAddr net.Addr, protocol, username, realm string, + OnChannelCreated func(srcAddr, dstAddr net.Addr, protocol, userID, realm string, relayAddr, peer net.Addr, channelNumber uint16) // OnChannelDeleted is called after a channel has been removed from the server. The relay // address, the peer address and the channel number can be used to uniquely identify the // channel deleted. - OnChannelDeleted func(srcAddr, dstAddr net.Addr, protocol, username, realm string, + OnChannelDeleted func(srcAddr, dstAddr net.Addr, protocol, userID, realm string, relayAddr, peer net.Addr, channelNumber uint16) } diff --git a/vendor/github.com/pion/turn/v4/internal/allocation/five_tuple.go b/vendor/github.com/pion/turn/v5/internal/allocation/five_tuple.go similarity index 96% rename from vendor/github.com/pion/turn/v4/internal/allocation/five_tuple.go rename to vendor/github.com/pion/turn/v5/internal/allocation/five_tuple.go index b9eba87272..37e4d3e9ba 100644 --- a/vendor/github.com/pion/turn/v4/internal/allocation/five_tuple.go +++ b/vendor/github.com/pion/turn/v5/internal/allocation/five_tuple.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package allocation diff --git a/vendor/github.com/pion/turn/v4/internal/allocation/permission.go b/vendor/github.com/pion/turn/v5/internal/allocation/permission.go similarity index 73% rename from vendor/github.com/pion/turn/v4/internal/allocation/permission.go rename to vendor/github.com/pion/turn/v5/internal/allocation/permission.go index 7b02adc358..f2223d1a91 100644 --- a/vendor/github.com/pion/turn/v4/internal/allocation/permission.go +++ b/vendor/github.com/pion/turn/v5/internal/allocation/permission.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package allocation @@ -10,7 +10,7 @@ import ( "github.com/pion/logging" ) -const permissionTimeout = time.Duration(5) * time.Minute +const DefaultPermissionTimeout = time.Duration(5) * time.Minute // Permission represents a TURN permission. TURN permissions mimic the address-restricted // filtering mechanism of NATs that comply with [RFC4787]. @@ -18,15 +18,17 @@ const permissionTimeout = time.Duration(5) * time.Minute type Permission struct { Addr net.Addr allocation *Allocation + timeout time.Duration lifetimeTimer *time.Timer log logging.LeveledLogger } // NewPermission create a new Permission. -func NewPermission(addr net.Addr, log logging.LeveledLogger) *Permission { +func NewPermission(addr net.Addr, log logging.LeveledLogger, timeout time.Duration) *Permission { return &Permission{ - Addr: addr, - log: log, + Addr: addr, + log: log, + timeout: timeout, } } diff --git a/vendor/github.com/pion/turn/v5/internal/auth/auth.go b/vendor/github.com/pion/turn/v5/internal/auth/auth.go new file mode 100644 index 0000000000..d8a8b51a2e --- /dev/null +++ b/vendor/github.com/pion/turn/v5/internal/auth/auth.go @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2026 The Pion community +// SPDX-License-Identifier: MIT + +// Package auth provides internal authentication / authorization +// types and utilities for the TURN server. +package auth + +import ( + "crypto/tls" + "net" +) + +// RequestAttributes represents attributes of a TURN request which +// may be useful for authorizing the underlying request. +type RequestAttributes struct { + Username string + Realm string + SrcAddr net.Addr + TLS *tls.ConnectionState + + // extend as needed +} + +// AuthHandler is a callback used to handle incoming auth requests, +// allowing users to customize Pion TURN with custom behavior. +type AuthHandler func(ra *RequestAttributes) (userID string, key []byte, ok bool) diff --git a/vendor/github.com/pion/turn/v4/internal/client/allocation.go b/vendor/github.com/pion/turn/v5/internal/client/allocation.go similarity index 87% rename from vendor/github.com/pion/turn/v4/internal/client/allocation.go rename to vendor/github.com/pion/turn/v5/internal/client/allocation.go index 3fccb57af9..032721f190 100644 --- a/vendor/github.com/pion/turn/v4/internal/client/allocation.go +++ b/vendor/github.com/pion/turn/v5/internal/client/allocation.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package client @@ -13,21 +13,24 @@ import ( "github.com/pion/logging" "github.com/pion/stun/v3" "github.com/pion/transport/v4" - "github.com/pion/turn/v4/internal/proto" + "github.com/pion/turn/v5/internal/proto" ) // AllocationConfig is a set of configuration params use by NewUDPConn and NewTCPAllocation. type AllocationConfig struct { - Client Client - RelayedAddr net.Addr - ServerAddr net.Addr - Integrity stun.MessageIntegrity - Nonce stun.Nonce - Username stun.Username - Realm stun.Realm - Lifetime time.Duration - Net transport.Net - Log logging.LeveledLogger + Client Client + RelayedAddr net.Addr + ServerAddr net.Addr + Integrity stun.MessageIntegrity + Nonce stun.Nonce + Username stun.Username + Realm stun.Realm + Lifetime time.Duration + Net transport.Net + Log logging.LeveledLogger + PermissionRefreshInterval time.Duration + BindingRefreshInterval time.Duration + BindingCheckInterval time.Duration } type allocation struct { diff --git a/vendor/github.com/pion/turn/v4/internal/client/binding.go b/vendor/github.com/pion/turn/v5/internal/client/binding.go similarity index 98% rename from vendor/github.com/pion/turn/v4/internal/client/binding.go rename to vendor/github.com/pion/turn/v5/internal/client/binding.go index 0720d035a3..eefeb17079 100644 --- a/vendor/github.com/pion/turn/v4/internal/client/binding.go +++ b/vendor/github.com/pion/turn/v5/internal/client/binding.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package client diff --git a/vendor/github.com/pion/turn/v4/internal/client/client.go b/vendor/github.com/pion/turn/v5/internal/client/client.go similarity index 87% rename from vendor/github.com/pion/turn/v4/internal/client/client.go rename to vendor/github.com/pion/turn/v5/internal/client/client.go index 45041c9048..d1c95e1546 100644 --- a/vendor/github.com/pion/turn/v4/internal/client/client.go +++ b/vendor/github.com/pion/turn/v5/internal/client/client.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT // Package client implements the API for a TURN client diff --git a/vendor/github.com/pion/turn/v4/internal/client/errors.go b/vendor/github.com/pion/turn/v5/internal/client/errors.go similarity index 91% rename from vendor/github.com/pion/turn/v4/internal/client/errors.go rename to vendor/github.com/pion/turn/v5/internal/client/errors.go index ff40d2e8a2..6991e92383 100644 --- a/vendor/github.com/pion/turn/v4/internal/client/errors.go +++ b/vendor/github.com/pion/turn/v5/internal/client/errors.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package client @@ -22,6 +22,7 @@ var ( errFailedToGetLifetime = errors.New("failed to get lifetime from refresh response") errInvalidTURNAddress = errors.New("invalid TURN server address") errUnexpectedSTUNRequestMessage = errors.New("unexpected STUN request message") + errCannotBindChannel = errors.New("cannot bind channel") ) type timeoutError struct { diff --git a/vendor/github.com/pion/turn/v4/internal/client/periodic_timer.go b/vendor/github.com/pion/turn/v5/internal/client/periodic_timer.go similarity index 96% rename from vendor/github.com/pion/turn/v4/internal/client/periodic_timer.go rename to vendor/github.com/pion/turn/v5/internal/client/periodic_timer.go index 9e949129fa..170bb13c74 100644 --- a/vendor/github.com/pion/turn/v4/internal/client/periodic_timer.go +++ b/vendor/github.com/pion/turn/v5/internal/client/periodic_timer.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package client diff --git a/vendor/github.com/pion/turn/v4/internal/client/permission.go b/vendor/github.com/pion/turn/v5/internal/client/permission.go similarity index 93% rename from vendor/github.com/pion/turn/v4/internal/client/permission.go rename to vendor/github.com/pion/turn/v5/internal/client/permission.go index d6708d861f..00bb16c4af 100644 --- a/vendor/github.com/pion/turn/v4/internal/client/permission.go +++ b/vendor/github.com/pion/turn/v5/internal/client/permission.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package client @@ -8,7 +8,7 @@ import ( "sync" "sync/atomic" - "github.com/pion/turn/v4/internal/ipnet" + "github.com/pion/turn/v5/internal/ipnet" ) type permState int32 diff --git a/vendor/github.com/pion/turn/v4/internal/client/tcp_alloc.go b/vendor/github.com/pion/turn/v5/internal/client/tcp_alloc.go similarity index 91% rename from vendor/github.com/pion/turn/v4/internal/client/tcp_alloc.go rename to vendor/github.com/pion/turn/v5/internal/client/tcp_alloc.go index 8ada3cab1e..20904ac711 100644 --- a/vendor/github.com/pion/turn/v4/internal/client/tcp_alloc.go +++ b/vendor/github.com/pion/turn/v5/internal/client/tcp_alloc.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package client @@ -13,7 +13,7 @@ import ( "github.com/pion/stun/v3" "github.com/pion/transport/v4" - "github.com/pion/turn/v4/internal/proto" + "github.com/pion/turn/v5/internal/proto" ) var ( @@ -62,6 +62,11 @@ func NewTCPAllocation(config *AllocationConfig) *TCPAllocation { alloc.lifetime()/2, ) + permRefreshInterval := defaultPermRefreshInterval + if config.PermissionRefreshInterval != 0 { + permRefreshInterval = config.PermissionRefreshInterval + } + alloc.refreshPermsTimer = NewPeriodicTimer( timerIDRefreshPerms, alloc.onRefreshTimers, @@ -144,21 +149,35 @@ func (a *TCPAllocation) DialWithConn(conn net.Conn, network, rAddrStr string) (* return a.DialTCPWithConn(conn, network, rAddr) } -// DialTCP acts like Dial for TCP networks. -func (a *TCPAllocation) DialTCP(network string, lAddr, rAddr *net.TCPAddr) (*TCPConn, error) { - var rAddrServer *net.TCPAddr +// RFC 6156: +// "TURN can run over UDP and TCP, and it allows for a client to request +// address/port pairs for receiving both UDP and TCP." +// "This document adds IPv6 support to TURN, which includes IPv4-to-IPv6, +// IPv6-to-IPv6, and IPv6-to-IPv4 relaying.". +func (a *TCPAllocation) serverTCPAddr() (*net.TCPAddr, error) { if addr, ok := a.serverAddr.(*net.TCPAddr); ok { - rAddrServer = &net.TCPAddr{ + return &net.TCPAddr{ IP: addr.IP, Port: addr.Port, - } - } else if addr, ok := a.serverAddr.(*net.UDPAddr); ok { - rAddrServer = &net.TCPAddr{ + Zone: addr.Zone, + }, nil + } + if addr, ok := a.serverAddr.(*net.UDPAddr); ok { + return &net.TCPAddr{ IP: addr.IP, Port: addr.Port, - } - } else { - return nil, errInvalidTURNAddress + Zone: addr.Zone, + }, nil + } + + return nil, errInvalidTURNAddress +} + +// DialTCP acts like Dial for TCP networks. +func (a *TCPAllocation) DialTCP(network string, lAddr, rAddr *net.TCPAddr) (*TCPConn, error) { + rAddrServer, err := a.serverTCPAddr() + if err != nil { + return nil, err } conn, err := a.net.DialTCP(network, lAddr, rAddrServer) @@ -291,7 +310,7 @@ func (a *TCPAllocation) Accept() (net.Conn, error) { // AcceptTCP accepts the next incoming call and returns the new connection. func (a *TCPAllocation) AcceptTCP() (transport.TCPConn, error) { - addr, err := net.ResolveTCPAddr("tcp4", a.serverAddr.String()) + addr, err := a.serverTCPAddr() if err != nil { return nil, err } diff --git a/vendor/github.com/pion/turn/v4/internal/client/tcp_conn.go b/vendor/github.com/pion/turn/v5/internal/client/tcp_conn.go similarity index 91% rename from vendor/github.com/pion/turn/v4/internal/client/tcp_conn.go rename to vendor/github.com/pion/turn/v5/internal/client/tcp_conn.go index ae2985e931..2b759440e6 100644 --- a/vendor/github.com/pion/turn/v4/internal/client/tcp_conn.go +++ b/vendor/github.com/pion/turn/v5/internal/client/tcp_conn.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package client @@ -8,7 +8,7 @@ import ( "net" "github.com/pion/transport/v4" - "github.com/pion/turn/v4/internal/proto" + "github.com/pion/turn/v5/internal/proto" ) var ( diff --git a/vendor/github.com/pion/turn/v4/internal/client/transaction.go b/vendor/github.com/pion/turn/v5/internal/client/transaction.go similarity index 98% rename from vendor/github.com/pion/turn/v4/internal/client/transaction.go rename to vendor/github.com/pion/turn/v5/internal/client/transaction.go index 744df66892..38b9e1046f 100644 --- a/vendor/github.com/pion/turn/v4/internal/client/transaction.go +++ b/vendor/github.com/pion/turn/v5/internal/client/transaction.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package client diff --git a/vendor/github.com/pion/turn/v4/internal/client/trylock.go b/vendor/github.com/pion/turn/v5/internal/client/trylock.go similarity index 88% rename from vendor/github.com/pion/turn/v4/internal/client/trylock.go rename to vendor/github.com/pion/turn/v5/internal/client/trylock.go index 8380b0c8b2..93bf4d8742 100644 --- a/vendor/github.com/pion/turn/v4/internal/client/trylock.go +++ b/vendor/github.com/pion/turn/v5/internal/client/trylock.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package client diff --git a/vendor/github.com/pion/turn/v4/internal/client/udp_conn.go b/vendor/github.com/pion/turn/v5/internal/client/udp_conn.go similarity index 87% rename from vendor/github.com/pion/turn/v4/internal/client/udp_conn.go rename to vendor/github.com/pion/turn/v5/internal/client/udp_conn.go index 7f5e1d8a3d..a08f7cde05 100644 --- a/vendor/github.com/pion/turn/v4/internal/client/udp_conn.go +++ b/vendor/github.com/pion/turn/v5/internal/client/udp_conn.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT // Package client implements the API for a TURN client @@ -13,15 +13,15 @@ import ( "time" "github.com/pion/stun/v3" - "github.com/pion/turn/v4/internal/proto" + "github.com/pion/turn/v5/internal/proto" ) const ( - maxReadQueueSize = 1024 - permRefreshInterval = 120 * time.Second - bindingRefreshInterval = 5 * time.Minute - bindingCheckInterval = 30 * time.Second - maxRetryAttempts = 3 + maxReadQueueSize = 1024 + defaultPermRefreshInterval = 120 * time.Second + defaultBindingRefreshInterval = 5 * time.Minute + defaultBindingCheckInterval = 30 * time.Second + maxRetryAttempts = 3 ) const ( @@ -38,19 +38,21 @@ type inboundData struct { // UDPConn is the implementation of the Conn and PacketConn interfaces for UDP network connections. // compatible with net.PacketConn and net.Conn. type UDPConn struct { - bindingMgr *bindingManager // Thread-safe - checkBindingsTimer *PeriodicTimer // Thread-safe - readCh chan *inboundData // Thread-safe - closeCh chan struct{} // Thread-safe + bindingMgr *bindingManager // Thread-safe + checkBindingsTimer *PeriodicTimer // Thread-safe + readCh chan *inboundData // Thread-safe + closeCh chan struct{} // Thread-safe + bindingRefreshInterval time.Duration // Read-only allocation } // NewUDPConn creates a new instance of UDPConn. func NewUDPConn(config *AllocationConfig) *UDPConn { conn := &UDPConn{ - bindingMgr: newBindingManager(), - readCh: make(chan *inboundData, maxReadQueueSize), - closeCh: make(chan struct{}), + bindingMgr: newBindingManager(), + readCh: make(chan *inboundData, maxReadQueueSize), + closeCh: make(chan struct{}), + bindingRefreshInterval: defaultBindingRefreshInterval, allocation: allocation{ client: config.Client, relayedAddr: config.RelayedAddr, @@ -67,6 +69,10 @@ func NewUDPConn(config *AllocationConfig) *UDPConn { }, } + if config.BindingRefreshInterval != 0 { + conn.bindingRefreshInterval = config.BindingRefreshInterval + } + conn.log.Debugf("Initial lifetime: %d seconds", int(conn.lifetime().Seconds())) conn.refreshAllocTimer = NewPeriodicTimer( @@ -75,12 +81,22 @@ func NewUDPConn(config *AllocationConfig) *UDPConn { conn.lifetime()/2, ) + permRefreshInterval := defaultPermRefreshInterval + if config.PermissionRefreshInterval != 0 { + permRefreshInterval = config.PermissionRefreshInterval + } + conn.refreshPermsTimer = NewPeriodicTimer( timerIDRefreshPerms, conn.onRefreshTimers, permRefreshInterval, ) + bindingCheckInterval := defaultBindingCheckInterval + if config.BindingCheckInterval != 0 { + bindingCheckInterval = config.BindingCheckInterval + } + conn.checkBindingsTimer = NewPeriodicTimer( timerIDCheckBindings, func(timerID int) { @@ -222,7 +238,11 @@ func (c *UDPConn) WriteTo(payload []byte, addr net.Addr) (int, error) { //nolint return 0, err } - return c.client.WriteTo(msg.Raw, c.serverAddr) + if _, err = c.client.WriteTo(msg.Raw, c.serverAddr); err != nil { + return 0, err + } + + return len(payload), nil } // Binding is ready beyond this point, so send over it. @@ -421,7 +441,7 @@ func (c *UDPConn) maybeBind(bound *binding) { switch { case state == bindingStateIdle: bound.setState(bindingStateRequest) - case state == bindingStateReady && time.Since(bound.refreshedAt()) > bindingRefreshInterval: + case state == bindingStateReady && time.Since(bound.refreshedAt()) > c.bindingRefreshInterval: bound.setState(bindingStateRefresh) default: return @@ -452,8 +472,6 @@ func (c *UDPConn) bind(bound *binding) error { trRes, err := c.client.PerformTransaction(msg, c.serverAddr, false) if err != nil { - c.bindingMgr.deleteByAddr(bound.addr) - return err } @@ -466,8 +484,11 @@ func (c *UDPConn) bind(bound *binding) error { return errTryAgain } + + return fmt.Errorf("%w: received error %d", errCannotBindChannel, code.Code) // nolint:err113 } - return fmt.Errorf("unexpected response type %s", res.Type) //nolint // dynamic errors + + return fmt.Errorf("%w: unexpected response type %s", errCannotBindChannel, res.Type) // nolint:err113 } c.log.Debugf("Channel binding successful: %s %d", bound.addr, bound.number) diff --git a/vendor/github.com/pion/turn/v4/internal/ipnet/util.go b/vendor/github.com/pion/turn/v5/internal/ipnet/util.go similarity index 65% rename from vendor/github.com/pion/turn/v4/internal/ipnet/util.go rename to vendor/github.com/pion/turn/v5/internal/ipnet/util.go index 9753ef35cb..74df9f4075 100644 --- a/vendor/github.com/pion/turn/v4/internal/ipnet/util.go +++ b/vendor/github.com/pion/turn/v5/internal/ipnet/util.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT // Package ipnet contains helper functions around net and IP @@ -26,15 +26,26 @@ func AddrIPPort(a net.Addr) (net.IP, int, error) { return nil, 0, errFailedToCastAddr } -// AddrEqual asserts that two net.Addrs are equal -// Currently only supports UDP but will be extended in the future to support others. -func AddrEqual(a, b net.Addr) bool { - aUDP, ok := a.(*net.UDPAddr) +// AddrEqual asserts that two net.Addrs are equal. +// Compares IP and Port and intentionally ignores IPv6 Zone. +func AddrEqual(addrA, addrB net.Addr) bool { + aUDP, ok := addrA.(*net.UDPAddr) if !ok { - return false + var aTCP, bTCP *net.TCPAddr + aTCP, ok = addrA.(*net.TCPAddr) + if !ok { + return false + } + + bTCP, ok = addrB.(*net.TCPAddr) + if !ok { + return false + } + + return aTCP.IP.Equal(bTCP.IP) && aTCP.Port == bTCP.Port } - bUDP, ok := b.(*net.UDPAddr) + bUDP, ok := addrB.(*net.UDPAddr) if !ok { return false } diff --git a/vendor/github.com/pion/turn/v4/internal/proto/addr.go b/vendor/github.com/pion/turn/v5/internal/proto/addr.go similarity index 88% rename from vendor/github.com/pion/turn/v4/internal/proto/addr.go rename to vendor/github.com/pion/turn/v5/internal/proto/addr.go index 38b3027fca..24ca3d795f 100644 --- a/vendor/github.com/pion/turn/v4/internal/proto/addr.go +++ b/vendor/github.com/pion/turn/v5/internal/proto/addr.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package proto @@ -6,6 +6,7 @@ package proto import ( "fmt" "net" + "strconv" ) // Addr is ip:port. @@ -38,7 +39,7 @@ func (a Addr) EqualIP(b Addr) bool { } func (a Addr) String() string { - return fmt.Sprintf("%s:%d", a.IP, a.Port) + return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port)) } // FiveTuple represents 5-TUPLE value. diff --git a/vendor/github.com/pion/turn/v4/internal/proto/chandata.go b/vendor/github.com/pion/turn/v5/internal/proto/chandata.go similarity index 98% rename from vendor/github.com/pion/turn/v4/internal/proto/chandata.go rename to vendor/github.com/pion/turn/v5/internal/proto/chandata.go index 4c9bb26203..89ea732aab 100644 --- a/vendor/github.com/pion/turn/v4/internal/proto/chandata.go +++ b/vendor/github.com/pion/turn/v5/internal/proto/chandata.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package proto diff --git a/vendor/github.com/pion/turn/v4/internal/proto/chann.go b/vendor/github.com/pion/turn/v5/internal/proto/chann.go similarity index 96% rename from vendor/github.com/pion/turn/v4/internal/proto/chann.go rename to vendor/github.com/pion/turn/v5/internal/proto/chann.go index da017bf658..c6f9de6a8a 100644 --- a/vendor/github.com/pion/turn/v4/internal/proto/chann.go +++ b/vendor/github.com/pion/turn/v5/internal/proto/chann.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package proto diff --git a/vendor/github.com/pion/turn/v4/internal/proto/connection_id.go b/vendor/github.com/pion/turn/v5/internal/proto/connection_id.go similarity index 93% rename from vendor/github.com/pion/turn/v4/internal/proto/connection_id.go rename to vendor/github.com/pion/turn/v5/internal/proto/connection_id.go index d95d9b0456..f14122a12b 100644 --- a/vendor/github.com/pion/turn/v4/internal/proto/connection_id.go +++ b/vendor/github.com/pion/turn/v5/internal/proto/connection_id.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package proto diff --git a/vendor/github.com/pion/turn/v4/internal/proto/data.go b/vendor/github.com/pion/turn/v5/internal/proto/data.go similarity index 92% rename from vendor/github.com/pion/turn/v4/internal/proto/data.go rename to vendor/github.com/pion/turn/v5/internal/proto/data.go index ea2f0c8685..d2295958ad 100644 --- a/vendor/github.com/pion/turn/v4/internal/proto/data.go +++ b/vendor/github.com/pion/turn/v5/internal/proto/data.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package proto diff --git a/vendor/github.com/pion/turn/v4/internal/proto/dontfrag.go b/vendor/github.com/pion/turn/v5/internal/proto/dontfrag.go similarity index 94% rename from vendor/github.com/pion/turn/v4/internal/proto/dontfrag.go rename to vendor/github.com/pion/turn/v5/internal/proto/dontfrag.go index d46ae8f144..eb576b220b 100644 --- a/vendor/github.com/pion/turn/v4/internal/proto/dontfrag.go +++ b/vendor/github.com/pion/turn/v5/internal/proto/dontfrag.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package proto @@ -8,6 +8,7 @@ import ( ) // DontFragmentAttr is a deprecated alias for DontFragment +// // Deprecated: Please use DontFragment. type DontFragmentAttr = DontFragment diff --git a/vendor/github.com/pion/turn/v4/internal/proto/evenport.go b/vendor/github.com/pion/turn/v5/internal/proto/evenport.go similarity index 95% rename from vendor/github.com/pion/turn/v4/internal/proto/evenport.go rename to vendor/github.com/pion/turn/v5/internal/proto/evenport.go index 6989ab8945..84b7ee9672 100644 --- a/vendor/github.com/pion/turn/v4/internal/proto/evenport.go +++ b/vendor/github.com/pion/turn/v5/internal/proto/evenport.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package proto diff --git a/vendor/github.com/pion/turn/v4/internal/proto/lifetime.go b/vendor/github.com/pion/turn/v5/internal/proto/lifetime.go similarity index 95% rename from vendor/github.com/pion/turn/v4/internal/proto/lifetime.go rename to vendor/github.com/pion/turn/v5/internal/proto/lifetime.go index 37b86f81a6..4e062e3b8e 100644 --- a/vendor/github.com/pion/turn/v4/internal/proto/lifetime.go +++ b/vendor/github.com/pion/turn/v5/internal/proto/lifetime.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package proto diff --git a/vendor/github.com/pion/turn/v4/internal/proto/peeraddr.go b/vendor/github.com/pion/turn/v5/internal/proto/peeraddr.go similarity index 94% rename from vendor/github.com/pion/turn/v4/internal/proto/peeraddr.go rename to vendor/github.com/pion/turn/v5/internal/proto/peeraddr.go index a919bd15a5..ce870c22fb 100644 --- a/vendor/github.com/pion/turn/v4/internal/proto/peeraddr.go +++ b/vendor/github.com/pion/turn/v5/internal/proto/peeraddr.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package proto diff --git a/vendor/github.com/pion/turn/v4/internal/proto/proto.go b/vendor/github.com/pion/turn/v5/internal/proto/proto.go similarity index 94% rename from vendor/github.com/pion/turn/v4/internal/proto/proto.go rename to vendor/github.com/pion/turn/v5/internal/proto/proto.go index 170cf7f188..8ac8a1fdc1 100644 --- a/vendor/github.com/pion/turn/v4/internal/proto/proto.go +++ b/vendor/github.com/pion/turn/v5/internal/proto/proto.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT // Package proto implements RFC 5766 Traversal Using Relays around NAT. diff --git a/vendor/github.com/pion/turn/v4/internal/proto/relayedaddr.go b/vendor/github.com/pion/turn/v5/internal/proto/relayedaddr.go similarity index 94% rename from vendor/github.com/pion/turn/v4/internal/proto/relayedaddr.go rename to vendor/github.com/pion/turn/v5/internal/proto/relayedaddr.go index 1d22ade9e3..e3b20c2e82 100644 --- a/vendor/github.com/pion/turn/v4/internal/proto/relayedaddr.go +++ b/vendor/github.com/pion/turn/v5/internal/proto/relayedaddr.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package proto diff --git a/vendor/github.com/pion/turn/v4/internal/proto/reqfamily.go b/vendor/github.com/pion/turn/v5/internal/proto/reqfamily.go similarity index 96% rename from vendor/github.com/pion/turn/v4/internal/proto/reqfamily.go rename to vendor/github.com/pion/turn/v5/internal/proto/reqfamily.go index d00c302fce..15a6a70042 100644 --- a/vendor/github.com/pion/turn/v4/internal/proto/reqfamily.go +++ b/vendor/github.com/pion/turn/v5/internal/proto/reqfamily.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package proto diff --git a/vendor/github.com/pion/turn/v4/internal/proto/reqtrans.go b/vendor/github.com/pion/turn/v5/internal/proto/reqtrans.go similarity index 96% rename from vendor/github.com/pion/turn/v4/internal/proto/reqtrans.go rename to vendor/github.com/pion/turn/v5/internal/proto/reqtrans.go index b907e3846c..5ae9fa6573 100644 --- a/vendor/github.com/pion/turn/v4/internal/proto/reqtrans.go +++ b/vendor/github.com/pion/turn/v5/internal/proto/reqtrans.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package proto diff --git a/vendor/github.com/pion/turn/v4/internal/proto/rsrvtoken.go b/vendor/github.com/pion/turn/v5/internal/proto/rsrvtoken.go similarity index 95% rename from vendor/github.com/pion/turn/v4/internal/proto/rsrvtoken.go rename to vendor/github.com/pion/turn/v5/internal/proto/rsrvtoken.go index aed38861ab..2644019c57 100644 --- a/vendor/github.com/pion/turn/v4/internal/proto/rsrvtoken.go +++ b/vendor/github.com/pion/turn/v5/internal/proto/rsrvtoken.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package proto diff --git a/vendor/github.com/pion/turn/v4/stun_conn.go b/vendor/github.com/pion/turn/v5/internal/proto/stun_conn.go similarity index 89% rename from vendor/github.com/pion/turn/v4/stun_conn.go rename to vendor/github.com/pion/turn/v5/internal/proto/stun_conn.go index fd1f1ae6e4..b449402e8a 100644 --- a/vendor/github.com/pion/turn/v4/stun_conn.go +++ b/vendor/github.com/pion/turn/v5/internal/proto/stun_conn.go @@ -1,7 +1,7 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT -package turn +package proto import ( "encoding/binary" @@ -10,7 +10,6 @@ import ( "time" "github.com/pion/stun/v3" - "github.com/pion/turn/v4/internal/proto" ) var ( @@ -27,12 +26,8 @@ type STUNConn struct { } const ( - stunHeaderSize = 20 - - channelDataLengthSize = 2 - channelDataNumberSize = channelDataLengthSize - channelDataHeaderSize = channelDataLengthSize + channelDataNumberSize - channelDataPadding = 4 + stunHeaderSize = 20 + channelDataPadding = 4 ) // Given a buffer give the last offset of the TURN frame @@ -48,7 +43,7 @@ func consumeSingleTURNFrame(b []byte) (int, error) { switch { case stun.IsMessage(b): datagramSize = binary.BigEndian.Uint16(b[2:4]) + stunHeaderSize - case proto.ChannelNumber(binary.BigEndian.Uint16(b[0:2])).Valid(): + case ChannelNumber(binary.BigEndian.Uint16(b[0:2])).Valid(): datagramSize = binary.BigEndian.Uint16(b[channelDataNumberSize:channelDataHeaderSize]) if paddingOverflow := (datagramSize + channelDataPadding) % channelDataPadding; paddingOverflow != 0 { datagramSize = (datagramSize + channelDataPadding) - paddingOverflow @@ -122,6 +117,11 @@ func (s *STUNConn) SetWriteDeadline(t time.Time) error { return s.nextConn.SetWriteDeadline(t) } +// Conn returns the net.Conn used for this STUNConn. +func (s *STUNConn) Conn() net.Conn { + return s.nextConn +} + // NewSTUNConn creates a STUNConn. func NewSTUNConn(nextConn net.Conn) *STUNConn { return &STUNConn{nextConn: nextConn} diff --git a/vendor/github.com/pion/turn/v4/internal/server/base36.go b/vendor/github.com/pion/turn/v5/internal/server/base36.go similarity index 95% rename from vendor/github.com/pion/turn/v4/internal/server/base36.go rename to vendor/github.com/pion/turn/v5/internal/server/base36.go index ea1121a8a5..2adb7d8efc 100644 --- a/vendor/github.com/pion/turn/v4/internal/server/base36.go +++ b/vendor/github.com/pion/turn/v5/internal/server/base36.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package server diff --git a/vendor/github.com/pion/turn/v5/internal/server/errors.go b/vendor/github.com/pion/turn/v5/internal/server/errors.go new file mode 100644 index 0000000000..33b31146c9 --- /dev/null +++ b/vendor/github.com/pion/turn/v5/internal/server/errors.go @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2026 The Pion community +// SPDX-License-Identifier: MIT + +package server + +import "errors" + +var ( + errFailedToGenerateNonce = errors.New("failed to generate nonce") + errInvalidNonce = errors.New("invalid nonce") + errFailedToSendError = errors.New("failed to send error message") + errNoSuchUser = errors.New("no such user exists") + errUnexpectedClass = errors.New("unexpected class") + errUnexpectedMethod = errors.New("unexpected method") + errFailedToHandle = errors.New("failed to handle") + errUnhandledSTUNPacket = errors.New("unhandled STUN packet") + errUnableToHandleChannelData = errors.New("unable to handle ChannelData") + errFailedToCreateSTUNPacket = errors.New("failed to create stun message from packet") + errFailedToCreateChannelData = errors.New("failed to create channel data from packet") + errRelayAlreadyAllocatedForFiveTuple = errors.New("relay already allocated for 5-TUPLE") + errUnsupportedTransportProtocol = errors.New("RequestedTransport must be UDP or TCP") + errNoDontFragmentSupport = errors.New("no support for DONT-FRAGMENT") + errRequestWithReservationTokenAndEvenPort = errors.New("Request must not contain RESERVATION-TOKEN and EVEN-PORT") //nolint:lll + errRequestWithReservationTokenAndRequestedFamily = errors.New("Request must not contain RESERVATION-TOKEN and REQUESTED-ADDRESS-FAMILY") //nolint:lll + errUnsupportedAddressFamily = errors.New("unsupported address family") + errPeerAddressFamilyMismatch = errors.New("peer address family does not match allocation") + errNoAllocationFound = errors.New("no allocation found") + errNoPermission = errors.New("unable to handle send-indication, no permission added") + errShortWrite = errors.New("packet write smaller than packet") + errNoSuchChannelBind = errors.New("no such channel bind") + errFailedWriteSocket = errors.New("failed writing to socket") +) diff --git a/vendor/github.com/pion/turn/v4/internal/server/nonce.go b/vendor/github.com/pion/turn/v5/internal/server/nonce.go similarity index 96% rename from vendor/github.com/pion/turn/v4/internal/server/nonce.go rename to vendor/github.com/pion/turn/v5/internal/server/nonce.go index 3b432a77a8..d5566afa34 100644 --- a/vendor/github.com/pion/turn/v4/internal/server/nonce.go +++ b/vendor/github.com/pion/turn/v5/internal/server/nonce.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package server diff --git a/vendor/github.com/pion/turn/v4/internal/server/server.go b/vendor/github.com/pion/turn/v5/internal/server/server.go similarity index 71% rename from vendor/github.com/pion/turn/v4/internal/server/server.go rename to vendor/github.com/pion/turn/v5/internal/server/server.go index 1bbfb0cae6..48ff17960d 100644 --- a/vendor/github.com/pion/turn/v4/internal/server/server.go +++ b/vendor/github.com/pion/turn/v5/internal/server/server.go @@ -1,18 +1,20 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT // Package server implements the private API to implement a TURN server package server import ( + "crypto/tls" "fmt" "net" "time" "github.com/pion/logging" "github.com/pion/stun/v3" - "github.com/pion/turn/v4/internal/allocation" - "github.com/pion/turn/v4/internal/proto" + "github.com/pion/turn/v5/internal/allocation" + "github.com/pion/turn/v5/internal/auth" + "github.com/pion/turn/v5/internal/proto" ) // Request contains all the state needed to process a single incoming datagram. @@ -21,20 +23,24 @@ type Request struct { Conn net.PacketConn SrcAddr net.Addr Buff []byte + TLS *tls.ConnectionState // Server State AllocationManager *allocation.Manager NonceHash NonceManager // User Configuration - AuthHandler func(username string, realm string, srcAddr net.Addr) (key []byte, ok bool) + AuthHandler auth.AuthHandler // Quota Handler QuotaHandler func(username string, realm string, srcAddr net.Addr) (ok bool) - Log logging.LeveledLogger - Realm string + Log logging.LeveledLogger + Realm string + ChannelBindTimeout time.Duration + PermissionTimeout time.Duration + AllocationLifetime time.Duration } // HandleRequest processes the give Request. @@ -84,8 +90,31 @@ func handleTURNPacket(req Request) error { ) } - err = handler(req, stunMsg) - if err != nil { + unknownAttributes := []stun.AttrType(nil) + for _, attr := range stunMsg.Attributes { + if attr.Type.Required() && !attr.Type.Known() { + unknownAttributes = append(unknownAttributes, attr.Type) + } + } + + if len(unknownAttributes) != 0 { + req.Log.Errorf( + "%v-%v with unknown comprehension-required attributes from %v: %v", + stunMsg.Type.Method, + stunMsg.Type.Class, + req.SrcAddr, + unknownAttributes, + ) + + return buildAndSend(req.Conn, req.SrcAddr, buildMsg( + stunMsg.TransactionID, + stun.NewType(stunMsg.Type.Method, stun.ClassErrorResponse), + stun.CodeUnknownAttribute, + stun.UnknownAttributes(unknownAttributes), + )...) + } + + if err = handler(req, stunMsg); err != nil { // nolint:errorlint return fmt.Errorf( "%w %v-%v from %v: %v", @@ -125,6 +154,10 @@ func getMessageHandler(class stun.MessageClass, method stun.Method) ( // nolint: return handleChannelBindRequest, nil case stun.MethodBinding: return handleBindingRequest, nil + case stun.MethodConnect: + return handleConnectRequest, nil + case stun.MethodConnectionBind: + return handleConnectionBindRequest, nil default: return nil, fmt.Errorf("%w: %s", errUnexpectedMethod, method) } diff --git a/vendor/github.com/pion/turn/v4/internal/server/short_nonce.go b/vendor/github.com/pion/turn/v5/internal/server/short_nonce.go similarity index 98% rename from vendor/github.com/pion/turn/v4/internal/server/short_nonce.go rename to vendor/github.com/pion/turn/v5/internal/server/short_nonce.go index fc8b28caa8..78aaad13a8 100644 --- a/vendor/github.com/pion/turn/v4/internal/server/short_nonce.go +++ b/vendor/github.com/pion/turn/v5/internal/server/short_nonce.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package server diff --git a/vendor/github.com/pion/turn/v4/internal/server/stun.go b/vendor/github.com/pion/turn/v5/internal/server/stun.go similarity index 82% rename from vendor/github.com/pion/turn/v4/internal/server/stun.go rename to vendor/github.com/pion/turn/v5/internal/server/stun.go index 1880bf177f..5433539fcd 100644 --- a/vendor/github.com/pion/turn/v4/internal/server/stun.go +++ b/vendor/github.com/pion/turn/v5/internal/server/stun.go @@ -1,11 +1,11 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package server import ( "github.com/pion/stun/v3" - "github.com/pion/turn/v4/internal/ipnet" + "github.com/pion/turn/v5/internal/ipnet" ) func handleBindingRequest(req Request, stunMsg *stun.Message) error { diff --git a/vendor/github.com/pion/turn/v4/internal/server/turn.go b/vendor/github.com/pion/turn/v5/internal/server/turn.go similarity index 51% rename from vendor/github.com/pion/turn/v4/internal/server/turn.go rename to vendor/github.com/pion/turn/v5/internal/server/turn.go index 60dd3a6a9a..ef81c2b0d2 100644 --- a/vendor/github.com/pion/turn/v4/internal/server/turn.go +++ b/vendor/github.com/pion/turn/v5/internal/server/turn.go @@ -1,24 +1,28 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package server import ( + "context" + "errors" "fmt" + "io" "net" + "strconv" "github.com/pion/randutil" "github.com/pion/stun/v3" - "github.com/pion/turn/v4/internal/allocation" - "github.com/pion/turn/v4/internal/ipnet" - "github.com/pion/turn/v4/internal/proto" + "github.com/pion/turn/v5/internal/allocation" + "github.com/pion/turn/v5/internal/ipnet" + "github.com/pion/turn/v5/internal/proto" ) const runesAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" // See: https://tools.ietf.org/html/rfc5766#section-6.2 // . -func handleAllocateRequest(req Request, stunMsg *stun.Message) error { //nolint:cyclop +func handleAllocateRequest(req Request, stunMsg *stun.Message) error { //nolint:cyclop,gocyclo,maintidx,gocognit req.Log.Debugf("Received AllocateRequest from %s", req.SrcAddr) // 1. The server MUST require that the request be authenticated. This @@ -26,7 +30,7 @@ func handleAllocateRequest(req Request, stunMsg *stun.Message) error { //nolint: // mechanism of [https://tools.ietf.org/html/rfc5389#section-10.2.2] // unless the client and server agree to use another mechanism through // some procedure outside the scope of this document. - messageIntegrity, hasAuth, err := authenticateRequest(req, stunMsg, stun.MethodAllocate) + messageIntegrity, hasAuth, userID, err := authenticateRequest(req, stunMsg, stun.MethodAllocate) if !hasAuth { return err } @@ -123,6 +127,12 @@ func handleAllocateRequest(req Request, stunMsg *stun.Message) error { //nolint: if err = evenPort.GetFrom(stunMsg); err == nil { return buildAndSendErr(req.Conn, req.SrcAddr, errRequestWithReservationTokenAndEvenPort, badRequestMsg...) } + + allocationPort, reservationFound := req.AllocationManager.GetReservation(string(reservationTokenAttr)) + if !reservationFound { + return buildAndSendErr(req.Conn, req.SrcAddr, errNoAllocationFound, insufficientCapacityMsg...) + } + requestedPort = allocationPort + 1 } // 6. The server checks if the request contains an EVEN-PORT attribute. @@ -145,11 +155,54 @@ func handleAllocateRequest(req Request, stunMsg *stun.Message) error { //nolint: } } - // Parse realm and username (already checked in authenticateRequest) + // Parse realm (already checked in authenticateRequest) realmAttr := &stun.Realm{} _ = realmAttr.GetFrom(stunMsg) - usernameAttr := &stun.Username{} - _ = usernameAttr.GetFrom(stunMsg) + + // RFC 6156: Parse REQUESTED-ADDRESS-FAMILY attribute if present. + // If absent, default to IPv4 per RFC 6156 Section 4.1.1. + // "If the REQUESTED-ADDRESS-FAMILY attribute is absent, the server MUST + // allocate an IPv4-relayed transport address for the TURN client." + // "Length: this 16-bit field contains the length of the attribute in + // bytes. The length of this attribute is 4 bytes." + // "If the server does not support the address family requested by the + // client, it MUST generate an Allocate error response, and it MUST + // include an ERROR-CODE attribute with the 440 (Address Family not + // Supported) response code, which is defined in Section 4.2.1." + var requestedFamily proto.RequestedAddressFamily + if err = requestedFamily.GetFrom(stunMsg); err != nil { + switch { + case errors.Is(err, stun.ErrAttributeNotFound): + requestedFamily = proto.RequestedFamilyIPv4 + case stun.IsAttrSizeInvalid(err) || stun.IsAttrSizeOverflow(err): + return buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) + default: + msg := buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodeAddrFamilyNotSupported}, + ) + + return buildAndSendErr(req.Conn, req.SrcAddr, errUnsupportedAddressFamily, msg...) + } + } + + // RFC 6156: Check if the requested address family is supported. + // If not, reject with 440 (Address Family not Supported) error. + if requestedFamily != proto.RequestedFamilyIPv4 && requestedFamily != proto.RequestedFamilyIPv6 { + msg := buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodeAddrFamilyNotSupported}, + ) + + return buildAndSendErr(req.Conn, req.SrcAddr, errUnsupportedAddressFamily, msg...) + } + + // RFC 6156: REQUESTED-ADDRESS-FAMILY and RESERVATION-TOKEN are mutually exclusive. + if stunMsg.Contains(stun.AttrReservationToken) && stunMsg.Contains(stun.AttrRequestedAddressFamily) { + return buildAndSendErr(req.Conn, req.SrcAddr, errRequestWithReservationTokenAndRequestedFamily, badRequestMsg...) + } // 7. At any point, the server MAY choose to reject the request with a // 486 (Allocation Quota Reached) error if it feels the client is @@ -157,7 +210,7 @@ func handleAllocateRequest(req Request, stunMsg *stun.Message) error { //nolint: // server is free to define this allocation quota any way it wishes, // but SHOULD define it based on the username used to authenticate // the request, and not on the client's transport address. - if req.QuotaHandler != nil && !req.QuotaHandler(usernameAttr.String(), realmAttr.String(), req.SrcAddr) { + if req.QuotaHandler != nil && !req.QuotaHandler(userID, realmAttr.String(), req.SrcAddr) { quotaReachedMsg := buildMsg(stunMsg.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeAllocQuotaReached}) @@ -169,14 +222,16 @@ func handleAllocateRequest(req Request, stunMsg *stun.Message) error { //nolint: // with a 300 (Try Alternate) error if it wishes to redirect the // client to a different server. The use of this error code and // attribute follow the specification in [RFC5389]. - lifetimeDuration := allocationLifeTime(stunMsg) + lifetimeDuration := allocationLifeTime(req, stunMsg) alloc, err := req.AllocationManager.CreateAllocation( fiveTuple, req.Conn, + requestedTransport.Protocol, requestedPort, lifetimeDuration, - usernameAttr.String(), + userID, realmAttr.String(), + requestedFamily, ) if err != nil { return buildAndSendErr(req.Conn, req.SrcAddr, err, insufficientCapacityMsg...) @@ -236,24 +291,62 @@ func handleAllocateRequest(req Request, stunMsg *stun.Message) error { //nolint: func handleRefreshRequest(req Request, stunMsg *stun.Message) error { req.Log.Debugf("Received RefreshRequest from %s", req.SrcAddr) - messageIntegrity, hasAuth, err := authenticateRequest(req, stunMsg, stun.MethodRefresh) + messageIntegrity, hasAuth, userID, err := authenticateRequest(req, stunMsg, stun.MethodRefresh) if !hasAuth { return err } - lifetimeDuration := allocationLifeTime(stunMsg) + lifetimeDuration := allocationLifeTime(req, stunMsg) fiveTuple := &allocation.FiveTuple{ SrcAddr: req.SrcAddr, DstAddr: req.Conn.LocalAddr(), Protocol: allocation.UDP, } - if lifetimeDuration != 0 { - a := req.AllocationManager.GetAllocation(fiveTuple) + a := req.AllocationManager.GetAllocationForUserID(fiveTuple, userID) //nolint:varnamelen + if a == nil { + return fmt.Errorf("%w %v:%v", errNoAllocationFound, req.SrcAddr, req.Conn.LocalAddr()) + } + + // RFC 6156: If REQUESTED-ADDRESS-FAMILY is present in Refresh request, + // it must match the allocation's address family. + // "The client MUST NOT include any REQUESTED-ADDRESS-FAMILY attribute in + // its Refresh Request." + // "If a server receives a Refresh Request with a REQUESTED-ADDRESS- + // FAMILY attribute, and the attribute's value doesn't match the address + // family of the allocation, the server MUST reply with a 443 (Peer + // Address Family Mismatch) Refresh error response." + var requestedFamily proto.RequestedAddressFamily + badRequestMsg := buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodRefresh, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest}, + ) + if err = requestedFamily.GetFrom(stunMsg); err != nil { + switch { + case errors.Is(err, stun.ErrAttributeNotFound): + case stun.IsAttrSizeInvalid(err) || stun.IsAttrSizeOverflow(err): + return buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) + default: + msg := buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodRefresh, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodePeerAddrFamilyMismatch}, + ) - if a == nil { - return fmt.Errorf("%w %v:%v", errNoAllocationFound, req.SrcAddr, req.Conn.LocalAddr()) + return buildAndSendErr(req.Conn, req.SrcAddr, errPeerAddressFamilyMismatch, msg...) } + } else if requestedFamily != a.AddressFamily() { + msg := buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodRefresh, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodePeerAddrFamilyMismatch}, + ) + + return buildAndSendErr(req.Conn, req.SrcAddr, errPeerAddressFamilyMismatch, msg...) + } + + if lifetimeDuration != 0 { a.Refresh(lifetimeDuration) } else { req.AllocationManager.DeleteAllocation(fiveTuple) @@ -278,20 +371,20 @@ func handleRefreshRequest(req Request, stunMsg *stun.Message) error { func handleCreatePermissionRequest(req Request, stunMsg *stun.Message) error { req.Log.Debugf("Received CreatePermission from %s", req.SrcAddr) - alloc := req.AllocationManager.GetAllocation(&allocation.FiveTuple{ + messageIntegrity, hasAuth, userID, err := authenticateRequest(req, stunMsg, stun.MethodCreatePermission) + if !hasAuth { + return err + } + + alloc := req.AllocationManager.GetAllocationForUserID(&allocation.FiveTuple{ SrcAddr: req.SrcAddr, DstAddr: req.Conn.LocalAddr(), Protocol: allocation.UDP, - }) + }, userID) if alloc == nil { return fmt.Errorf("%w %v:%v", errNoAllocationFound, req.SrcAddr, req.Conn.LocalAddr()) } - messageIntegrity, hasAuth, err := authenticateRequest(req, stunMsg, stun.MethodCreatePermission) - if !hasAuth { - return err - } - addCount := 0 if err := stunMsg.ForEach(stun.AttrXORPeerAddress, func(m *stun.Message) error { @@ -300,14 +393,21 @@ func handleCreatePermissionRequest(req Request, stunMsg *stun.Message) error { return err } + // RFC 6156: Peer address must match allocation's address family. + if !ipMatchesFamily(peerAddress.IP, alloc.AddressFamily()) { + req.Log.Infof("peer address family mismatch for client %s to peer %s", req.SrcAddr, peerAddress.IP) + + return errPeerAddressFamilyMismatch + } + if err := req.AllocationManager.GrantPermission(req.SrcAddr, peerAddress.IP); err != nil { req.Log.Infof("permission denied for client %s to peer %s", req.SrcAddr, peerAddress.IP) return err } - req.Log.Debugf("Adding permission for %s", fmt.Sprintf("%s:%d", - peerAddress.IP, peerAddress.Port)) + req.Log.Debugf("Adding permission for %s", net.JoinHostPort( + peerAddress.IP.String(), strconv.Itoa(peerAddress.Port))) alloc.AddPermission(allocation.NewPermission( &net.UDPAddr{ @@ -315,6 +415,7 @@ func handleCreatePermissionRequest(req Request, stunMsg *stun.Message) error { Port: peerAddress.Port, }, req.Log, + req.PermissionTimeout, )) addCount++ @@ -362,7 +463,7 @@ func handleSendIndication(req Request, stunMsg *stun.Message) error { return fmt.Errorf("%w: %v", errNoPermission, msgDst) } - l, err := alloc.RelaySocket.WriteTo(dataAttr, msgDst) + l, err := alloc.WriteTo(dataAttr, msgDst) if err != nil { return fmt.Errorf("%w: %s", errFailedWriteSocket, err.Error()) } else if l != len(dataAttr) { @@ -372,29 +473,29 @@ func handleSendIndication(req Request, stunMsg *stun.Message) error { return err } -func handleChannelBindRequest(req Request, stunMsg *stun.Message) error { +func handleChannelBindRequest(req Request, stunMsg *stun.Message) error { // nolint:cyclop req.Log.Debugf("Received ChannelBindRequest from %s", req.SrcAddr) - alloc := req.AllocationManager.GetAllocation(&allocation.FiveTuple{ - SrcAddr: req.SrcAddr, - DstAddr: req.Conn.LocalAddr(), - Protocol: allocation.UDP, - }) - if alloc == nil { - return fmt.Errorf("%w %v:%v", errNoAllocationFound, req.SrcAddr, req.Conn.LocalAddr()) - } - badRequestMsg := buildMsg( stunMsg.TransactionID, stun.NewType(stun.MethodChannelBind, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest}, ) - messageIntegrity, hasAuth, err := authenticateRequest(req, stunMsg, stun.MethodChannelBind) + messageIntegrity, hasAuth, userID, err := authenticateRequest(req, stunMsg, stun.MethodChannelBind) if !hasAuth { return err } + alloc := req.AllocationManager.GetAllocationForUserID(&allocation.FiveTuple{ + SrcAddr: req.SrcAddr, + DstAddr: req.Conn.LocalAddr(), + Protocol: allocation.UDP, + }, userID) + if alloc == nil { + return fmt.Errorf("%w %v:%v", errNoAllocationFound, req.SrcAddr, req.Conn.LocalAddr()) + } + var channel proto.ChannelNumber if err = channel.GetFrom(stunMsg); err != nil { return buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) @@ -405,6 +506,17 @@ func handleChannelBindRequest(req Request, stunMsg *stun.Message) error { return buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) } + // RFC 6156: Peer address must match allocation's address family. + if !ipMatchesFamily(peerAddr.IP, alloc.AddressFamily()) { + req.Log.Infof("peer address family mismatch for client %s to peer %s", req.SrcAddr, peerAddr.IP) + + peerAddrFamilyMismatchMsg := buildMsg(stunMsg.TransactionID, + stun.NewType(stun.MethodChannelBind, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodePeerAddrFamilyMismatch}) + + return buildAndSendErr(req.Conn, req.SrcAddr, errPeerAddressFamilyMismatch, peerAddrFamilyMismatchMsg...) + } + if err = req.AllocationManager.GrantPermission(req.SrcAddr, peerAddr.IP); err != nil { req.Log.Infof("permission denied for client %s to peer %s", req.SrcAddr, peerAddr.IP) @@ -415,13 +527,40 @@ func handleChannelBindRequest(req Request, stunMsg *stun.Message) error { return buildAndSendErr(req.Conn, req.SrcAddr, err, unauthorizedRequestMsg...) } + peerNetAddr := &net.UDPAddr{IP: peerAddr.IP, Port: peerAddr.Port} req.Log.Debugf("Binding channel %d to %s", channel, peerAddr) err = alloc.AddChannelBind(allocation.NewChannelBind( channel, - &net.UDPAddr{IP: peerAddr.IP, Port: peerAddr.Port}, + peerNetAddr, req.Log, - ), req.ChannelBindTimeout) - if err != nil { + ), req.ChannelBindTimeout, req.PermissionTimeout) + if err != nil { // nolint:nestif + if errors.Is(err, allocation.ErrSamePeerDifferentChannel) { + if existing := alloc.GetChannelByAddr(peerNetAddr); existing != nil { + req.Log.Warnf( + "ChannelBind rejected: peer %s already bound to channel %d, attempted %d", + peerNetAddr.String(), uint16(existing.Number), uint16(channel), + ) + } else { + req.Log.Warnf( + "ChannelBind rejected: peer %s attempted to rebind to channel %d", + peerNetAddr.String(), uint16(channel), + ) + } + } else if errors.Is(err, allocation.ErrSameChannelDifferentPeer) { + if existing := alloc.GetChannelByNumber(channel); existing != nil { + req.Log.Warnf( + "ChannelBind rejected: channel %d already bound to peer %s, attempted peer %s", + uint16(channel), existing.Peer.String(), peerNetAddr.String(), + ) + } else { + req.Log.Warnf( + "ChannelBind rejected: channel %d attempted to bind to peer %s", + uint16(channel), peerNetAddr.String(), + ) + } + } + return buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) } @@ -450,7 +589,7 @@ func handleChannelData(req Request, channelData *proto.ChannelData) error { return fmt.Errorf("%w %x", errNoSuchChannelBind, uint16(channelData.Number)) } - l, err := alloc.RelaySocket.WriteTo(channelData.Data, channel.Peer) + l, err := alloc.WriteTo(channelData.Data, channel.Peer) if err != nil { return fmt.Errorf("%w: %s", errFailedWriteSocket, err.Error()) } else if l != len(channelData.Data) { @@ -459,3 +598,159 @@ func handleChannelData(req Request, channelData *proto.ChannelData) error { return nil } + +func handleConnectRequest(req Request, stunMsg *stun.Message) error { + req.Log.Debugf("Received Connect from %s", req.SrcAddr) + + messageIntegrity, hasAuth, userID, err := authenticateRequest(req, stunMsg, stun.MethodConnect) + if !hasAuth { + return err + } + + alloc := req.AllocationManager.GetAllocationForUserID(&allocation.FiveTuple{ + SrcAddr: req.SrcAddr, + DstAddr: req.Conn.LocalAddr(), + Protocol: allocation.UDP, + }, userID) + if alloc == nil { + return fmt.Errorf("%w %v:%v", errNoAllocationFound, req.SrcAddr, req.Conn.LocalAddr()) + } + + // If the request does not contain an XOR-PEER-ADDRESS attribute, or if + // such attribute is invalid, the server MUST return a 400 (Bad Request) + // error. + var peerAddr proto.PeerAddress + if err = peerAddr.GetFrom(stunMsg); err != nil { + return buildAndSendErr(req.Conn, req.SrcAddr, err, buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodConnect, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest}, + )...) + } + + connectionID, err := req.AllocationManager.CreateTCPConnection(alloc, peerAddr) + if err != nil { + // If the server is currently processing a Connect request for this + // allocation with the same XOR-PEER-ADDRESS, it MUST return a 446 + // (Connection Already Exists) error. + + // If the server has already successfully processed a Connect request + // for this allocation with the same XOR-PEER-ADDRESS, and the resulting + // client and peer data connections are either pending or active, it + // MUST return a 446 (Connection Already Exists) error. + if errors.Is(err, allocation.ErrDupeTCPConnection) { + return buildAndSendErr(req.Conn, req.SrcAddr, err, buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodConnect, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodeConnAlreadyExists}, + )...) + } + + // Otherwise, the server MUST initiate an outgoing TCP connection. The + // local endpoint is the relayed transport address associated with the + // allocation. The remote endpoint is the one indicated by the XOR- + // PEER-ADDRESS attribute. If the connection attempt fails or times + // out, the server MUST return a 447 (Connection Timeout or Failure) + // error. The timeout value MUST be at least 30 seconds. + if errors.Is(err, allocation.ErrTCPConnectionTimeoutOrFailure) { + return buildAndSendErr(req.Conn, req.SrcAddr, err, buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodConnect, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodeConnTimeoutOrFailure}, + )...) + } + + return err + } + + // The server MUST include the CONNECTION-ID attribute in the Connect + // success response. The attribute's value MUST uniquely identify the + // peer data connection. + return buildAndSend(req.Conn, req.SrcAddr, buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodConnect, stun.ClassSuccessResponse), + connectionID, + messageIntegrity, + )...) +} + +func handleConnectionBindRequest(req Request, stunMsg *stun.Message) error { + req.Log.Debugf("Received ConnectBind from %s", req.SrcAddr) + badRequestMsg := buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodConnectionBind, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest}, + ) + + _, hasAuth, userID, err := authenticateRequest(req, stunMsg, stun.MethodConnectionBind) + if !hasAuth { + return err + } + + var connectionID proto.ConnectionID + if err = connectionID.GetFrom(stunMsg); err != nil { + return buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) + } + + // Authentication of the client by the server MUST use the same method + // and credentials as for the control connection. + // + // GetTCPConnection asserts that userName used for auth is same as allocation + tcpConn := req.AllocationManager.GetTCPConnection(userID, connectionID) + if tcpConn == nil { + return buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) + } + + stunConn, ok := req.Conn.(*proto.STUNConn) + if !ok { + return buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) + } + + if err = buildAndSend(req.Conn, req.SrcAddr, buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodConnectionBind, stun.ClassSuccessResponse), + connectionID, + )...); err != nil { + return err + } + + copyCompleteCtx, copyCompleteCancel := context.WithCancel(context.Background()) + go func() { + if _, ioErr := io.Copy(tcpConn, stunConn.Conn()); ioErr != nil { + req.Log.Debugf("Exit tcpConn->stunConn read loop on error: %s", ioErr) + } + copyCompleteCancel() + }() + + go func() { + if _, ioErr := io.Copy(stunConn.Conn(), tcpConn); ioErr != nil { + req.Log.Debugf("Exit stunConn->tcpConn read loop on error: %s", ioErr) + } + copyCompleteCancel() + }() + + // When either side has failed close both + <-copyCompleteCtx.Done() + if err = tcpConn.Close(); err != nil { + req.Log.Debugf("Close tcpConn error: %s", err) + } + if err = stunConn.Conn().Close(); err != nil { + req.Log.Debugf("Close stunConn error: %s", err) + } + + req.AllocationManager.RemoveTCPConnection(connectionID) + + return nil +} + +// ipMatchesFamily checks if an IP address matches the given address family. +func ipMatchesFamily(ip net.IP, family proto.RequestedAddressFamily) bool { + if family == proto.RequestedFamilyIPv4 { + return ip.To4() != nil + } + if family == proto.RequestedFamilyIPv6 { + return ip.To4() == nil && ip.To16() != nil + } + + return false +} diff --git a/vendor/github.com/pion/turn/v4/internal/server/util.go b/vendor/github.com/pion/turn/v5/internal/server/util.go similarity index 74% rename from vendor/github.com/pion/turn/v4/internal/server/util.go rename to vendor/github.com/pion/turn/v5/internal/server/util.go index 65ce1038b2..9cea8a5efe 100644 --- a/vendor/github.com/pion/turn/v4/internal/server/util.go +++ b/vendor/github.com/pion/turn/v5/internal/server/util.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package server @@ -10,7 +10,8 @@ import ( "time" "github.com/pion/stun/v3" - "github.com/pion/turn/v4/internal/proto" + "github.com/pion/turn/v5/internal/auth" + "github.com/pion/turn/v5/internal/proto" ) const ( @@ -49,17 +50,18 @@ func buildMsg( } func authenticateRequest(req Request, stunMsg *stun.Message, callingMethod stun.Method) ( - stun.MessageIntegrity, - bool, - error, + messageIntegrity stun.MessageIntegrity, + hasAuth bool, + username string, + err error, ) { - respondWithNonce := func(responseCode stun.ErrorCode) (stun.MessageIntegrity, bool, error) { + respondWithNonce := func(responseCode stun.ErrorCode) (stun.MessageIntegrity, bool, string, error) { nonce, err := req.NonceHash.Generate() if err != nil { - return nil, false, err + return nil, false, "", err } - return nil, false, buildAndSend(req.Conn, req.SrcAddr, buildMsg(stunMsg.TransactionID, + return nil, false, "", buildAndSend(req.Conn, req.SrcAddr, buildMsg(stunMsg.TransactionID, stun.NewType(callingMethod, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: responseCode}, stun.NewNonce(nonce), @@ -85,11 +87,11 @@ func authenticateRequest(req Request, stunMsg *stun.Message, callingMethod stun. if req.AuthHandler == nil { sendErr := buildAndSend(req.Conn, req.SrcAddr, badRequestMsg...) - return nil, false, sendErr + return nil, false, "", sendErr } if err := nonceAttr.GetFrom(stunMsg); err != nil { - return nil, false, buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) + return nil, false, "", buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) } // Assert Nonce is signed and is not expired. @@ -98,14 +100,19 @@ func authenticateRequest(req Request, stunMsg *stun.Message, callingMethod stun. } if err := realmAttr.GetFrom(stunMsg); err != nil { - return nil, false, buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) + return nil, false, "", buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) } else if err := usernameAttr.GetFrom(stunMsg); err != nil { - return nil, false, buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) + return nil, false, "", buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) } - ourKey, ok := req.AuthHandler(usernameAttr.String(), realmAttr.String(), req.SrcAddr) + userID, ourKey, ok := req.AuthHandler(&auth.RequestAttributes{ + Username: usernameAttr.String(), + Realm: realmAttr.String(), + SrcAddr: req.SrcAddr, + TLS: req.TLS, + }) if !ok { - return nil, false, buildAndSendErr( + return nil, false, "", buildAndSendErr( req.Conn, req.SrcAddr, fmt.Errorf("%w %s", errNoSuchUser, usernameAttr.String()), @@ -116,12 +123,12 @@ func authenticateRequest(req Request, stunMsg *stun.Message, callingMethod stun. if err := stun.MessageIntegrity(ourKey).Check(stunMsg); err != nil { genAuthEvent(req, stunMsg, callingMethod, false) - return nil, false, buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) + return nil, false, "", buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) } genAuthEvent(req, stunMsg, callingMethod, true) - return stun.MessageIntegrity(ourKey), true, nil + return stun.MessageIntegrity(ourKey), true, userID, nil } func genAuthEvent(req Request, stunMsg *stun.Message, callingMethod stun.Method, verdict bool) { @@ -134,6 +141,7 @@ func genAuthEvent(req Request, stunMsg *stun.Message, callingMethod stun.Method, return } + // Auth event is generated per the username, not the user-id. usernameAttr := &stun.Username{} if err := usernameAttr.GetFrom(stunMsg); err != nil { return @@ -149,8 +157,8 @@ func genAuthEvent(req Request, stunMsg *stun.Message, callingMethod stun.Method, callingMethod.String(), verdict) } -func allocationLifeTime(m *stun.Message) time.Duration { - lifetimeDuration := proto.DefaultLifetime +func allocationLifeTime(req Request, m *stun.Message) time.Duration { + lifetimeDuration := req.AllocationLifetime var lifetime proto.Lifetime if err := lifetime.GetFrom(m); err == nil { diff --git a/vendor/github.com/pion/turn/v4/lt_cred.go b/vendor/github.com/pion/turn/v5/lt_cred.go similarity index 67% rename from vendor/github.com/pion/turn/v4/lt_cred.go rename to vendor/github.com/pion/turn/v5/lt_cred.go index 65576be288..5a892eee44 100644 --- a/vendor/github.com/pion/turn/v4/lt_cred.go +++ b/vendor/github.com/pion/turn/v5/lt_cred.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package turn @@ -7,12 +7,12 @@ import ( "crypto/hmac" "crypto/sha1" //nolint:gosec,gci "encoding/base64" - "net" "strconv" "strings" "time" "github.com/pion/logging" + "github.com/pion/turn/v5/internal/auth" ) // GenerateLongTermCredentials can be used to create credentials valid for [duration] time. @@ -57,27 +57,27 @@ func NewLongTermAuthHandler(sharedSecret string, logger logging.LeveledLogger) A logger = logging.NewDefaultLoggerFactory().NewLogger("turn") } - return func(username, realm string, srcAddr net.Addr) (key []byte, ok bool) { - logger.Tracef("Authentication username=%q realm=%q srcAddr=%v", username, realm, srcAddr) - t, err := strconv.Atoi(username) + return func(ra *auth.RequestAttributes) (userID string, key []byte, ok bool) { + logger.Tracef("Authentication username=%q realm=%q srcAddr=%v", ra.Username, ra.Realm, ra.SrcAddr) + t, err := strconv.Atoi(ra.Username) if err != nil { - logger.Errorf("Invalid time-windowed username %q", username) + logger.Errorf("Invalid time-windowed username %q", ra.Username) - return nil, false + return "", nil, false } if int64(t) < time.Now().Unix() { - logger.Errorf("Expired time-windowed username %q", username) + logger.Errorf("Expired time-windowed username %q", ra.Username) - return nil, false + return "", nil, false } - password, err := longTermCredentials(username, sharedSecret) + password, err := longTermCredentials(ra.Username, sharedSecret) if err != nil { logger.Error(err.Error()) - return nil, false + return "", nil, false } - return GenerateAuthKey(username, realm, password), true + return ra.Username, GenerateAuthKey(ra.Username, ra.Realm, password), true } } @@ -92,27 +92,36 @@ func LongTermTURNRESTAuthHandler(sharedSecret string, logger logging.LeveledLogg logger = logging.NewDefaultLoggerFactory().NewLogger("turn") } - return func(username, realm string, srcAddr net.Addr) (key []byte, ok bool) { - logger.Tracef("Authentication username=%q realm=%q srcAddr=%v", username, realm, srcAddr) - timestamp := strings.Split(username, ":")[0] + return func(ra *auth.RequestAttributes) (string, []byte, bool) { + logger.Tracef("Authentication username=%q realm=%q srcAddr=%v", ra.Username, ra.Realm, ra.SrcAddr) + + // Parse timestamp and userID. Set userID to the full username if the id cannot be + // parsed. + fields := strings.Split(ra.Username, ":") + timestamp := fields[0] + userID := ra.Username + if len(fields) > 0 { + userID = fields[1] + } + t, err := strconv.Atoi(timestamp) if err != nil { - logger.Errorf("Invalid time-windowed username %q", username) + logger.Errorf("Invalid time-windowed username %q", ra.Username) - return nil, false + return "", nil, false } if int64(t) < time.Now().Unix() { - logger.Errorf("Expired time-windowed username %q", username) + logger.Errorf("Expired time-windowed username %q", ra.Username) - return nil, false + return "", nil, false } - password, err := longTermCredentials(username, sharedSecret) + password, err := longTermCredentials(ra.Username, sharedSecret) if err != nil { logger.Error(err.Error()) - return nil, false + return "", nil, false } - return GenerateAuthKey(username, realm, password), true + return userID, GenerateAuthKey(ra.Username, ra.Realm, password), true } } diff --git a/vendor/github.com/pion/turn/v5/relay_address_generator_none.go b/vendor/github.com/pion/turn/v5/relay_address_generator_none.go new file mode 100644 index 0000000000..f94c9296ce --- /dev/null +++ b/vendor/github.com/pion/turn/v5/relay_address_generator_none.go @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: 2026 The Pion community +// SPDX-License-Identifier: MIT + +package turn + +import ( + "context" + "fmt" + "net" + "strconv" + + "github.com/pion/transport/v4" + "github.com/pion/transport/v4/reuseport" + "github.com/pion/transport/v4/stdnet" +) + +// RelayAddressGeneratorNone returns the listener with no modifications. +type RelayAddressGeneratorNone struct { + // Address is passed to Listen/ListenPacket when creating the Relay + Address string + + Net transport.Net +} + +// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured. +func (r *RelayAddressGeneratorNone) Validate() error { + if r.Net == nil { + var err error + r.Net, err = stdnet.NewNet() + if err != nil { + return fmt.Errorf("failed to create network: %w", err) + } + } + + if r.Address == "" { + return errListeningAddressInvalid + } + + return nil +} + +// AllocatePacketConn generates a new PacketConn to receive traffic on and the IP/Port +// to populate the allocation response with. +func (r *RelayAddressGeneratorNone) AllocatePacketConn(conf AllocateListenerConfig) ( + net.PacketConn, + net.Addr, + error, +) { + conn, err := r.Net.ListenPacket(conf.Network, + net.JoinHostPort(r.Address, strconv.Itoa(conf.RequestedPort))) // nolint: noctx + if err != nil { + return nil, nil, err + } + + return conn, conn.LocalAddr(), nil +} + +// AllocateListener generates a new Listener to receive traffic on and the IP/Port +// to populate the allocation response with. +func (r *RelayAddressGeneratorNone) AllocateListener(conf AllocateListenerConfig) (net.Listener, net.Addr, error) { + // AllocateListener can be called independently of Validate (e.g. in tests), + // so ensure we're initialized to avoid nil dereferences. + if r.Net == nil || r.Address == "" { + if err := r.Validate(); err != nil { + return nil, nil, err + } + } + + tcpAddr, err := r.Net.ResolveTCPAddr(conf.Network, net.JoinHostPort(r.Address, strconv.Itoa(conf.RequestedPort))) + if err != nil { + return nil, nil, err + } + + listenConfig := r.Net.CreateListenConfig(&net.ListenConfig{ + // Enable SO_REUSEADDR and SO_REUSEPORT where needed to let multiple connnections + // bind to the same relay address. + Control: reuseport.Control, + }) + ln, err := listenConfig.Listen(context.TODO(), conf.Network, tcpAddr.String()) + if err != nil { + return nil, nil, err + } + + return ln, ln.Addr(), nil +} + +// AllocateConn creates a new outgoing TCP connection bound to the relay address to send traffic to a peer. +func (r *RelayAddressGeneratorNone) AllocateConn(conf AllocateConnConfig) (net.Conn, error) { + dialer := r.Net.CreateDialer(&net.Dialer{ + LocalAddr: conf.LocalAddr, + // Enable SO_REUSEADDR and SO_REUSEPORT where needed to let multiple connnections + // bind to the same relay address. + Control: reuseport.Control, + }) + + return dialer.Dial(conf.Network, conf.RemoteAddr.String()) +} diff --git a/vendor/github.com/pion/turn/v5/relay_address_generator_range.go b/vendor/github.com/pion/turn/v5/relay_address_generator_range.go new file mode 100644 index 0000000000..f8ec004c3c --- /dev/null +++ b/vendor/github.com/pion/turn/v5/relay_address_generator_range.go @@ -0,0 +1,185 @@ +// SPDX-FileCopyrightText: 2026 The Pion community +// SPDX-License-Identifier: MIT + +package turn + +import ( + "context" + "fmt" + "net" + "strconv" + + "github.com/pion/randutil" + "github.com/pion/transport/v4" + "github.com/pion/transport/v4/reuseport" + "github.com/pion/transport/v4/stdnet" +) + +// RelayAddressGeneratorPortRange can be used to only allocate connections inside a defined port range. +// Similar to the RelayAddressGeneratorStatic a static ip address can be set. +type RelayAddressGeneratorPortRange struct { + // RelayAddress is the IP returned to the user when the relay is created + RelayAddress net.IP + + // MinPort the minimum port to allocate + MinPort uint16 + // MaxPort the maximum (inclusive) port to allocate + MaxPort uint16 + + // MaxRetries the amount of tries to allocate a random port in the defined range + MaxRetries int + + // Rand the random source of numbers + Rand randutil.MathRandomGenerator + + // Address is passed to Listen/ListenPacket when creating the Relay + Address string + + Net transport.Net +} + +// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured. +func (r *RelayAddressGeneratorPortRange) Validate() error { + if r.Net == nil { + var err error + r.Net, err = stdnet.NewNet() + if err != nil { + return fmt.Errorf("failed to create network: %w", err) + } + } + + if r.Rand == nil { + r.Rand = randutil.NewMathRandomGenerator() + } + + if r.MaxRetries == 0 { + r.MaxRetries = 10 + } + + switch { + case r.MinPort == 0: + return errMinPortNotZero + case r.MaxPort == 0: + return errMaxPortNotZero + case r.RelayAddress == nil: + return errRelayAddressInvalid + case r.Address == "": + return errListeningAddressInvalid + default: + return nil + } +} + +// AllocatePacketConn generates a new PacketConn to receive traffic on and the IP/Port +// to populate the allocation response with. +func (r *RelayAddressGeneratorPortRange) AllocatePacketConn( + conf AllocateListenerConfig, +) (net.PacketConn, net.Addr, error) { + if conf.RequestedPort != 0 { + listenAddr := net.JoinHostPort(r.Address, strconv.Itoa(conf.RequestedPort)) + conn, err := r.Net.ListenPacket(conf.Network, listenAddr) // nolint: noctx + if err != nil { + return nil, nil, err + } + + relayAddr, ok := conn.LocalAddr().(*net.UDPAddr) + if !ok { + return nil, nil, errNilConn + } + + relayAddr.IP = r.RelayAddress + + return conn, relayAddr, nil + } + + for try := 0; try < r.MaxRetries; try++ { + port := r.MinPort + uint16(r.Rand.Intn(int((r.MaxPort+1)-r.MinPort))) // nolint:gosec // G115 false positive + listenAddr := net.JoinHostPort(r.Address, strconv.Itoa(int(port))) + conn, err := r.Net.ListenPacket(conf.Network, listenAddr) // nolint: noctx + if err != nil { + continue + } + + relayAddr, ok := conn.LocalAddr().(*net.UDPAddr) + if !ok { + return nil, nil, errNilConn + } + + relayAddr.IP = r.RelayAddress + + return conn, relayAddr, nil + } + + return nil, nil, errMaxRetriesExceeded +} + +// AllocateListener generates a new Listener to receive traffic on and the IP/Port +// to populate the allocation response with. +func (r *RelayAddressGeneratorPortRange) AllocateListener( // nolint: cyclop + conf AllocateListenerConfig, +) (net.Listener, net.Addr, error) { + // AllocateListener can be called independently of Validate (e.g. in tests), + // so ensure we're initialized to avoid nil dereferences. + if r.Net == nil || r.Address == "" || r.RelayAddress == nil || r.Rand == nil { + if err := r.Validate(); err != nil { + return nil, nil, err + } + } + + listenConfig := r.Net.CreateListenConfig(&net.ListenConfig{ + // Enable SO_REUSEADDR and SO_REUSEPORT where needed to let multiple connnections + // bind to the same relay address. + Control: reuseport.Control, + }) + listen := func(port int) (net.Listener, net.Addr, error) { + listenAddr := net.JoinHostPort(r.Address, strconv.Itoa(port)) + tcpAddr, err := r.Net.ResolveTCPAddr(conf.Network, listenAddr) + if err != nil { + return nil, nil, err + } + + ln, err := listenConfig.Listen(context.TODO(), conf.Network, tcpAddr.String()) + if err != nil { + return nil, nil, err + } + + relayAddr, ok := ln.Addr().(*net.TCPAddr) + if !ok { + _ = ln.Close() + + return nil, nil, errNilConn + } + + relayAddr.IP = r.RelayAddress + + return ln, relayAddr, nil + } + + if conf.RequestedPort != 0 { + return listen(conf.RequestedPort) + } + + for try := 0; try < r.MaxRetries; try++ { + port := int(r.MinPort + uint16(r.Rand.Intn(int((r.MaxPort+1)-r.MinPort)))) // nolint:gosec // G115 false positive + ln, addr, err := listen(port) + if err != nil { + continue + } + + return ln, addr, nil + } + + return nil, nil, errMaxRetriesExceeded +} + +// AllocateConn creates a new outgoing TCP connection bound to the relay address to send traffic to a peer. +func (r *RelayAddressGeneratorPortRange) AllocateConn(conf AllocateConnConfig) (net.Conn, error) { + dialer := r.Net.CreateDialer(&net.Dialer{ + LocalAddr: conf.LocalAddr, + // Enable SO_REUSEADDR and SO_REUSEPORT where needed to let multiple connnections + // bind to the same relay address. + Control: reuseport.Control, + }) + + return dialer.Dial(conf.Network, conf.RemoteAddr.String()) +} diff --git a/vendor/github.com/pion/turn/v5/relay_address_generator_static.go b/vendor/github.com/pion/turn/v5/relay_address_generator_static.go new file mode 100644 index 0000000000..1c6824d559 --- /dev/null +++ b/vendor/github.com/pion/turn/v5/relay_address_generator_static.go @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: 2026 The Pion community +// SPDX-License-Identifier: MIT + +package turn + +import ( + "context" + "fmt" + "net" + "strconv" + + "github.com/pion/transport/v4" + "github.com/pion/transport/v4/reuseport" + "github.com/pion/transport/v4/stdnet" +) + +// RelayAddressGeneratorStatic can be used to return static IP address each time a relay is created. +// This can be used when you have a single static IP address that you want to use. +type RelayAddressGeneratorStatic struct { + // RelayAddress is the IP returned to the user when the relay is created + RelayAddress net.IP + + // Address is passed to Listen/ListenPacket when creating the Relay + Address string + + Net transport.Net +} + +// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured. +func (r *RelayAddressGeneratorStatic) Validate() error { + if r.Net == nil { + var err error + r.Net, err = stdnet.NewNet() + if err != nil { + return fmt.Errorf("failed to create network: %w", err) + } + } + + switch { + case r.RelayAddress == nil: + return errRelayAddressInvalid + case r.Address == "": + return errListeningAddressInvalid + default: + return nil + } +} + +// AllocatePacketConn generates a new PacketConn to receive traffic on and the IP/Port +// to populate the allocation response with. +func (r *RelayAddressGeneratorStatic) AllocatePacketConn( + conf AllocateListenerConfig, +) (net.PacketConn, net.Addr, error) { + conn, err := r.Net.ListenPacket(conf.Network, + net.JoinHostPort(r.Address, strconv.Itoa(conf.RequestedPort))) // nolint: noctx + if err != nil { + return nil, nil, err + } + + // Replace actual listening IP with the user requested one of RelayAddressGeneratorStatic + relayAddr, ok := conn.LocalAddr().(*net.UDPAddr) + if !ok { + return nil, nil, errNilConn + } + + relayAddr.IP = r.RelayAddress + + return conn, relayAddr, nil +} + +// AllocateListener generates a new Listener to receive traffic on and the IP/Port +// to populate the allocation response with. +func (r *RelayAddressGeneratorStatic) AllocateListener(conf AllocateListenerConfig) (net.Listener, net.Addr, error) { + // AllocateListener can be called independently of Validate (e.g. in tests), + // so ensure we're initialized to avoid nil dereferences. + if r.Net == nil || r.Address == "" || r.RelayAddress == nil { + if err := r.Validate(); err != nil { + return nil, nil, err + } + } + + tcpAddr, err := r.Net.ResolveTCPAddr(conf.Network, net.JoinHostPort(r.Address, strconv.Itoa(conf.RequestedPort))) + if err != nil { + return nil, nil, err + } + + listenConfig := r.Net.CreateListenConfig(&net.ListenConfig{ + // Enable SO_REUSEADDR and SO_REUSEPORT where needed to let multiple connnections + // bind to the same relay address. + Control: reuseport.Control, + }) + ln, err := listenConfig.Listen(context.TODO(), conf.Network, tcpAddr.String()) + if err != nil { + return nil, nil, err + } + + // Replace actual listening IP with the user requested one of RelayAddressGeneratorStatic + relayAddr, ok := ln.Addr().(*net.TCPAddr) + if !ok { + _ = ln.Close() + + return nil, nil, errNilConn + } + + relayAddr.IP = r.RelayAddress + + return ln, relayAddr, nil +} + +// AllocateConn creates a new outgoing TCP connection bound to the relay address to send traffic to a peer. +func (r *RelayAddressGeneratorStatic) AllocateConn(conf AllocateConnConfig) (net.Conn, error) { + dialer := r.Net.CreateDialer(&net.Dialer{ + LocalAddr: conf.LocalAddr, + // Enable SO_REUSEADDR and SO_REUSEPORT where needed to let multiple connnections + // bind to the same relay address. + Control: reuseport.Control, + }) + + return dialer.Dial(conf.Network, conf.RemoteAddr.String()) +} diff --git a/vendor/github.com/pion/turn/v4/renovate.json b/vendor/github.com/pion/turn/v5/renovate.json similarity index 100% rename from vendor/github.com/pion/turn/v4/renovate.json rename to vendor/github.com/pion/turn/v5/renovate.json diff --git a/vendor/github.com/pion/turn/v4/server.go b/vendor/github.com/pion/turn/v5/server.go similarity index 75% rename from vendor/github.com/pion/turn/v4/server.go rename to vendor/github.com/pion/turn/v5/server.go index eaf6fdb01a..de324d4728 100644 --- a/vendor/github.com/pion/turn/v4/server.go +++ b/vendor/github.com/pion/turn/v5/server.go @@ -1,19 +1,21 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT // Package turn contains the public API for pion/turn, a toolkit for building TURN clients and servers package turn import ( + "context" + "crypto/tls" "errors" "fmt" "net" "time" "github.com/pion/logging" - "github.com/pion/turn/v4/internal/allocation" - "github.com/pion/turn/v4/internal/proto" - "github.com/pion/turn/v4/internal/server" + "github.com/pion/turn/v5/internal/allocation" + "github.com/pion/turn/v5/internal/proto" + "github.com/pion/turn/v5/internal/server" ) const ( @@ -27,6 +29,8 @@ type Server struct { quotaHandler QuotaHandler realm string channelBindTimeout time.Duration + permissionTimeout time.Duration + allocationLifetime time.Duration nonceHash server.NonceManager eventHandler EventHandler @@ -63,6 +67,8 @@ func NewServer(config ServerConfig) (*Server, error) { //nolint:gocognit,cyclop quotaHandler: config.QuotaHandler, realm: config.Realm, channelBindTimeout: config.ChannelBindTimeout, + permissionTimeout: config.PermissionTimeout, + allocationLifetime: config.AllocationLifetime, packetConnConfigs: config.PacketConnConfigs, listenerConfigs: config.ListenerConfigs, nonceHash: nonceHash, @@ -73,6 +79,12 @@ func NewServer(config ServerConfig) (*Server, error) { //nolint:gocognit,cyclop if server.channelBindTimeout == 0 { server.channelBindTimeout = proto.DefaultLifetime } + if server.permissionTimeout == 0 { + server.permissionTimeout = allocation.DefaultPermissionTimeout + } + if server.allocationLifetime == 0 { + server.allocationLifetime = proto.DefaultLifetime + } for _, cfg := range server.packetConnConfigs { am, err := server.createAllocationManager(cfg.RelayAddressGenerator, cfg.PermissionHandler) @@ -81,7 +93,7 @@ func NewServer(config ServerConfig) (*Server, error) { //nolint:gocognit,cyclop } go func(cfg PacketConnConfig, am *allocation.Manager) { - server.readLoop(cfg.PacketConn, am) + server.readLoop(cfg.PacketConn, am, nil) if err := am.Close(); err != nil { server.log.Errorf("Failed to close AllocationManager: %s", err) @@ -157,7 +169,26 @@ func (s *Server) readListener(l net.Listener, am *allocation.Manager) { } go func() { - s.readLoop(NewSTUNConn(conn), am) + var tlsConnectionState *tls.ConnectionState + + // Extract tls connection state if possible + tlsConn, ok := conn.(*tls.Conn) + if ok { + // Force TLS handshake to complete before extracting connection state. + // Per crypto/tls docs: handshakes are lazy (complete on first I/O). + // We must call Handshake() explicitly to populate PeerCertificates. + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + if err := tlsConn.HandshakeContext(ctx); err != nil { + s.log.Errorf("TLS handshake failed: %s", err) + + return + } + cs := tlsConn.ConnectionState() + tlsConnectionState = &cs + } + + s.readLoop(NewSTUNConn(conn), am, tlsConnectionState) // Delete allocation am.DeleteAllocation(&allocation.FiveTuple{ @@ -177,14 +208,18 @@ type nilAddressGenerator struct{} func (n *nilAddressGenerator) Validate() error { return errRelayAddressGeneratorNil } -func (n *nilAddressGenerator) AllocatePacketConn(string, int) (net.PacketConn, net.Addr, error) { +func (n *nilAddressGenerator) AllocatePacketConn(AllocateListenerConfig) (net.PacketConn, net.Addr, error) { return nil, nil, errRelayAddressGeneratorNil } -func (n *nilAddressGenerator) AllocateConn(string, int) (net.Conn, net.Addr, error) { +func (n *nilAddressGenerator) AllocateListener(AllocateListenerConfig) (net.Listener, net.Addr, error) { return nil, nil, errRelayAddressGeneratorNil } +func (n *nilAddressGenerator) AllocateConn(AllocateConnConfig) (net.Conn, error) { + return nil, errRelayAddressGeneratorNil +} + func (s *Server) createAllocationManager( addrGenerator RelayAddressGenerator, handler PermissionHandler, @@ -198,6 +233,7 @@ func (s *Server) createAllocationManager( am, err := allocation.NewManager(allocation.ManagerConfig{ AllocatePacketConn: addrGenerator.AllocatePacketConn, + AllocateListener: addrGenerator.AllocateListener, AllocateConn: addrGenerator.AllocateConn, PermissionHandler: handler, EventHandler: s.eventHandler, @@ -212,7 +248,7 @@ func (s *Server) createAllocationManager( return am, err } -func (s *Server) readLoop(conn net.PacketConn, allocationManager *allocation.Manager) { +func (s *Server) readLoop(conn net.PacketConn, allocationManager *allocation.Manager, tlsState *tls.ConnectionState) { buf := make([]byte, s.inboundMTU) for { n, addr, err := conn.ReadFrom(buf) @@ -231,12 +267,15 @@ func (s *Server) readLoop(conn net.PacketConn, allocationManager *allocation.Man Conn: conn, SrcAddr: addr, Buff: buf[:n], + TLS: tlsState, Log: s.log, AuthHandler: s.authHandler, QuotaHandler: s.quotaHandler, Realm: s.realm, AllocationManager: allocationManager, ChannelBindTimeout: s.channelBindTimeout, + PermissionTimeout: s.permissionTimeout, + AllocationLifetime: s.allocationLifetime, NonceHash: s.nonceHash, }); err != nil { if s.eventHandler.OnAllocationError != nil { diff --git a/vendor/github.com/pion/turn/v4/server_config.go b/vendor/github.com/pion/turn/v5/server_config.go similarity index 80% rename from vendor/github.com/pion/turn/v4/server_config.go rename to vendor/github.com/pion/turn/v5/server_config.go index fcddabcf87..4ab068e774 100644 --- a/vendor/github.com/pion/turn/v4/server_config.go +++ b/vendor/github.com/pion/turn/v5/server_config.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-FileCopyrightText: 2026 The Pion community // SPDX-License-Identifier: MIT package turn @@ -11,9 +11,16 @@ import ( "time" "github.com/pion/logging" - "github.com/pion/turn/v4/internal/allocation" + "github.com/pion/turn/v5/internal/allocation" + "github.com/pion/turn/v5/internal/auth" ) +// AllocateListenerConfig defines the parameters passed to the relay address allocator. +type AllocateListenerConfig = allocation.AllocateListenerConfig + +// AllocateConnConfig defines the parameters passed to the TCP connection generator. +type AllocateConnConfig = allocation.AllocateConnConfig + // RelayAddressGenerator is used to generate a RelayAddress when creating an allocation. // You can use one of the provided ones or provide your own. type RelayAddressGenerator interface { @@ -21,10 +28,13 @@ type RelayAddressGenerator interface { Validate() error // Allocate a PacketConn (UDP) RelayAddress - AllocatePacketConn(network string, requestedPort int) (net.PacketConn, net.Addr, error) + AllocatePacketConn(AllocateListenerConfig) (net.PacketConn, net.Addr, error) - // Allocate a Conn (TCP) RelayAddress - AllocateConn(network string, requestedPort int) (net.Conn, net.Addr, error) + // Allocate a Listener (TCP) RelayAddress + AllocateListener(AllocateListenerConfig) (net.Listener, net.Addr, error) + + // Allocate a Conn (TCP) relay connection + AllocateConn(AllocateConnConfig) (net.Conn, error) } // PermissionHandler is a callback to filter incoming CreatePermission and ChannelBindRequest @@ -75,7 +85,7 @@ type ListenerConfig struct { Listener net.Listener // When an allocation is generated the RelayAddressGenerator - // creates the net.PacketConn and returns the IP/Port it is available at + // creates the net.Listener and returns the IP/Port it is available at RelayAddressGenerator RelayAddressGenerator // PermissionHandler is a callback to filter peer addresses. Can be set as nil, in which @@ -96,9 +106,13 @@ func (c *ListenerConfig) validate() error { return c.RelayAddressGenerator.Validate() } +// RequestAttributes represents attributes of a TURN request which +// may be useful for authorizing the underlying request. +type RequestAttributes = auth.RequestAttributes + // AuthHandler is a callback used to handle incoming auth requests, // allowing users to customize Pion TURN with custom behavior. -type AuthHandler func(username, realm string, srcAddr net.Addr) (key []byte, ok bool) +type AuthHandler = auth.AuthHandler // GenerateAuthKey is a convenience function to easily generate keys in the format used by AuthHandler. func GenerateAuthKey(username, realm, password string) []byte { @@ -145,6 +159,12 @@ type ServerConfig struct { // ChannelBindTimeout sets the lifetime of channel binding. Defaults to 10 minutes. ChannelBindTimeout time.Duration + // PermissionTimeout sets the lifetime of permission. Defaults to 10 minutes. + PermissionTimeout time.Duration + + // AllocationLife sets the lifetime of allocation. Defaults to 10 minutes. + AllocationLifetime time.Duration + // Sets the server inbound MTU(Maximum transmition unit). Defaults to 1600 bytes. InboundMTU int } diff --git a/vendor/github.com/pion/turn/v5/stun_conn.go b/vendor/github.com/pion/turn/v5/stun_conn.go new file mode 100644 index 0000000000..08ff6f3355 --- /dev/null +++ b/vendor/github.com/pion/turn/v5/stun_conn.go @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2026 The Pion community +// SPDX-License-Identifier: MIT + +package turn + +import ( + "net" + + "github.com/pion/turn/v5/internal/proto" +) + +type STUNConn = proto.STUNConn + +// NewSTUNConn creates a STUNConn. +func NewSTUNConn(nextConn net.Conn) *STUNConn { + return proto.NewSTUNConn(nextConn) +} diff --git a/vendor/github.com/pion/webrtc/v4/.golangci.yml b/vendor/github.com/pion/webrtc/v4/.golangci.yml index 1fbb8dbbfe..6c7ceed691 100644 --- a/vendor/github.com/pion/webrtc/v4/.golangci.yml +++ b/vendor/github.com/pion/webrtc/v4/.golangci.yml @@ -146,3 +146,6 @@ formatters: - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports exclusions: generated: lax +issues: + max-issues-per-linter: 0 + max-same-issues: 0 \ No newline at end of file diff --git a/vendor/github.com/pion/webrtc/v4/DESIGN.md b/vendor/github.com/pion/webrtc/v4/DESIGN.md index 45ca1ac0e2..725ac4e1eb 100644 --- a/vendor/github.com/pion/webrtc/v4/DESIGN.md +++ b/vendor/github.com/pion/webrtc/v4/DESIGN.md @@ -19,7 +19,7 @@ When possible we leave all decisions to the user. When choice is possible (like If you know how to use WebRTC in your browser, you know how to use Pion WebRTC. We try our best just to duplicate the Javascript API, so your code can look the same everywhere. -If this is your first time using WebRTC, don't worry! We have multiple [examples](https://github.com/pion/webrtc/tree/master/examples) and [GoDoc](https://pkg.go.dev/github.com/pion/webrtc/v4) +If this is your first time using WebRTC, don't worry! We have multiple [examples](https://github.com/pion/webrtc/tree/main/examples) and [GoDoc](https://pkg.go.dev/github.com/pion/webrtc/v4) ### Bring your own media Pion WebRTC doesn't make any assumptions about where your audio, video or text come from. You can use FFmpeg, GStreamer, MLT or just serve a video file. diff --git a/vendor/github.com/pion/webrtc/v4/README.md b/vendor/github.com/pion/webrtc/v4/README.md index 97bbe70f0e..1e75bf820d 100644 --- a/vendor/github.com/pion/webrtc/v4/README.md +++ b/vendor/github.com/pion/webrtc/v4/README.md @@ -13,7 +13,7 @@
GitHub Workflow Status Go Reference - Coverage Status + Coverage Status Go Report Card License: MIT

@@ -92,12 +92,12 @@ He is available to talk about Pion or general WebRTC questions, feel free to rea * IVF, Ogg, H264 and Matroska provided for easy sending and saving * [getUserMedia](https://github.com/pion/mediadevices) implementation (Requires Cgo) * Easy integration with x264, libvpx, GStreamer and ffmpeg. -* [Simulcast](https://github.com/pion/webrtc/tree/master/examples/simulcast) -* [SVC](https://github.com/pion/rtp/blob/master/codecs/vp9_packet.go#L138) +* [Simulcast](https://github.com/pion/webrtc/tree/main/examples/simulcast) +* [SVC](https://github.com/pion/rtp/blob/main/codecs/vp9_packet.go#L138) * [NACK](https://github.com/pion/interceptor/pull/4) -* [Sender/Receiver Reports](https://github.com/pion/interceptor/tree/master/pkg/report) -* [Transport Wide Congestion Control Feedback](https://github.com/pion/interceptor/tree/master/pkg/twcc) -* [Bandwidth Estimation](https://github.com/pion/webrtc/tree/master/examples/bandwidth-estimation-from-disk) +* [Sender/Receiver Reports](https://github.com/pion/interceptor/tree/main/pkg/report) +* [Transport Wide Congestion Control Feedback](https://github.com/pion/interceptor/tree/main/pkg/twcc) +* [Bandwidth Estimation](https://github.com/pion/webrtc/tree/main/examples/bandwidth-estimation-from-disk) #### Security * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 and TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA for DTLS v1.2 diff --git a/vendor/github.com/pion/webrtc/v4/rtptransceiver.go b/vendor/github.com/pion/webrtc/v4/rtptransceiver.go index d3dcfa79b2..130a68590e 100644 --- a/vendor/github.com/pion/webrtc/v4/rtptransceiver.go +++ b/vendor/github.com/pion/webrtc/v4/rtptransceiver.go @@ -432,12 +432,10 @@ func handleUnknownRTPPacket( return mid, rid, rsid, false, err } - if rp.Padding && len(rp.Payload) == 0 { - return mid, rid, rsid, true, nil - } + isPaddingOnlyPacket := rp.Padding && len(rp.Payload) == 0 if !rp.Header.Extension { - return mid, rid, rsid, false, nil + return mid, rid, rsid, isPaddingOnlyPacket, nil } if payload := rp.GetExtension(midExtensionID); payload != nil { @@ -452,5 +450,5 @@ func handleUnknownRTPPacket( rsid = string(payload) } - return mid, rid, rsid, false, nil + return mid, rid, rsid, isPaddingOnlyPacket, nil } diff --git a/vendor/go.mongodb.org/mongo-driver/LICENSE b/vendor/go.mongodb.org/mongo-driver/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bson.go b/vendor/go.mongodb.org/mongo-driver/bson/bson.go new file mode 100644 index 0000000000..a0d8185826 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bson.go @@ -0,0 +1,50 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 +// +// Based on gopkg.in/mgo.v2/bson by Gustavo Niemeyer +// See THIRD-PARTY-NOTICES for original license terms. + +package bson // import "go.mongodb.org/mongo-driver/bson" + +import ( + "go.mongodb.org/mongo-driver/bson/primitive" +) + +// Zeroer allows custom struct types to implement a report of zero +// state. All struct types that don't implement Zeroer or where IsZero +// returns false are considered to be not zero. +type Zeroer interface { + IsZero() bool +} + +// D is an ordered representation of a BSON document. This type should be used when the order of the elements matters, +// such as MongoDB command documents. If the order of the elements does not matter, an M should be used instead. +// +// A D should not be constructed with duplicate key names, as that can cause undefined server behavior. +// +// Example usage: +// +// bson.D{{"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}} +type D = primitive.D + +// E represents a BSON element for a D. It is usually used inside a D. +type E = primitive.E + +// M is an unordered representation of a BSON document. This type should be used when the order of the elements does not +// matter. This type is handled as a regular map[string]interface{} when encoding and decoding. Elements will be +// serialized in an undefined, random order. If the order of the elements matters, a D should be used instead. +// +// Example usage: +// +// bson.M{"foo": "bar", "hello": "world", "pi": 3.14159} +type M = primitive.M + +// An A is an ordered representation of a BSON array. +// +// Example usage: +// +// bson.A{"bar", "world", 3.14159, bson.D{{"qux", 12345}}} +type A = primitive.A diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/array_codec.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/array_codec.go new file mode 100644 index 0000000000..652aa48b85 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/array_codec.go @@ -0,0 +1,55 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "reflect" + + "go.mongodb.org/mongo-driver/bson/bsonrw" + "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" +) + +// ArrayCodec is the Codec used for bsoncore.Array values. +// +// Deprecated: ArrayCodec will not be directly accessible in Go Driver 2.0. +type ArrayCodec struct{} + +var defaultArrayCodec = NewArrayCodec() + +// NewArrayCodec returns an ArrayCodec. +// +// Deprecated: NewArrayCodec will not be available in Go Driver 2.0. See +// [ArrayCodec] for more details. +func NewArrayCodec() *ArrayCodec { + return &ArrayCodec{} +} + +// EncodeValue is the ValueEncoder for bsoncore.Array values. +func (ac *ArrayCodec) EncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tCoreArray { + return ValueEncoderError{Name: "CoreArrayEncodeValue", Types: []reflect.Type{tCoreArray}, Received: val} + } + + arr := val.Interface().(bsoncore.Array) + return bsonrw.Copier{}.CopyArrayFromBytes(vw, arr) +} + +// DecodeValue is the ValueDecoder for bsoncore.Array values. +func (ac *ArrayCodec) DecodeValue(_ DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tCoreArray { + return ValueDecoderError{Name: "CoreArrayDecodeValue", Types: []reflect.Type{tCoreArray}, Received: val} + } + + if val.IsNil() { + val.Set(reflect.MakeSlice(val.Type(), 0, 0)) + } + + val.SetLen(0) + arr, err := bsonrw.Copier{}.AppendArrayBytes(val.Interface().(bsoncore.Array), vr) + val.Set(reflect.ValueOf(arr)) + return err +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/bsoncodec.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/bsoncodec.go new file mode 100644 index 0000000000..0693bd432f --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/bsoncodec.go @@ -0,0 +1,382 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec // import "go.mongodb.org/mongo-driver/bson/bsoncodec" + +import ( + "fmt" + "reflect" + "strings" + + "go.mongodb.org/mongo-driver/bson/bsonrw" + "go.mongodb.org/mongo-driver/bson/bsontype" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +var ( + emptyValue = reflect.Value{} +) + +// Marshaler is an interface implemented by types that can marshal themselves +// into a BSON document represented as bytes. The bytes returned must be a valid +// BSON document if the error is nil. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Marshaler] instead. +type Marshaler interface { + MarshalBSON() ([]byte, error) +} + +// ValueMarshaler is an interface implemented by types that can marshal +// themselves into a BSON value as bytes. The type must be the valid type for +// the bytes returned. The bytes and byte type together must be valid if the +// error is nil. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.ValueMarshaler] instead. +type ValueMarshaler interface { + MarshalBSONValue() (bsontype.Type, []byte, error) +} + +// Unmarshaler is an interface implemented by types that can unmarshal a BSON +// document representation of themselves. The BSON bytes can be assumed to be +// valid. UnmarshalBSON must copy the BSON bytes if it wishes to retain the data +// after returning. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Unmarshaler] instead. +type Unmarshaler interface { + UnmarshalBSON([]byte) error +} + +// ValueUnmarshaler is an interface implemented by types that can unmarshal a +// BSON value representation of themselves. The BSON bytes and type can be +// assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it +// wishes to retain the data after returning. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.ValueUnmarshaler] instead. +type ValueUnmarshaler interface { + UnmarshalBSONValue(bsontype.Type, []byte) error +} + +// ValueEncoderError is an error returned from a ValueEncoder when the provided value can't be +// encoded by the ValueEncoder. +type ValueEncoderError struct { + Name string + Types []reflect.Type + Kinds []reflect.Kind + Received reflect.Value +} + +func (vee ValueEncoderError) Error() string { + typeKinds := make([]string, 0, len(vee.Types)+len(vee.Kinds)) + for _, t := range vee.Types { + typeKinds = append(typeKinds, t.String()) + } + for _, k := range vee.Kinds { + if k == reflect.Map { + typeKinds = append(typeKinds, "map[string]*") + continue + } + typeKinds = append(typeKinds, k.String()) + } + received := vee.Received.Kind().String() + if vee.Received.IsValid() { + received = vee.Received.Type().String() + } + return fmt.Sprintf("%s can only encode valid %s, but got %s", vee.Name, strings.Join(typeKinds, ", "), received) +} + +// ValueDecoderError is an error returned from a ValueDecoder when the provided value can't be +// decoded by the ValueDecoder. +type ValueDecoderError struct { + Name string + Types []reflect.Type + Kinds []reflect.Kind + Received reflect.Value +} + +func (vde ValueDecoderError) Error() string { + typeKinds := make([]string, 0, len(vde.Types)+len(vde.Kinds)) + for _, t := range vde.Types { + typeKinds = append(typeKinds, t.String()) + } + for _, k := range vde.Kinds { + if k == reflect.Map { + typeKinds = append(typeKinds, "map[string]*") + continue + } + typeKinds = append(typeKinds, k.String()) + } + received := vde.Received.Kind().String() + if vde.Received.IsValid() { + received = vde.Received.Type().String() + } + return fmt.Sprintf("%s can only decode valid and settable %s, but got %s", vde.Name, strings.Join(typeKinds, ", "), received) +} + +// EncodeContext is the contextual information required for a Codec to encode a +// value. +type EncodeContext struct { + *Registry + + // MinSize causes the Encoder to marshal Go integer values (int, int8, int16, int32, int64, + // uint, uint8, uint16, uint32, or uint64) as the minimum BSON int size (either 32 or 64 bits) + // that can represent the integer value. + // + // Deprecated: Use bson.Encoder.IntMinSize instead. + MinSize bool + + errorOnInlineDuplicates bool + stringifyMapKeysWithFmt bool + nilMapAsEmpty bool + nilSliceAsEmpty bool + nilByteSliceAsEmpty bool + omitZeroStruct bool + useJSONStructTags bool +} + +// ErrorOnInlineDuplicates causes the Encoder to return an error if there is a duplicate field in +// the marshaled BSON when the "inline" struct tag option is set. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.ErrorOnInlineDuplicates] instead. +func (ec *EncodeContext) ErrorOnInlineDuplicates() { + ec.errorOnInlineDuplicates = true +} + +// StringifyMapKeysWithFmt causes the Encoder to convert Go map keys to BSON document field name +// strings using fmt.Sprintf() instead of the default string conversion logic. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.StringifyMapKeysWithFmt] instead. +func (ec *EncodeContext) StringifyMapKeysWithFmt() { + ec.stringifyMapKeysWithFmt = true +} + +// NilMapAsEmpty causes the Encoder to marshal nil Go maps as empty BSON documents instead of BSON +// null. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.NilMapAsEmpty] instead. +func (ec *EncodeContext) NilMapAsEmpty() { + ec.nilMapAsEmpty = true +} + +// NilSliceAsEmpty causes the Encoder to marshal nil Go slices as empty BSON arrays instead of BSON +// null. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.NilSliceAsEmpty] instead. +func (ec *EncodeContext) NilSliceAsEmpty() { + ec.nilSliceAsEmpty = true +} + +// NilByteSliceAsEmpty causes the Encoder to marshal nil Go byte slices as empty BSON binary values +// instead of BSON null. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.NilByteSliceAsEmpty] instead. +func (ec *EncodeContext) NilByteSliceAsEmpty() { + ec.nilByteSliceAsEmpty = true +} + +// OmitZeroStruct causes the Encoder to consider the zero value for a struct (e.g. MyStruct{}) +// as empty and omit it from the marshaled BSON when the "omitempty" struct tag option is set. +// +// Note that the Encoder only examines exported struct fields when determining if a struct is the +// zero value. It considers pointers to a zero struct value (e.g. &MyStruct{}) not empty. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.OmitZeroStruct] instead. +func (ec *EncodeContext) OmitZeroStruct() { + ec.omitZeroStruct = true +} + +// UseJSONStructTags causes the Encoder to fall back to using the "json" struct tag if a "bson" +// struct tag is not specified. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.UseJSONStructTags] instead. +func (ec *EncodeContext) UseJSONStructTags() { + ec.useJSONStructTags = true +} + +// DecodeContext is the contextual information required for a Codec to decode a +// value. +type DecodeContext struct { + *Registry + + // Truncate, if true, instructs decoders to to truncate the fractional part of BSON "double" + // values when attempting to unmarshal them into a Go integer (int, int8, int16, int32, int64, + // uint, uint8, uint16, uint32, or uint64) struct field. The truncation logic does not apply to + // BSON "decimal128" values. + // + // Deprecated: Use bson.Decoder.AllowTruncatingDoubles instead. + Truncate bool + + // Ancestor is the type of a containing document. This is mainly used to determine what type + // should be used when decoding an embedded document into an empty interface. For example, if + // Ancestor is a bson.M, BSON embedded document values being decoded into an empty interface + // will be decoded into a bson.M. + // + // Deprecated: Use bson.Decoder.DefaultDocumentM or bson.Decoder.DefaultDocumentD instead. + Ancestor reflect.Type + + // defaultDocumentType specifies the Go type to decode top-level and nested BSON documents into. In particular, the + // usage for this field is restricted to data typed as "interface{}" or "map[string]interface{}". If DocumentType is + // set to a type that a BSON document cannot be unmarshaled into (e.g. "string"), unmarshalling will result in an + // error. DocumentType overrides the Ancestor field. + defaultDocumentType reflect.Type + + binaryAsSlice bool + useJSONStructTags bool + useLocalTimeZone bool + zeroMaps bool + zeroStructs bool +} + +// BinaryAsSlice causes the Decoder to unmarshal BSON binary field values that are the "Generic" or +// "Old" BSON binary subtype as a Go byte slice instead of a primitive.Binary. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.BinaryAsSlice] instead. +func (dc *DecodeContext) BinaryAsSlice() { + dc.binaryAsSlice = true +} + +// UseJSONStructTags causes the Decoder to fall back to using the "json" struct tag if a "bson" +// struct tag is not specified. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.UseJSONStructTags] instead. +func (dc *DecodeContext) UseJSONStructTags() { + dc.useJSONStructTags = true +} + +// UseLocalTimeZone causes the Decoder to unmarshal time.Time values in the local timezone instead +// of the UTC timezone. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.UseLocalTimeZone] instead. +func (dc *DecodeContext) UseLocalTimeZone() { + dc.useLocalTimeZone = true +} + +// ZeroMaps causes the Decoder to delete any existing values from Go maps in the destination value +// passed to Decode before unmarshaling BSON documents into them. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.ZeroMaps] instead. +func (dc *DecodeContext) ZeroMaps() { + dc.zeroMaps = true +} + +// ZeroStructs causes the Decoder to delete any existing values from Go structs in the destination +// value passed to Decode before unmarshaling BSON documents into them. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.ZeroStructs] instead. +func (dc *DecodeContext) ZeroStructs() { + dc.zeroStructs = true +} + +// DefaultDocumentM causes the Decoder to always unmarshal documents into the primitive.M type. This +// behavior is restricted to data typed as "interface{}" or "map[string]interface{}". +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.DefaultDocumentM] instead. +func (dc *DecodeContext) DefaultDocumentM() { + dc.defaultDocumentType = reflect.TypeOf(primitive.M{}) +} + +// DefaultDocumentD causes the Decoder to always unmarshal documents into the primitive.D type. This +// behavior is restricted to data typed as "interface{}" or "map[string]interface{}". +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.DefaultDocumentD] instead. +func (dc *DecodeContext) DefaultDocumentD() { + dc.defaultDocumentType = reflect.TypeOf(primitive.D{}) +} + +// ValueCodec is an interface for encoding and decoding a reflect.Value. +// values. +// +// Deprecated: Use [ValueEncoder] and [ValueDecoder] instead. +type ValueCodec interface { + ValueEncoder + ValueDecoder +} + +// ValueEncoder is the interface implemented by types that can handle the encoding of a value. +type ValueEncoder interface { + EncodeValue(EncodeContext, bsonrw.ValueWriter, reflect.Value) error +} + +// ValueEncoderFunc is an adapter function that allows a function with the correct signature to be +// used as a ValueEncoder. +type ValueEncoderFunc func(EncodeContext, bsonrw.ValueWriter, reflect.Value) error + +// EncodeValue implements the ValueEncoder interface. +func (fn ValueEncoderFunc) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + return fn(ec, vw, val) +} + +// ValueDecoder is the interface implemented by types that can handle the decoding of a value. +type ValueDecoder interface { + DecodeValue(DecodeContext, bsonrw.ValueReader, reflect.Value) error +} + +// ValueDecoderFunc is an adapter function that allows a function with the correct signature to be +// used as a ValueDecoder. +type ValueDecoderFunc func(DecodeContext, bsonrw.ValueReader, reflect.Value) error + +// DecodeValue implements the ValueDecoder interface. +func (fn ValueDecoderFunc) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + return fn(dc, vr, val) +} + +// typeDecoder is the interface implemented by types that can handle the decoding of a value given its type. +type typeDecoder interface { + decodeType(DecodeContext, bsonrw.ValueReader, reflect.Type) (reflect.Value, error) +} + +// typeDecoderFunc is an adapter function that allows a function with the correct signature to be used as a typeDecoder. +type typeDecoderFunc func(DecodeContext, bsonrw.ValueReader, reflect.Type) (reflect.Value, error) + +func (fn typeDecoderFunc) decodeType(dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + return fn(dc, vr, t) +} + +// decodeAdapter allows two functions with the correct signatures to be used as both a ValueDecoder and typeDecoder. +type decodeAdapter struct { + ValueDecoderFunc + typeDecoderFunc +} + +var _ ValueDecoder = decodeAdapter{} +var _ typeDecoder = decodeAdapter{} + +// decodeTypeOrValue calls decoder.decodeType is decoder is a typeDecoder. Otherwise, it allocates a new element of type +// t and calls decoder.DecodeValue on it. +func decodeTypeOrValue(decoder ValueDecoder, dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + td, _ := decoder.(typeDecoder) + return decodeTypeOrValueWithInfo(decoder, td, dc, vr, t, true) +} + +func decodeTypeOrValueWithInfo(vd ValueDecoder, td typeDecoder, dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type, convert bool) (reflect.Value, error) { + if td != nil { + val, err := td.decodeType(dc, vr, t) + if err == nil && convert && val.Type() != t { + // This conversion step is necessary for slices and maps. If a user declares variables like: + // + // type myBool bool + // var m map[string]myBool + // + // and tries to decode BSON bytes into the map, the decoding will fail if this conversion is not present + // because we'll try to assign a value of type bool to one of type myBool. + val = val.Convert(t) + } + return val, err + } + + val := reflect.New(t).Elem() + err := vd.DecodeValue(dc, vr, val) + return val, err +} + +// CodecZeroer is the interface implemented by Codecs that can also determine if +// a value of the type that would be encoded is zero. +// +// Deprecated: Defining custom rules for the zero/empty value will not be supported in Go Driver +// 2.0. Users who want to omit empty complex values should use a pointer field and set the value to +// nil instead. +type CodecZeroer interface { + IsTypeZero(interface{}) bool +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/byte_slice_codec.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/byte_slice_codec.go new file mode 100644 index 0000000000..0134b5a94b --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/byte_slice_codec.go @@ -0,0 +1,138 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "fmt" + "reflect" + + "go.mongodb.org/mongo-driver/bson/bsonoptions" + "go.mongodb.org/mongo-driver/bson/bsonrw" + "go.mongodb.org/mongo-driver/bson/bsontype" +) + +// ByteSliceCodec is the Codec used for []byte values. +// +// Deprecated: ByteSliceCodec will not be directly configurable in Go Driver +// 2.0. To configure the byte slice encode and decode behavior, use the +// configuration methods on a [go.mongodb.org/mongo-driver/bson.Encoder] or +// [go.mongodb.org/mongo-driver/bson.Decoder]. To configure the byte slice +// encode and decode behavior for a mongo.Client, use +// [go.mongodb.org/mongo-driver/mongo/options.ClientOptions.SetBSONOptions]. +// +// For example, to configure a mongo.Client to encode nil byte slices as empty +// BSON binary values, use: +// +// opt := options.Client().SetBSONOptions(&options.BSONOptions{ +// NilByteSliceAsEmpty: true, +// }) +// +// See the deprecation notice for each field in ByteSliceCodec for the +// corresponding settings. +type ByteSliceCodec struct { + // EncodeNilAsEmpty causes EncodeValue to marshal nil Go byte slices as empty BSON binary values + // instead of BSON null. + // + // Deprecated: Use bson.Encoder.NilByteSliceAsEmpty or options.BSONOptions.NilByteSliceAsEmpty + // instead. + EncodeNilAsEmpty bool +} + +var ( + defaultByteSliceCodec = NewByteSliceCodec() + + // Assert that defaultByteSliceCodec satisfies the typeDecoder interface, which allows it to be + // used by collection type decoders (e.g. map, slice, etc) to set individual values in a + // collection. + _ typeDecoder = defaultByteSliceCodec +) + +// NewByteSliceCodec returns a ByteSliceCodec with options opts. +// +// Deprecated: NewByteSliceCodec will not be available in Go Driver 2.0. See +// [ByteSliceCodec] for more details. +func NewByteSliceCodec(opts ...*bsonoptions.ByteSliceCodecOptions) *ByteSliceCodec { + byteSliceOpt := bsonoptions.MergeByteSliceCodecOptions(opts...) + codec := ByteSliceCodec{} + if byteSliceOpt.EncodeNilAsEmpty != nil { + codec.EncodeNilAsEmpty = *byteSliceOpt.EncodeNilAsEmpty + } + return &codec +} + +// EncodeValue is the ValueEncoder for []byte. +func (bsc *ByteSliceCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tByteSlice { + return ValueEncoderError{Name: "ByteSliceEncodeValue", Types: []reflect.Type{tByteSlice}, Received: val} + } + if val.IsNil() && !bsc.EncodeNilAsEmpty && !ec.nilByteSliceAsEmpty { + return vw.WriteNull() + } + return vw.WriteBinary(val.Interface().([]byte)) +} + +func (bsc *ByteSliceCodec) decodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tByteSlice { + return emptyValue, ValueDecoderError{ + Name: "ByteSliceDecodeValue", + Types: []reflect.Type{tByteSlice}, + Received: reflect.Zero(t), + } + } + + var data []byte + var err error + switch vrType := vr.Type(); vrType { + case bsontype.String: + str, err := vr.ReadString() + if err != nil { + return emptyValue, err + } + data = []byte(str) + case bsontype.Symbol: + sym, err := vr.ReadSymbol() + if err != nil { + return emptyValue, err + } + data = []byte(sym) + case bsontype.Binary: + var subtype byte + data, subtype, err = vr.ReadBinary() + if err != nil { + return emptyValue, err + } + if subtype != bsontype.BinaryGeneric && subtype != bsontype.BinaryBinaryOld { + return emptyValue, decodeBinaryError{subtype: subtype, typeName: "[]byte"} + } + case bsontype.Null: + err = vr.ReadNull() + case bsontype.Undefined: + err = vr.ReadUndefined() + default: + return emptyValue, fmt.Errorf("cannot decode %v into a []byte", vrType) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(data), nil +} + +// DecodeValue is the ValueDecoder for []byte. +func (bsc *ByteSliceCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tByteSlice { + return ValueDecoderError{Name: "ByteSliceDecodeValue", Types: []reflect.Type{tByteSlice}, Received: val} + } + + elem, err := bsc.decodeType(dc, vr, tByteSlice) + if err != nil { + return err + } + + val.Set(elem) + return nil +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/codec_cache.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/codec_cache.go new file mode 100644 index 0000000000..844b50299f --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/codec_cache.go @@ -0,0 +1,166 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "reflect" + "sync" + "sync/atomic" +) + +// Runtime check that the kind encoder and decoder caches can store any valid +// reflect.Kind constant. +func init() { + if s := reflect.Kind(len(kindEncoderCache{}.entries)).String(); s != "kind27" { + panic("The capacity of kindEncoderCache is too small.\n" + + "This is due to a new type being added to reflect.Kind.") + } +} + +// statically assert array size +var _ = (kindEncoderCache{}).entries[reflect.UnsafePointer] +var _ = (kindDecoderCache{}).entries[reflect.UnsafePointer] + +type typeEncoderCache struct { + cache sync.Map // map[reflect.Type]ValueEncoder +} + +func (c *typeEncoderCache) Store(rt reflect.Type, enc ValueEncoder) { + c.cache.Store(rt, enc) +} + +func (c *typeEncoderCache) Load(rt reflect.Type) (ValueEncoder, bool) { + if v, _ := c.cache.Load(rt); v != nil { + return v.(ValueEncoder), true + } + return nil, false +} + +func (c *typeEncoderCache) LoadOrStore(rt reflect.Type, enc ValueEncoder) ValueEncoder { + if v, loaded := c.cache.LoadOrStore(rt, enc); loaded { + enc = v.(ValueEncoder) + } + return enc +} + +func (c *typeEncoderCache) Clone() *typeEncoderCache { + cc := new(typeEncoderCache) + c.cache.Range(func(k, v interface{}) bool { + if k != nil && v != nil { + cc.cache.Store(k, v) + } + return true + }) + return cc +} + +type typeDecoderCache struct { + cache sync.Map // map[reflect.Type]ValueDecoder +} + +func (c *typeDecoderCache) Store(rt reflect.Type, dec ValueDecoder) { + c.cache.Store(rt, dec) +} + +func (c *typeDecoderCache) Load(rt reflect.Type) (ValueDecoder, bool) { + if v, _ := c.cache.Load(rt); v != nil { + return v.(ValueDecoder), true + } + return nil, false +} + +func (c *typeDecoderCache) LoadOrStore(rt reflect.Type, dec ValueDecoder) ValueDecoder { + if v, loaded := c.cache.LoadOrStore(rt, dec); loaded { + dec = v.(ValueDecoder) + } + return dec +} + +func (c *typeDecoderCache) Clone() *typeDecoderCache { + cc := new(typeDecoderCache) + c.cache.Range(func(k, v interface{}) bool { + if k != nil && v != nil { + cc.cache.Store(k, v) + } + return true + }) + return cc +} + +// atomic.Value requires that all calls to Store() have the same concrete type +// so we wrap the ValueEncoder with a kindEncoderCacheEntry to ensure the type +// is always the same (since different concrete types may implement the +// ValueEncoder interface). +type kindEncoderCacheEntry struct { + enc ValueEncoder +} + +type kindEncoderCache struct { + entries [reflect.UnsafePointer + 1]atomic.Value // *kindEncoderCacheEntry +} + +func (c *kindEncoderCache) Store(rt reflect.Kind, enc ValueEncoder) { + if enc != nil && rt < reflect.Kind(len(c.entries)) { + c.entries[rt].Store(&kindEncoderCacheEntry{enc: enc}) + } +} + +func (c *kindEncoderCache) Load(rt reflect.Kind) (ValueEncoder, bool) { + if rt < reflect.Kind(len(c.entries)) { + if ent, ok := c.entries[rt].Load().(*kindEncoderCacheEntry); ok { + return ent.enc, ent.enc != nil + } + } + return nil, false +} + +func (c *kindEncoderCache) Clone() *kindEncoderCache { + cc := new(kindEncoderCache) + for i, v := range c.entries { + if val := v.Load(); val != nil { + cc.entries[i].Store(val) + } + } + return cc +} + +// atomic.Value requires that all calls to Store() have the same concrete type +// so we wrap the ValueDecoder with a kindDecoderCacheEntry to ensure the type +// is always the same (since different concrete types may implement the +// ValueDecoder interface). +type kindDecoderCacheEntry struct { + dec ValueDecoder +} + +type kindDecoderCache struct { + entries [reflect.UnsafePointer + 1]atomic.Value // *kindDecoderCacheEntry +} + +func (c *kindDecoderCache) Store(rt reflect.Kind, dec ValueDecoder) { + if rt < reflect.Kind(len(c.entries)) { + c.entries[rt].Store(&kindDecoderCacheEntry{dec: dec}) + } +} + +func (c *kindDecoderCache) Load(rt reflect.Kind) (ValueDecoder, bool) { + if rt < reflect.Kind(len(c.entries)) { + if ent, ok := c.entries[rt].Load().(*kindDecoderCacheEntry); ok { + return ent.dec, ent.dec != nil + } + } + return nil, false +} + +func (c *kindDecoderCache) Clone() *kindDecoderCache { + cc := new(kindDecoderCache) + for i, v := range c.entries { + if val := v.Load(); val != nil { + cc.entries[i].Store(val) + } + } + return cc +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/cond_addr_codec.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/cond_addr_codec.go new file mode 100644 index 0000000000..cb8180f25c --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/cond_addr_codec.go @@ -0,0 +1,63 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "reflect" + + "go.mongodb.org/mongo-driver/bson/bsonrw" +) + +// condAddrEncoder is the encoder used when a pointer to the encoding value has an encoder. +type condAddrEncoder struct { + canAddrEnc ValueEncoder + elseEnc ValueEncoder +} + +var _ ValueEncoder = (*condAddrEncoder)(nil) + +// newCondAddrEncoder returns an condAddrEncoder. +func newCondAddrEncoder(canAddrEnc, elseEnc ValueEncoder) *condAddrEncoder { + encoder := condAddrEncoder{canAddrEnc: canAddrEnc, elseEnc: elseEnc} + return &encoder +} + +// EncodeValue is the ValueEncoderFunc for a value that may be addressable. +func (cae *condAddrEncoder) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if val.CanAddr() { + return cae.canAddrEnc.EncodeValue(ec, vw, val) + } + if cae.elseEnc != nil { + return cae.elseEnc.EncodeValue(ec, vw, val) + } + return ErrNoEncoder{Type: val.Type()} +} + +// condAddrDecoder is the decoder used when a pointer to the value has a decoder. +type condAddrDecoder struct { + canAddrDec ValueDecoder + elseDec ValueDecoder +} + +var _ ValueDecoder = (*condAddrDecoder)(nil) + +// newCondAddrDecoder returns an CondAddrDecoder. +func newCondAddrDecoder(canAddrDec, elseDec ValueDecoder) *condAddrDecoder { + decoder := condAddrDecoder{canAddrDec: canAddrDec, elseDec: elseDec} + return &decoder +} + +// DecodeValue is the ValueDecoderFunc for a value that may be addressable. +func (cad *condAddrDecoder) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if val.CanAddr() { + return cad.canAddrDec.DecodeValue(dc, vr, val) + } + if cad.elseDec != nil { + return cad.elseDec.DecodeValue(dc, vr, val) + } + return ErrNoDecoder{Type: val.Type()} +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_decoders.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_decoders.go new file mode 100644 index 0000000000..8702d6d39e --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_decoders.go @@ -0,0 +1,1819 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "encoding/json" + "errors" + "fmt" + "math" + "net/url" + "reflect" + "strconv" + "time" + + "go.mongodb.org/mongo-driver/bson/bsonrw" + "go.mongodb.org/mongo-driver/bson/bsontype" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" +) + +var ( + defaultValueDecoders DefaultValueDecoders + errCannotTruncate = errors.New("float64 can only be truncated to a lower precision type when truncation is enabled") +) + +type decodeBinaryError struct { + subtype byte + typeName string +} + +func (d decodeBinaryError) Error() string { + return fmt.Sprintf("only binary values with subtype 0x00 or 0x02 can be decoded into %s, but got subtype %v", d.typeName, d.subtype) +} + +func newDefaultStructCodec() *StructCodec { + codec, err := NewStructCodec(DefaultStructTagParser) + if err != nil { + // This function is called from the codec registration path, so errors can't be propagated. If there's an error + // constructing the StructCodec, we panic to avoid losing it. + panic(fmt.Errorf("error creating default StructCodec: %w", err)) + } + return codec +} + +// DefaultValueDecoders is a namespace type for the default ValueDecoders used +// when creating a registry. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +type DefaultValueDecoders struct{} + +// RegisterDefaultDecoders will register the decoder methods attached to DefaultValueDecoders with +// the provided RegistryBuilder. +// +// There is no support for decoding map[string]interface{} because there is no decoder for +// interface{}, so users must either register this decoder themselves or use the +// EmptyInterfaceDecoder available in the bson package. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) RegisterDefaultDecoders(rb *RegistryBuilder) { + if rb == nil { + panic(errors.New("argument to RegisterDefaultDecoders must not be nil")) + } + + intDecoder := decodeAdapter{dvd.IntDecodeValue, dvd.intDecodeType} + floatDecoder := decodeAdapter{dvd.FloatDecodeValue, dvd.floatDecodeType} + + rb. + RegisterTypeDecoder(tD, ValueDecoderFunc(dvd.DDecodeValue)). + RegisterTypeDecoder(tBinary, decodeAdapter{dvd.BinaryDecodeValue, dvd.binaryDecodeType}). + RegisterTypeDecoder(tUndefined, decodeAdapter{dvd.UndefinedDecodeValue, dvd.undefinedDecodeType}). + RegisterTypeDecoder(tDateTime, decodeAdapter{dvd.DateTimeDecodeValue, dvd.dateTimeDecodeType}). + RegisterTypeDecoder(tNull, decodeAdapter{dvd.NullDecodeValue, dvd.nullDecodeType}). + RegisterTypeDecoder(tRegex, decodeAdapter{dvd.RegexDecodeValue, dvd.regexDecodeType}). + RegisterTypeDecoder(tDBPointer, decodeAdapter{dvd.DBPointerDecodeValue, dvd.dBPointerDecodeType}). + RegisterTypeDecoder(tTimestamp, decodeAdapter{dvd.TimestampDecodeValue, dvd.timestampDecodeType}). + RegisterTypeDecoder(tMinKey, decodeAdapter{dvd.MinKeyDecodeValue, dvd.minKeyDecodeType}). + RegisterTypeDecoder(tMaxKey, decodeAdapter{dvd.MaxKeyDecodeValue, dvd.maxKeyDecodeType}). + RegisterTypeDecoder(tJavaScript, decodeAdapter{dvd.JavaScriptDecodeValue, dvd.javaScriptDecodeType}). + RegisterTypeDecoder(tSymbol, decodeAdapter{dvd.SymbolDecodeValue, dvd.symbolDecodeType}). + RegisterTypeDecoder(tByteSlice, defaultByteSliceCodec). + RegisterTypeDecoder(tTime, defaultTimeCodec). + RegisterTypeDecoder(tEmpty, defaultEmptyInterfaceCodec). + RegisterTypeDecoder(tCoreArray, defaultArrayCodec). + RegisterTypeDecoder(tOID, decodeAdapter{dvd.ObjectIDDecodeValue, dvd.objectIDDecodeType}). + RegisterTypeDecoder(tDecimal, decodeAdapter{dvd.Decimal128DecodeValue, dvd.decimal128DecodeType}). + RegisterTypeDecoder(tJSONNumber, decodeAdapter{dvd.JSONNumberDecodeValue, dvd.jsonNumberDecodeType}). + RegisterTypeDecoder(tURL, decodeAdapter{dvd.URLDecodeValue, dvd.urlDecodeType}). + RegisterTypeDecoder(tCoreDocument, ValueDecoderFunc(dvd.CoreDocumentDecodeValue)). + RegisterTypeDecoder(tCodeWithScope, decodeAdapter{dvd.CodeWithScopeDecodeValue, dvd.codeWithScopeDecodeType}). + RegisterDefaultDecoder(reflect.Bool, decodeAdapter{dvd.BooleanDecodeValue, dvd.booleanDecodeType}). + RegisterDefaultDecoder(reflect.Int, intDecoder). + RegisterDefaultDecoder(reflect.Int8, intDecoder). + RegisterDefaultDecoder(reflect.Int16, intDecoder). + RegisterDefaultDecoder(reflect.Int32, intDecoder). + RegisterDefaultDecoder(reflect.Int64, intDecoder). + RegisterDefaultDecoder(reflect.Uint, defaultUIntCodec). + RegisterDefaultDecoder(reflect.Uint8, defaultUIntCodec). + RegisterDefaultDecoder(reflect.Uint16, defaultUIntCodec). + RegisterDefaultDecoder(reflect.Uint32, defaultUIntCodec). + RegisterDefaultDecoder(reflect.Uint64, defaultUIntCodec). + RegisterDefaultDecoder(reflect.Float32, floatDecoder). + RegisterDefaultDecoder(reflect.Float64, floatDecoder). + RegisterDefaultDecoder(reflect.Array, ValueDecoderFunc(dvd.ArrayDecodeValue)). + RegisterDefaultDecoder(reflect.Map, defaultMapCodec). + RegisterDefaultDecoder(reflect.Slice, defaultSliceCodec). + RegisterDefaultDecoder(reflect.String, defaultStringCodec). + RegisterDefaultDecoder(reflect.Struct, newDefaultStructCodec()). + RegisterDefaultDecoder(reflect.Ptr, NewPointerCodec()). + RegisterTypeMapEntry(bsontype.Double, tFloat64). + RegisterTypeMapEntry(bsontype.String, tString). + RegisterTypeMapEntry(bsontype.Array, tA). + RegisterTypeMapEntry(bsontype.Binary, tBinary). + RegisterTypeMapEntry(bsontype.Undefined, tUndefined). + RegisterTypeMapEntry(bsontype.ObjectID, tOID). + RegisterTypeMapEntry(bsontype.Boolean, tBool). + RegisterTypeMapEntry(bsontype.DateTime, tDateTime). + RegisterTypeMapEntry(bsontype.Regex, tRegex). + RegisterTypeMapEntry(bsontype.DBPointer, tDBPointer). + RegisterTypeMapEntry(bsontype.JavaScript, tJavaScript). + RegisterTypeMapEntry(bsontype.Symbol, tSymbol). + RegisterTypeMapEntry(bsontype.CodeWithScope, tCodeWithScope). + RegisterTypeMapEntry(bsontype.Int32, tInt32). + RegisterTypeMapEntry(bsontype.Int64, tInt64). + RegisterTypeMapEntry(bsontype.Timestamp, tTimestamp). + RegisterTypeMapEntry(bsontype.Decimal128, tDecimal). + RegisterTypeMapEntry(bsontype.MinKey, tMinKey). + RegisterTypeMapEntry(bsontype.MaxKey, tMaxKey). + RegisterTypeMapEntry(bsontype.Type(0), tD). + RegisterTypeMapEntry(bsontype.EmbeddedDocument, tD). + RegisterHookDecoder(tValueUnmarshaler, ValueDecoderFunc(dvd.ValueUnmarshalerDecodeValue)). + RegisterHookDecoder(tUnmarshaler, ValueDecoderFunc(dvd.UnmarshalerDecodeValue)) +} + +// DDecodeValue is the ValueDecoderFunc for primitive.D instances. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) DDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.IsValid() || !val.CanSet() || val.Type() != tD { + return ValueDecoderError{Name: "DDecodeValue", Kinds: []reflect.Kind{reflect.Slice}, Received: val} + } + + switch vrType := vr.Type(); vrType { + case bsontype.Type(0), bsontype.EmbeddedDocument: + dc.Ancestor = tD + case bsontype.Null: + val.Set(reflect.Zero(val.Type())) + return vr.ReadNull() + default: + return fmt.Errorf("cannot decode %v into a primitive.D", vrType) + } + + dr, err := vr.ReadDocument() + if err != nil { + return err + } + + decoder, err := dc.LookupDecoder(tEmpty) + if err != nil { + return err + } + tEmptyTypeDecoder, _ := decoder.(typeDecoder) + + // Use the elements in the provided value if it's non nil. Otherwise, allocate a new D instance. + var elems primitive.D + if !val.IsNil() { + val.SetLen(0) + elems = val.Interface().(primitive.D) + } else { + elems = make(primitive.D, 0) + } + + for { + key, elemVr, err := dr.ReadElement() + if errors.Is(err, bsonrw.ErrEOD) { + break + } else if err != nil { + return err + } + + // Pass false for convert because we don't need to call reflect.Value.Convert for tEmpty. + elem, err := decodeTypeOrValueWithInfo(decoder, tEmptyTypeDecoder, dc, elemVr, tEmpty, false) + if err != nil { + return err + } + + elems = append(elems, primitive.E{Key: key, Value: elem.Interface()}) + } + + val.Set(reflect.ValueOf(elems)) + return nil +} + +func (dvd DefaultValueDecoders) booleanDecodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t.Kind() != reflect.Bool { + return emptyValue, ValueDecoderError{ + Name: "BooleanDecodeValue", + Kinds: []reflect.Kind{reflect.Bool}, + Received: reflect.Zero(t), + } + } + + var b bool + var err error + switch vrType := vr.Type(); vrType { + case bsontype.Int32: + i32, err := vr.ReadInt32() + if err != nil { + return emptyValue, err + } + b = (i32 != 0) + case bsontype.Int64: + i64, err := vr.ReadInt64() + if err != nil { + return emptyValue, err + } + b = (i64 != 0) + case bsontype.Double: + f64, err := vr.ReadDouble() + if err != nil { + return emptyValue, err + } + b = (f64 != 0) + case bsontype.Boolean: + b, err = vr.ReadBoolean() + case bsontype.Null: + err = vr.ReadNull() + case bsontype.Undefined: + err = vr.ReadUndefined() + default: + return emptyValue, fmt.Errorf("cannot decode %v into a boolean", vrType) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(b), nil +} + +// BooleanDecodeValue is the ValueDecoderFunc for bool types. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) BooleanDecodeValue(dctx DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.IsValid() || !val.CanSet() || val.Kind() != reflect.Bool { + return ValueDecoderError{Name: "BooleanDecodeValue", Kinds: []reflect.Kind{reflect.Bool}, Received: val} + } + + elem, err := dvd.booleanDecodeType(dctx, vr, val.Type()) + if err != nil { + return err + } + + val.SetBool(elem.Bool()) + return nil +} + +func (DefaultValueDecoders) intDecodeType(dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + var i64 int64 + var err error + switch vrType := vr.Type(); vrType { + case bsontype.Int32: + i32, err := vr.ReadInt32() + if err != nil { + return emptyValue, err + } + i64 = int64(i32) + case bsontype.Int64: + i64, err = vr.ReadInt64() + if err != nil { + return emptyValue, err + } + case bsontype.Double: + f64, err := vr.ReadDouble() + if err != nil { + return emptyValue, err + } + if !dc.Truncate && math.Floor(f64) != f64 { + return emptyValue, errCannotTruncate + } + if f64 > float64(math.MaxInt64) { + return emptyValue, fmt.Errorf("%g overflows int64", f64) + } + i64 = int64(f64) + case bsontype.Boolean: + b, err := vr.ReadBoolean() + if err != nil { + return emptyValue, err + } + if b { + i64 = 1 + } + case bsontype.Null: + if err = vr.ReadNull(); err != nil { + return emptyValue, err + } + case bsontype.Undefined: + if err = vr.ReadUndefined(); err != nil { + return emptyValue, err + } + default: + return emptyValue, fmt.Errorf("cannot decode %v into an integer type", vrType) + } + + switch t.Kind() { + case reflect.Int8: + if i64 < math.MinInt8 || i64 > math.MaxInt8 { + return emptyValue, fmt.Errorf("%d overflows int8", i64) + } + + return reflect.ValueOf(int8(i64)), nil + case reflect.Int16: + if i64 < math.MinInt16 || i64 > math.MaxInt16 { + return emptyValue, fmt.Errorf("%d overflows int16", i64) + } + + return reflect.ValueOf(int16(i64)), nil + case reflect.Int32: + if i64 < math.MinInt32 || i64 > math.MaxInt32 { + return emptyValue, fmt.Errorf("%d overflows int32", i64) + } + + return reflect.ValueOf(int32(i64)), nil + case reflect.Int64: + return reflect.ValueOf(i64), nil + case reflect.Int: + if i64 > math.MaxInt { // Can we fit this inside of an int + return emptyValue, fmt.Errorf("%d overflows int", i64) + } + + return reflect.ValueOf(int(i64)), nil + default: + return emptyValue, ValueDecoderError{ + Name: "IntDecodeValue", + Kinds: []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int}, + Received: reflect.Zero(t), + } + } +} + +// IntDecodeValue is the ValueDecoderFunc for int types. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) IntDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() { + return ValueDecoderError{ + Name: "IntDecodeValue", + Kinds: []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int}, + Received: val, + } + } + + elem, err := dvd.intDecodeType(dc, vr, val.Type()) + if err != nil { + return err + } + + val.SetInt(elem.Int()) + return nil +} + +// UintDecodeValue is the ValueDecoderFunc for uint types. +// +// Deprecated: UintDecodeValue is not registered by default. Use UintCodec.DecodeValue instead. +func (dvd DefaultValueDecoders) UintDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + var i64 int64 + var err error + switch vr.Type() { + case bsontype.Int32: + i32, err := vr.ReadInt32() + if err != nil { + return err + } + i64 = int64(i32) + case bsontype.Int64: + i64, err = vr.ReadInt64() + if err != nil { + return err + } + case bsontype.Double: + f64, err := vr.ReadDouble() + if err != nil { + return err + } + if !dc.Truncate && math.Floor(f64) != f64 { + return errors.New("UintDecodeValue can only truncate float64 to an integer type when truncation is enabled") + } + if f64 > float64(math.MaxInt64) { + return fmt.Errorf("%g overflows int64", f64) + } + i64 = int64(f64) + case bsontype.Boolean: + b, err := vr.ReadBoolean() + if err != nil { + return err + } + if b { + i64 = 1 + } + default: + return fmt.Errorf("cannot decode %v into an integer type", vr.Type()) + } + + if !val.CanSet() { + return ValueDecoderError{ + Name: "UintDecodeValue", + Kinds: []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint}, + Received: val, + } + } + + switch val.Kind() { + case reflect.Uint8: + if i64 < 0 || i64 > math.MaxUint8 { + return fmt.Errorf("%d overflows uint8", i64) + } + case reflect.Uint16: + if i64 < 0 || i64 > math.MaxUint16 { + return fmt.Errorf("%d overflows uint16", i64) + } + case reflect.Uint32: + if i64 < 0 || i64 > math.MaxUint32 { + return fmt.Errorf("%d overflows uint32", i64) + } + case reflect.Uint64: + if i64 < 0 { + return fmt.Errorf("%d overflows uint64", i64) + } + case reflect.Uint: + if i64 < 0 || uint64(i64) > uint64(math.MaxUint) { // Can we fit this inside of an uint + return fmt.Errorf("%d overflows uint", i64) + } + default: + return ValueDecoderError{ + Name: "UintDecodeValue", + Kinds: []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint}, + Received: val, + } + } + + val.SetUint(uint64(i64)) + return nil +} + +func (dvd DefaultValueDecoders) floatDecodeType(dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + var f float64 + var err error + switch vrType := vr.Type(); vrType { + case bsontype.Int32: + i32, err := vr.ReadInt32() + if err != nil { + return emptyValue, err + } + f = float64(i32) + case bsontype.Int64: + i64, err := vr.ReadInt64() + if err != nil { + return emptyValue, err + } + f = float64(i64) + case bsontype.Double: + f, err = vr.ReadDouble() + if err != nil { + return emptyValue, err + } + case bsontype.Boolean: + b, err := vr.ReadBoolean() + if err != nil { + return emptyValue, err + } + if b { + f = 1 + } + case bsontype.Null: + if err = vr.ReadNull(); err != nil { + return emptyValue, err + } + case bsontype.Undefined: + if err = vr.ReadUndefined(); err != nil { + return emptyValue, err + } + default: + return emptyValue, fmt.Errorf("cannot decode %v into a float32 or float64 type", vrType) + } + + switch t.Kind() { + case reflect.Float32: + if !dc.Truncate && float64(float32(f)) != f { + return emptyValue, errCannotTruncate + } + + return reflect.ValueOf(float32(f)), nil + case reflect.Float64: + return reflect.ValueOf(f), nil + default: + return emptyValue, ValueDecoderError{ + Name: "FloatDecodeValue", + Kinds: []reflect.Kind{reflect.Float32, reflect.Float64}, + Received: reflect.Zero(t), + } + } +} + +// FloatDecodeValue is the ValueDecoderFunc for float types. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) FloatDecodeValue(ec DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() { + return ValueDecoderError{ + Name: "FloatDecodeValue", + Kinds: []reflect.Kind{reflect.Float32, reflect.Float64}, + Received: val, + } + } + + elem, err := dvd.floatDecodeType(ec, vr, val.Type()) + if err != nil { + return err + } + + val.SetFloat(elem.Float()) + return nil +} + +// StringDecodeValue is the ValueDecoderFunc for string types. +// +// Deprecated: StringDecodeValue is not registered by default. Use StringCodec.DecodeValue instead. +func (dvd DefaultValueDecoders) StringDecodeValue(_ DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + var str string + var err error + switch vr.Type() { + // TODO(GODRIVER-577): Handle JavaScript and Symbol BSON types when allowed. + case bsontype.String: + str, err = vr.ReadString() + if err != nil { + return err + } + default: + return fmt.Errorf("cannot decode %v into a string type", vr.Type()) + } + if !val.CanSet() || val.Kind() != reflect.String { + return ValueDecoderError{Name: "StringDecodeValue", Kinds: []reflect.Kind{reflect.String}, Received: val} + } + + val.SetString(str) + return nil +} + +func (DefaultValueDecoders) javaScriptDecodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tJavaScript { + return emptyValue, ValueDecoderError{ + Name: "JavaScriptDecodeValue", + Types: []reflect.Type{tJavaScript}, + Received: reflect.Zero(t), + } + } + + var js string + var err error + switch vrType := vr.Type(); vrType { + case bsontype.JavaScript: + js, err = vr.ReadJavascript() + case bsontype.Null: + err = vr.ReadNull() + case bsontype.Undefined: + err = vr.ReadUndefined() + default: + return emptyValue, fmt.Errorf("cannot decode %v into a primitive.JavaScript", vrType) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(primitive.JavaScript(js)), nil +} + +// JavaScriptDecodeValue is the ValueDecoderFunc for the primitive.JavaScript type. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) JavaScriptDecodeValue(dctx DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tJavaScript { + return ValueDecoderError{Name: "JavaScriptDecodeValue", Types: []reflect.Type{tJavaScript}, Received: val} + } + + elem, err := dvd.javaScriptDecodeType(dctx, vr, tJavaScript) + if err != nil { + return err + } + + val.SetString(elem.String()) + return nil +} + +func (DefaultValueDecoders) symbolDecodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tSymbol { + return emptyValue, ValueDecoderError{ + Name: "SymbolDecodeValue", + Types: []reflect.Type{tSymbol}, + Received: reflect.Zero(t), + } + } + + var symbol string + var err error + switch vrType := vr.Type(); vrType { + case bsontype.String: + symbol, err = vr.ReadString() + case bsontype.Symbol: + symbol, err = vr.ReadSymbol() + case bsontype.Binary: + data, subtype, err := vr.ReadBinary() + if err != nil { + return emptyValue, err + } + + if subtype != bsontype.BinaryGeneric && subtype != bsontype.BinaryBinaryOld { + return emptyValue, decodeBinaryError{subtype: subtype, typeName: "primitive.Symbol"} + } + symbol = string(data) + case bsontype.Null: + err = vr.ReadNull() + case bsontype.Undefined: + err = vr.ReadUndefined() + default: + return emptyValue, fmt.Errorf("cannot decode %v into a primitive.Symbol", vrType) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(primitive.Symbol(symbol)), nil +} + +// SymbolDecodeValue is the ValueDecoderFunc for the primitive.Symbol type. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) SymbolDecodeValue(dctx DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tSymbol { + return ValueDecoderError{Name: "SymbolDecodeValue", Types: []reflect.Type{tSymbol}, Received: val} + } + + elem, err := dvd.symbolDecodeType(dctx, vr, tSymbol) + if err != nil { + return err + } + + val.SetString(elem.String()) + return nil +} + +func (DefaultValueDecoders) binaryDecodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tBinary { + return emptyValue, ValueDecoderError{ + Name: "BinaryDecodeValue", + Types: []reflect.Type{tBinary}, + Received: reflect.Zero(t), + } + } + + var data []byte + var subtype byte + var err error + switch vrType := vr.Type(); vrType { + case bsontype.Binary: + data, subtype, err = vr.ReadBinary() + case bsontype.Null: + err = vr.ReadNull() + case bsontype.Undefined: + err = vr.ReadUndefined() + default: + return emptyValue, fmt.Errorf("cannot decode %v into a Binary", vrType) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(primitive.Binary{Subtype: subtype, Data: data}), nil +} + +// BinaryDecodeValue is the ValueDecoderFunc for Binary. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) BinaryDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tBinary { + return ValueDecoderError{Name: "BinaryDecodeValue", Types: []reflect.Type{tBinary}, Received: val} + } + + elem, err := dvd.binaryDecodeType(dc, vr, tBinary) + if err != nil { + return err + } + + val.Set(elem) + return nil +} + +func (DefaultValueDecoders) undefinedDecodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tUndefined { + return emptyValue, ValueDecoderError{ + Name: "UndefinedDecodeValue", + Types: []reflect.Type{tUndefined}, + Received: reflect.Zero(t), + } + } + + var err error + switch vrType := vr.Type(); vrType { + case bsontype.Undefined: + err = vr.ReadUndefined() + case bsontype.Null: + err = vr.ReadNull() + default: + return emptyValue, fmt.Errorf("cannot decode %v into an Undefined", vr.Type()) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(primitive.Undefined{}), nil +} + +// UndefinedDecodeValue is the ValueDecoderFunc for Undefined. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) UndefinedDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tUndefined { + return ValueDecoderError{Name: "UndefinedDecodeValue", Types: []reflect.Type{tUndefined}, Received: val} + } + + elem, err := dvd.undefinedDecodeType(dc, vr, tUndefined) + if err != nil { + return err + } + + val.Set(elem) + return nil +} + +// Accept both 12-byte string and pretty-printed 24-byte hex string formats. +func (dvd DefaultValueDecoders) objectIDDecodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tOID { + return emptyValue, ValueDecoderError{ + Name: "ObjectIDDecodeValue", + Types: []reflect.Type{tOID}, + Received: reflect.Zero(t), + } + } + + var oid primitive.ObjectID + var err error + switch vrType := vr.Type(); vrType { + case bsontype.ObjectID: + oid, err = vr.ReadObjectID() + if err != nil { + return emptyValue, err + } + case bsontype.String: + str, err := vr.ReadString() + if err != nil { + return emptyValue, err + } + if oid, err = primitive.ObjectIDFromHex(str); err == nil { + break + } + if len(str) != 12 { + return emptyValue, fmt.Errorf("an ObjectID string must be exactly 12 bytes long (got %v)", len(str)) + } + byteArr := []byte(str) + copy(oid[:], byteArr) + case bsontype.Null: + if err = vr.ReadNull(); err != nil { + return emptyValue, err + } + case bsontype.Undefined: + if err = vr.ReadUndefined(); err != nil { + return emptyValue, err + } + default: + return emptyValue, fmt.Errorf("cannot decode %v into an ObjectID", vrType) + } + + return reflect.ValueOf(oid), nil +} + +// ObjectIDDecodeValue is the ValueDecoderFunc for primitive.ObjectID. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) ObjectIDDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tOID { + return ValueDecoderError{Name: "ObjectIDDecodeValue", Types: []reflect.Type{tOID}, Received: val} + } + + elem, err := dvd.objectIDDecodeType(dc, vr, tOID) + if err != nil { + return err + } + + val.Set(elem) + return nil +} + +func (DefaultValueDecoders) dateTimeDecodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tDateTime { + return emptyValue, ValueDecoderError{ + Name: "DateTimeDecodeValue", + Types: []reflect.Type{tDateTime}, + Received: reflect.Zero(t), + } + } + + var dt int64 + var err error + switch vrType := vr.Type(); vrType { + case bsontype.DateTime: + dt, err = vr.ReadDateTime() + case bsontype.Null: + err = vr.ReadNull() + case bsontype.Undefined: + err = vr.ReadUndefined() + default: + return emptyValue, fmt.Errorf("cannot decode %v into a DateTime", vrType) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(primitive.DateTime(dt)), nil +} + +// DateTimeDecodeValue is the ValueDecoderFunc for DateTime. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) DateTimeDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tDateTime { + return ValueDecoderError{Name: "DateTimeDecodeValue", Types: []reflect.Type{tDateTime}, Received: val} + } + + elem, err := dvd.dateTimeDecodeType(dc, vr, tDateTime) + if err != nil { + return err + } + + val.Set(elem) + return nil +} + +func (DefaultValueDecoders) nullDecodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tNull { + return emptyValue, ValueDecoderError{ + Name: "NullDecodeValue", + Types: []reflect.Type{tNull}, + Received: reflect.Zero(t), + } + } + + var err error + switch vrType := vr.Type(); vrType { + case bsontype.Undefined: + err = vr.ReadUndefined() + case bsontype.Null: + err = vr.ReadNull() + default: + return emptyValue, fmt.Errorf("cannot decode %v into a Null", vr.Type()) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(primitive.Null{}), nil +} + +// NullDecodeValue is the ValueDecoderFunc for Null. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) NullDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tNull { + return ValueDecoderError{Name: "NullDecodeValue", Types: []reflect.Type{tNull}, Received: val} + } + + elem, err := dvd.nullDecodeType(dc, vr, tNull) + if err != nil { + return err + } + + val.Set(elem) + return nil +} + +func (DefaultValueDecoders) regexDecodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tRegex { + return emptyValue, ValueDecoderError{ + Name: "RegexDecodeValue", + Types: []reflect.Type{tRegex}, + Received: reflect.Zero(t), + } + } + + var pattern, options string + var err error + switch vrType := vr.Type(); vrType { + case bsontype.Regex: + pattern, options, err = vr.ReadRegex() + case bsontype.Null: + err = vr.ReadNull() + case bsontype.Undefined: + err = vr.ReadUndefined() + default: + return emptyValue, fmt.Errorf("cannot decode %v into a Regex", vrType) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(primitive.Regex{Pattern: pattern, Options: options}), nil +} + +// RegexDecodeValue is the ValueDecoderFunc for Regex. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) RegexDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tRegex { + return ValueDecoderError{Name: "RegexDecodeValue", Types: []reflect.Type{tRegex}, Received: val} + } + + elem, err := dvd.regexDecodeType(dc, vr, tRegex) + if err != nil { + return err + } + + val.Set(elem) + return nil +} + +func (DefaultValueDecoders) dBPointerDecodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tDBPointer { + return emptyValue, ValueDecoderError{ + Name: "DBPointerDecodeValue", + Types: []reflect.Type{tDBPointer}, + Received: reflect.Zero(t), + } + } + + var ns string + var pointer primitive.ObjectID + var err error + switch vrType := vr.Type(); vrType { + case bsontype.DBPointer: + ns, pointer, err = vr.ReadDBPointer() + case bsontype.Null: + err = vr.ReadNull() + case bsontype.Undefined: + err = vr.ReadUndefined() + default: + return emptyValue, fmt.Errorf("cannot decode %v into a DBPointer", vrType) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(primitive.DBPointer{DB: ns, Pointer: pointer}), nil +} + +// DBPointerDecodeValue is the ValueDecoderFunc for DBPointer. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) DBPointerDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tDBPointer { + return ValueDecoderError{Name: "DBPointerDecodeValue", Types: []reflect.Type{tDBPointer}, Received: val} + } + + elem, err := dvd.dBPointerDecodeType(dc, vr, tDBPointer) + if err != nil { + return err + } + + val.Set(elem) + return nil +} + +func (DefaultValueDecoders) timestampDecodeType(_ DecodeContext, vr bsonrw.ValueReader, reflectType reflect.Type) (reflect.Value, error) { + if reflectType != tTimestamp { + return emptyValue, ValueDecoderError{ + Name: "TimestampDecodeValue", + Types: []reflect.Type{tTimestamp}, + Received: reflect.Zero(reflectType), + } + } + + var t, incr uint32 + var err error + switch vrType := vr.Type(); vrType { + case bsontype.Timestamp: + t, incr, err = vr.ReadTimestamp() + case bsontype.Null: + err = vr.ReadNull() + case bsontype.Undefined: + err = vr.ReadUndefined() + default: + return emptyValue, fmt.Errorf("cannot decode %v into a Timestamp", vrType) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(primitive.Timestamp{T: t, I: incr}), nil +} + +// TimestampDecodeValue is the ValueDecoderFunc for Timestamp. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) TimestampDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tTimestamp { + return ValueDecoderError{Name: "TimestampDecodeValue", Types: []reflect.Type{tTimestamp}, Received: val} + } + + elem, err := dvd.timestampDecodeType(dc, vr, tTimestamp) + if err != nil { + return err + } + + val.Set(elem) + return nil +} + +func (DefaultValueDecoders) minKeyDecodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tMinKey { + return emptyValue, ValueDecoderError{ + Name: "MinKeyDecodeValue", + Types: []reflect.Type{tMinKey}, + Received: reflect.Zero(t), + } + } + + var err error + switch vrType := vr.Type(); vrType { + case bsontype.MinKey: + err = vr.ReadMinKey() + case bsontype.Null: + err = vr.ReadNull() + case bsontype.Undefined: + err = vr.ReadUndefined() + default: + return emptyValue, fmt.Errorf("cannot decode %v into a MinKey", vr.Type()) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(primitive.MinKey{}), nil +} + +// MinKeyDecodeValue is the ValueDecoderFunc for MinKey. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) MinKeyDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tMinKey { + return ValueDecoderError{Name: "MinKeyDecodeValue", Types: []reflect.Type{tMinKey}, Received: val} + } + + elem, err := dvd.minKeyDecodeType(dc, vr, tMinKey) + if err != nil { + return err + } + + val.Set(elem) + return nil +} + +func (DefaultValueDecoders) maxKeyDecodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tMaxKey { + return emptyValue, ValueDecoderError{ + Name: "MaxKeyDecodeValue", + Types: []reflect.Type{tMaxKey}, + Received: reflect.Zero(t), + } + } + + var err error + switch vrType := vr.Type(); vrType { + case bsontype.MaxKey: + err = vr.ReadMaxKey() + case bsontype.Null: + err = vr.ReadNull() + case bsontype.Undefined: + err = vr.ReadUndefined() + default: + return emptyValue, fmt.Errorf("cannot decode %v into a MaxKey", vr.Type()) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(primitive.MaxKey{}), nil +} + +// MaxKeyDecodeValue is the ValueDecoderFunc for MaxKey. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) MaxKeyDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tMaxKey { + return ValueDecoderError{Name: "MaxKeyDecodeValue", Types: []reflect.Type{tMaxKey}, Received: val} + } + + elem, err := dvd.maxKeyDecodeType(dc, vr, tMaxKey) + if err != nil { + return err + } + + val.Set(elem) + return nil +} + +func (dvd DefaultValueDecoders) decimal128DecodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tDecimal { + return emptyValue, ValueDecoderError{ + Name: "Decimal128DecodeValue", + Types: []reflect.Type{tDecimal}, + Received: reflect.Zero(t), + } + } + + var d128 primitive.Decimal128 + var err error + switch vrType := vr.Type(); vrType { + case bsontype.Decimal128: + d128, err = vr.ReadDecimal128() + case bsontype.Null: + err = vr.ReadNull() + case bsontype.Undefined: + err = vr.ReadUndefined() + default: + return emptyValue, fmt.Errorf("cannot decode %v into a primitive.Decimal128", vr.Type()) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(d128), nil +} + +// Decimal128DecodeValue is the ValueDecoderFunc for primitive.Decimal128. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) Decimal128DecodeValue(dctx DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tDecimal { + return ValueDecoderError{Name: "Decimal128DecodeValue", Types: []reflect.Type{tDecimal}, Received: val} + } + + elem, err := dvd.decimal128DecodeType(dctx, vr, tDecimal) + if err != nil { + return err + } + + val.Set(elem) + return nil +} + +func (dvd DefaultValueDecoders) jsonNumberDecodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tJSONNumber { + return emptyValue, ValueDecoderError{ + Name: "JSONNumberDecodeValue", + Types: []reflect.Type{tJSONNumber}, + Received: reflect.Zero(t), + } + } + + var jsonNum json.Number + var err error + switch vrType := vr.Type(); vrType { + case bsontype.Double: + f64, err := vr.ReadDouble() + if err != nil { + return emptyValue, err + } + jsonNum = json.Number(strconv.FormatFloat(f64, 'f', -1, 64)) + case bsontype.Int32: + i32, err := vr.ReadInt32() + if err != nil { + return emptyValue, err + } + jsonNum = json.Number(strconv.FormatInt(int64(i32), 10)) + case bsontype.Int64: + i64, err := vr.ReadInt64() + if err != nil { + return emptyValue, err + } + jsonNum = json.Number(strconv.FormatInt(i64, 10)) + case bsontype.Null: + err = vr.ReadNull() + case bsontype.Undefined: + err = vr.ReadUndefined() + default: + return emptyValue, fmt.Errorf("cannot decode %v into a json.Number", vrType) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(jsonNum), nil +} + +// JSONNumberDecodeValue is the ValueDecoderFunc for json.Number. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) JSONNumberDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tJSONNumber { + return ValueDecoderError{Name: "JSONNumberDecodeValue", Types: []reflect.Type{tJSONNumber}, Received: val} + } + + elem, err := dvd.jsonNumberDecodeType(dc, vr, tJSONNumber) + if err != nil { + return err + } + + val.Set(elem) + return nil +} + +func (dvd DefaultValueDecoders) urlDecodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tURL { + return emptyValue, ValueDecoderError{ + Name: "URLDecodeValue", + Types: []reflect.Type{tURL}, + Received: reflect.Zero(t), + } + } + + urlPtr := &url.URL{} + var err error + switch vrType := vr.Type(); vrType { + case bsontype.String: + var str string // Declare str here to avoid shadowing err during the ReadString call. + str, err = vr.ReadString() + if err != nil { + return emptyValue, err + } + + urlPtr, err = url.Parse(str) + case bsontype.Null: + err = vr.ReadNull() + case bsontype.Undefined: + err = vr.ReadUndefined() + default: + return emptyValue, fmt.Errorf("cannot decode %v into a *url.URL", vrType) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(urlPtr).Elem(), nil +} + +// URLDecodeValue is the ValueDecoderFunc for url.URL. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) URLDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tURL { + return ValueDecoderError{Name: "URLDecodeValue", Types: []reflect.Type{tURL}, Received: val} + } + + elem, err := dvd.urlDecodeType(dc, vr, tURL) + if err != nil { + return err + } + + val.Set(elem) + return nil +} + +// TimeDecodeValue is the ValueDecoderFunc for time.Time. +// +// Deprecated: TimeDecodeValue is not registered by default. Use TimeCodec.DecodeValue instead. +func (dvd DefaultValueDecoders) TimeDecodeValue(_ DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if vr.Type() != bsontype.DateTime { + return fmt.Errorf("cannot decode %v into a time.Time", vr.Type()) + } + + dt, err := vr.ReadDateTime() + if err != nil { + return err + } + + if !val.CanSet() || val.Type() != tTime { + return ValueDecoderError{Name: "TimeDecodeValue", Types: []reflect.Type{tTime}, Received: val} + } + + val.Set(reflect.ValueOf(time.Unix(dt/1000, dt%1000*1000000).UTC())) + return nil +} + +// ByteSliceDecodeValue is the ValueDecoderFunc for []byte. +// +// Deprecated: ByteSliceDecodeValue is not registered by default. Use ByteSliceCodec.DecodeValue instead. +func (dvd DefaultValueDecoders) ByteSliceDecodeValue(_ DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if vr.Type() != bsontype.Binary && vr.Type() != bsontype.Null { + return fmt.Errorf("cannot decode %v into a []byte", vr.Type()) + } + + if !val.CanSet() || val.Type() != tByteSlice { + return ValueDecoderError{Name: "ByteSliceDecodeValue", Types: []reflect.Type{tByteSlice}, Received: val} + } + + if vr.Type() == bsontype.Null { + val.Set(reflect.Zero(val.Type())) + return vr.ReadNull() + } + + data, subtype, err := vr.ReadBinary() + if err != nil { + return err + } + if subtype != 0x00 { + return fmt.Errorf("ByteSliceDecodeValue can only be used to decode subtype 0x00 for %s, got %v", bsontype.Binary, subtype) + } + + val.Set(reflect.ValueOf(data)) + return nil +} + +// MapDecodeValue is the ValueDecoderFunc for map[string]* types. +// +// Deprecated: MapDecodeValue is not registered by default. Use MapCodec.DecodeValue instead. +func (dvd DefaultValueDecoders) MapDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Kind() != reflect.Map || val.Type().Key().Kind() != reflect.String { + return ValueDecoderError{Name: "MapDecodeValue", Kinds: []reflect.Kind{reflect.Map}, Received: val} + } + + switch vr.Type() { + case bsontype.Type(0), bsontype.EmbeddedDocument: + case bsontype.Null: + val.Set(reflect.Zero(val.Type())) + return vr.ReadNull() + default: + return fmt.Errorf("cannot decode %v into a %s", vr.Type(), val.Type()) + } + + dr, err := vr.ReadDocument() + if err != nil { + return err + } + + if val.IsNil() { + val.Set(reflect.MakeMap(val.Type())) + } + + eType := val.Type().Elem() + decoder, err := dc.LookupDecoder(eType) + if err != nil { + return err + } + + if eType == tEmpty { + dc.Ancestor = val.Type() + } + + keyType := val.Type().Key() + for { + key, vr, err := dr.ReadElement() + if errors.Is(err, bsonrw.ErrEOD) { + break + } + if err != nil { + return err + } + + elem := reflect.New(eType).Elem() + + err = decoder.DecodeValue(dc, vr, elem) + if err != nil { + return err + } + + val.SetMapIndex(reflect.ValueOf(key).Convert(keyType), elem) + } + return nil +} + +// ArrayDecodeValue is the ValueDecoderFunc for array types. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) ArrayDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.IsValid() || val.Kind() != reflect.Array { + return ValueDecoderError{Name: "ArrayDecodeValue", Kinds: []reflect.Kind{reflect.Array}, Received: val} + } + + switch vrType := vr.Type(); vrType { + case bsontype.Array: + case bsontype.Type(0), bsontype.EmbeddedDocument: + if val.Type().Elem() != tE { + return fmt.Errorf("cannot decode document into %s", val.Type()) + } + case bsontype.Binary: + if val.Type().Elem() != tByte { + return fmt.Errorf("ArrayDecodeValue can only be used to decode binary into a byte array, got %v", vrType) + } + data, subtype, err := vr.ReadBinary() + if err != nil { + return err + } + if subtype != bsontype.BinaryGeneric && subtype != bsontype.BinaryBinaryOld { + return fmt.Errorf("ArrayDecodeValue can only be used to decode subtype 0x00 or 0x02 for %s, got %v", bsontype.Binary, subtype) + } + + if len(data) > val.Len() { + return fmt.Errorf("more elements returned in array than can fit inside %s", val.Type()) + } + + for idx, elem := range data { + val.Index(idx).Set(reflect.ValueOf(elem)) + } + return nil + case bsontype.Null: + val.Set(reflect.Zero(val.Type())) + return vr.ReadNull() + case bsontype.Undefined: + val.Set(reflect.Zero(val.Type())) + return vr.ReadUndefined() + default: + return fmt.Errorf("cannot decode %v into an array", vrType) + } + + var elemsFunc func(DecodeContext, bsonrw.ValueReader, reflect.Value) ([]reflect.Value, error) + switch val.Type().Elem() { + case tE: + elemsFunc = dvd.decodeD + default: + elemsFunc = dvd.decodeDefault + } + + elems, err := elemsFunc(dc, vr, val) + if err != nil { + return err + } + + if len(elems) > val.Len() { + return fmt.Errorf("more elements returned in array than can fit inside %s, got %v elements", val.Type(), len(elems)) + } + + for idx, elem := range elems { + val.Index(idx).Set(elem) + } + + return nil +} + +// SliceDecodeValue is the ValueDecoderFunc for slice types. +// +// Deprecated: SliceDecodeValue is not registered by default. Use SliceCodec.DecodeValue instead. +func (dvd DefaultValueDecoders) SliceDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Kind() != reflect.Slice { + return ValueDecoderError{Name: "SliceDecodeValue", Kinds: []reflect.Kind{reflect.Slice}, Received: val} + } + + switch vr.Type() { + case bsontype.Array: + case bsontype.Null: + val.Set(reflect.Zero(val.Type())) + return vr.ReadNull() + case bsontype.Type(0), bsontype.EmbeddedDocument: + if val.Type().Elem() != tE { + return fmt.Errorf("cannot decode document into %s", val.Type()) + } + default: + return fmt.Errorf("cannot decode %v into a slice", vr.Type()) + } + + var elemsFunc func(DecodeContext, bsonrw.ValueReader, reflect.Value) ([]reflect.Value, error) + switch val.Type().Elem() { + case tE: + dc.Ancestor = val.Type() + elemsFunc = dvd.decodeD + default: + elemsFunc = dvd.decodeDefault + } + + elems, err := elemsFunc(dc, vr, val) + if err != nil { + return err + } + + if val.IsNil() { + val.Set(reflect.MakeSlice(val.Type(), 0, len(elems))) + } + + val.SetLen(0) + val.Set(reflect.Append(val, elems...)) + + return nil +} + +// ValueUnmarshalerDecodeValue is the ValueDecoderFunc for ValueUnmarshaler implementations. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) ValueUnmarshalerDecodeValue(_ DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.IsValid() || (!val.Type().Implements(tValueUnmarshaler) && !reflect.PtrTo(val.Type()).Implements(tValueUnmarshaler)) { + return ValueDecoderError{Name: "ValueUnmarshalerDecodeValue", Types: []reflect.Type{tValueUnmarshaler}, Received: val} + } + + // If BSON value is null and the go value is a pointer, then don't call + // UnmarshalBSONValue. Even if the Go pointer is already initialized (i.e., + // non-nil), encountering null in BSON will result in the pointer being + // directly set to nil here. Since the pointer is being replaced with nil, + // there is no opportunity (or reason) for the custom UnmarshalBSONValue logic + // to be called. + if vr.Type() == bsontype.Null && val.Kind() == reflect.Ptr { + val.Set(reflect.Zero(val.Type())) + + return vr.ReadNull() + } + + if val.Kind() == reflect.Ptr && val.IsNil() { + if !val.CanSet() { + return ValueDecoderError{Name: "ValueUnmarshalerDecodeValue", Types: []reflect.Type{tValueUnmarshaler}, Received: val} + } + val.Set(reflect.New(val.Type().Elem())) + } + + if !val.Type().Implements(tValueUnmarshaler) { + if !val.CanAddr() { + return ValueDecoderError{Name: "ValueUnmarshalerDecodeValue", Types: []reflect.Type{tValueUnmarshaler}, Received: val} + } + val = val.Addr() // If the type doesn't implement the interface, a pointer to it must. + } + + t, src, err := bsonrw.Copier{}.CopyValueToBytes(vr) + if err != nil { + return err + } + + m, ok := val.Interface().(ValueUnmarshaler) + if !ok { + // NB: this error should be unreachable due to the above checks + return ValueDecoderError{Name: "ValueUnmarshalerDecodeValue", Types: []reflect.Type{tValueUnmarshaler}, Received: val} + } + return m.UnmarshalBSONValue(t, src) +} + +// UnmarshalerDecodeValue is the ValueDecoderFunc for Unmarshaler implementations. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) UnmarshalerDecodeValue(_ DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.IsValid() || (!val.Type().Implements(tUnmarshaler) && !reflect.PtrTo(val.Type()).Implements(tUnmarshaler)) { + return ValueDecoderError{Name: "UnmarshalerDecodeValue", Types: []reflect.Type{tUnmarshaler}, Received: val} + } + + if val.Kind() == reflect.Ptr && val.IsNil() { + if !val.CanSet() { + return ValueDecoderError{Name: "UnmarshalerDecodeValue", Types: []reflect.Type{tUnmarshaler}, Received: val} + } + val.Set(reflect.New(val.Type().Elem())) + } + + _, src, err := bsonrw.Copier{}.CopyValueToBytes(vr) + if err != nil { + return err + } + + // If the target Go value is a pointer and the BSON field value is empty, set the value to the + // zero value of the pointer (nil) and don't call UnmarshalBSON. UnmarshalBSON has no way to + // change the pointer value from within the function (only the value at the pointer address), + // so it can't set the pointer to "nil" itself. Since the most common Go value for an empty BSON + // field value is "nil", we set "nil" here and don't call UnmarshalBSON. This behavior matches + // the behavior of the Go "encoding/json" unmarshaler when the target Go value is a pointer and + // the JSON field value is "null". + if val.Kind() == reflect.Ptr && len(src) == 0 { + val.Set(reflect.Zero(val.Type())) + return nil + } + + if !val.Type().Implements(tUnmarshaler) { + if !val.CanAddr() { + return ValueDecoderError{Name: "UnmarshalerDecodeValue", Types: []reflect.Type{tUnmarshaler}, Received: val} + } + val = val.Addr() // If the type doesn't implement the interface, a pointer to it must. + } + + m, ok := val.Interface().(Unmarshaler) + if !ok { + // NB: this error should be unreachable due to the above checks + return ValueDecoderError{Name: "UnmarshalerDecodeValue", Types: []reflect.Type{tUnmarshaler}, Received: val} + } + return m.UnmarshalBSON(src) +} + +// EmptyInterfaceDecodeValue is the ValueDecoderFunc for interface{}. +// +// Deprecated: EmptyInterfaceDecodeValue is not registered by default. Use EmptyInterfaceCodec.DecodeValue instead. +func (dvd DefaultValueDecoders) EmptyInterfaceDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tEmpty { + return ValueDecoderError{Name: "EmptyInterfaceDecodeValue", Types: []reflect.Type{tEmpty}, Received: val} + } + + rtype, err := dc.LookupTypeMapEntry(vr.Type()) + if err != nil { + switch vr.Type() { + case bsontype.EmbeddedDocument: + if dc.Ancestor != nil { + rtype = dc.Ancestor + break + } + rtype = tD + case bsontype.Null: + val.Set(reflect.Zero(val.Type())) + return vr.ReadNull() + default: + return err + } + } + + decoder, err := dc.LookupDecoder(rtype) + if err != nil { + return err + } + + elem := reflect.New(rtype).Elem() + err = decoder.DecodeValue(dc, vr, elem) + if err != nil { + return err + } + + val.Set(elem) + return nil +} + +// CoreDocumentDecodeValue is the ValueDecoderFunc for bsoncore.Document. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (DefaultValueDecoders) CoreDocumentDecodeValue(_ DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tCoreDocument { + return ValueDecoderError{Name: "CoreDocumentDecodeValue", Types: []reflect.Type{tCoreDocument}, Received: val} + } + + if val.IsNil() { + val.Set(reflect.MakeSlice(val.Type(), 0, 0)) + } + + val.SetLen(0) + + cdoc, err := bsonrw.Copier{}.AppendDocumentBytes(val.Interface().(bsoncore.Document), vr) + val.Set(reflect.ValueOf(cdoc)) + return err +} + +func (dvd DefaultValueDecoders) decodeDefault(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) ([]reflect.Value, error) { + elems := make([]reflect.Value, 0) + + ar, err := vr.ReadArray() + if err != nil { + return nil, err + } + + eType := val.Type().Elem() + + decoder, err := dc.LookupDecoder(eType) + if err != nil { + return nil, err + } + eTypeDecoder, _ := decoder.(typeDecoder) + + idx := 0 + for { + vr, err := ar.ReadValue() + if errors.Is(err, bsonrw.ErrEOA) { + break + } + if err != nil { + return nil, err + } + + elem, err := decodeTypeOrValueWithInfo(decoder, eTypeDecoder, dc, vr, eType, true) + if err != nil { + return nil, newDecodeError(strconv.Itoa(idx), err) + } + elems = append(elems, elem) + idx++ + } + + return elems, nil +} + +func (dvd DefaultValueDecoders) readCodeWithScope(dc DecodeContext, vr bsonrw.ValueReader) (primitive.CodeWithScope, error) { + var cws primitive.CodeWithScope + + code, dr, err := vr.ReadCodeWithScope() + if err != nil { + return cws, err + } + + scope := reflect.New(tD).Elem() + elems, err := dvd.decodeElemsFromDocumentReader(dc, dr) + if err != nil { + return cws, err + } + + scope.Set(reflect.MakeSlice(tD, 0, len(elems))) + scope.Set(reflect.Append(scope, elems...)) + + cws = primitive.CodeWithScope{ + Code: primitive.JavaScript(code), + Scope: scope.Interface().(primitive.D), + } + return cws, nil +} + +func (dvd DefaultValueDecoders) codeWithScopeDecodeType(dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tCodeWithScope { + return emptyValue, ValueDecoderError{ + Name: "CodeWithScopeDecodeValue", + Types: []reflect.Type{tCodeWithScope}, + Received: reflect.Zero(t), + } + } + + var cws primitive.CodeWithScope + var err error + switch vrType := vr.Type(); vrType { + case bsontype.CodeWithScope: + cws, err = dvd.readCodeWithScope(dc, vr) + case bsontype.Null: + err = vr.ReadNull() + case bsontype.Undefined: + err = vr.ReadUndefined() + default: + return emptyValue, fmt.Errorf("cannot decode %v into a primitive.CodeWithScope", vrType) + } + if err != nil { + return emptyValue, err + } + + return reflect.ValueOf(cws), nil +} + +// CodeWithScopeDecodeValue is the ValueDecoderFunc for CodeWithScope. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value decoders registered. +func (dvd DefaultValueDecoders) CodeWithScopeDecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tCodeWithScope { + return ValueDecoderError{Name: "CodeWithScopeDecodeValue", Types: []reflect.Type{tCodeWithScope}, Received: val} + } + + elem, err := dvd.codeWithScopeDecodeType(dc, vr, tCodeWithScope) + if err != nil { + return err + } + + val.Set(elem) + return nil +} + +func (dvd DefaultValueDecoders) decodeD(dc DecodeContext, vr bsonrw.ValueReader, _ reflect.Value) ([]reflect.Value, error) { + switch vr.Type() { + case bsontype.Type(0), bsontype.EmbeddedDocument: + default: + return nil, fmt.Errorf("cannot decode %v into a D", vr.Type()) + } + + dr, err := vr.ReadDocument() + if err != nil { + return nil, err + } + + return dvd.decodeElemsFromDocumentReader(dc, dr) +} + +func (DefaultValueDecoders) decodeElemsFromDocumentReader(dc DecodeContext, dr bsonrw.DocumentReader) ([]reflect.Value, error) { + decoder, err := dc.LookupDecoder(tEmpty) + if err != nil { + return nil, err + } + + elems := make([]reflect.Value, 0) + for { + key, vr, err := dr.ReadElement() + if errors.Is(err, bsonrw.ErrEOD) { + break + } + if err != nil { + return nil, err + } + + val := reflect.New(tEmpty).Elem() + err = decoder.DecodeValue(dc, vr, val) + if err != nil { + return nil, newDecodeError(key, err) + } + + elems = append(elems, reflect.ValueOf(primitive.E{Key: key, Value: val.Interface()})) + } + + return elems, nil +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_encoders.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_encoders.go new file mode 100644 index 0000000000..4751ae995e --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/default_value_encoders.go @@ -0,0 +1,856 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "encoding/json" + "errors" + "fmt" + "math" + "net/url" + "reflect" + "sync" + "time" + + "go.mongodb.org/mongo-driver/bson/bsonrw" + "go.mongodb.org/mongo-driver/bson/bsontype" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" +) + +var defaultValueEncoders DefaultValueEncoders + +var bvwPool = bsonrw.NewBSONValueWriterPool() + +var errInvalidValue = errors.New("cannot encode invalid element") + +var sliceWriterPool = sync.Pool{ + New: func() interface{} { + sw := make(bsonrw.SliceWriter, 0) + return &sw + }, +} + +func encodeElement(ec EncodeContext, dw bsonrw.DocumentWriter, e primitive.E) error { + vw, err := dw.WriteDocumentElement(e.Key) + if err != nil { + return err + } + + if e.Value == nil { + return vw.WriteNull() + } + encoder, err := ec.LookupEncoder(reflect.TypeOf(e.Value)) + if err != nil { + return err + } + + err = encoder.EncodeValue(ec, vw, reflect.ValueOf(e.Value)) + if err != nil { + return err + } + return nil +} + +// DefaultValueEncoders is a namespace type for the default ValueEncoders used +// when creating a registry. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +type DefaultValueEncoders struct{} + +// RegisterDefaultEncoders will register the encoder methods attached to DefaultValueEncoders with +// the provided RegistryBuilder. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (dve DefaultValueEncoders) RegisterDefaultEncoders(rb *RegistryBuilder) { + if rb == nil { + panic(errors.New("argument to RegisterDefaultEncoders must not be nil")) + } + rb. + RegisterTypeEncoder(tByteSlice, defaultByteSliceCodec). + RegisterTypeEncoder(tTime, defaultTimeCodec). + RegisterTypeEncoder(tEmpty, defaultEmptyInterfaceCodec). + RegisterTypeEncoder(tCoreArray, defaultArrayCodec). + RegisterTypeEncoder(tOID, ValueEncoderFunc(dve.ObjectIDEncodeValue)). + RegisterTypeEncoder(tDecimal, ValueEncoderFunc(dve.Decimal128EncodeValue)). + RegisterTypeEncoder(tJSONNumber, ValueEncoderFunc(dve.JSONNumberEncodeValue)). + RegisterTypeEncoder(tURL, ValueEncoderFunc(dve.URLEncodeValue)). + RegisterTypeEncoder(tJavaScript, ValueEncoderFunc(dve.JavaScriptEncodeValue)). + RegisterTypeEncoder(tSymbol, ValueEncoderFunc(dve.SymbolEncodeValue)). + RegisterTypeEncoder(tBinary, ValueEncoderFunc(dve.BinaryEncodeValue)). + RegisterTypeEncoder(tUndefined, ValueEncoderFunc(dve.UndefinedEncodeValue)). + RegisterTypeEncoder(tDateTime, ValueEncoderFunc(dve.DateTimeEncodeValue)). + RegisterTypeEncoder(tNull, ValueEncoderFunc(dve.NullEncodeValue)). + RegisterTypeEncoder(tRegex, ValueEncoderFunc(dve.RegexEncodeValue)). + RegisterTypeEncoder(tDBPointer, ValueEncoderFunc(dve.DBPointerEncodeValue)). + RegisterTypeEncoder(tTimestamp, ValueEncoderFunc(dve.TimestampEncodeValue)). + RegisterTypeEncoder(tMinKey, ValueEncoderFunc(dve.MinKeyEncodeValue)). + RegisterTypeEncoder(tMaxKey, ValueEncoderFunc(dve.MaxKeyEncodeValue)). + RegisterTypeEncoder(tCoreDocument, ValueEncoderFunc(dve.CoreDocumentEncodeValue)). + RegisterTypeEncoder(tCodeWithScope, ValueEncoderFunc(dve.CodeWithScopeEncodeValue)). + RegisterDefaultEncoder(reflect.Bool, ValueEncoderFunc(dve.BooleanEncodeValue)). + RegisterDefaultEncoder(reflect.Int, ValueEncoderFunc(dve.IntEncodeValue)). + RegisterDefaultEncoder(reflect.Int8, ValueEncoderFunc(dve.IntEncodeValue)). + RegisterDefaultEncoder(reflect.Int16, ValueEncoderFunc(dve.IntEncodeValue)). + RegisterDefaultEncoder(reflect.Int32, ValueEncoderFunc(dve.IntEncodeValue)). + RegisterDefaultEncoder(reflect.Int64, ValueEncoderFunc(dve.IntEncodeValue)). + RegisterDefaultEncoder(reflect.Uint, defaultUIntCodec). + RegisterDefaultEncoder(reflect.Uint8, defaultUIntCodec). + RegisterDefaultEncoder(reflect.Uint16, defaultUIntCodec). + RegisterDefaultEncoder(reflect.Uint32, defaultUIntCodec). + RegisterDefaultEncoder(reflect.Uint64, defaultUIntCodec). + RegisterDefaultEncoder(reflect.Float32, ValueEncoderFunc(dve.FloatEncodeValue)). + RegisterDefaultEncoder(reflect.Float64, ValueEncoderFunc(dve.FloatEncodeValue)). + RegisterDefaultEncoder(reflect.Array, ValueEncoderFunc(dve.ArrayEncodeValue)). + RegisterDefaultEncoder(reflect.Map, defaultMapCodec). + RegisterDefaultEncoder(reflect.Slice, defaultSliceCodec). + RegisterDefaultEncoder(reflect.String, defaultStringCodec). + RegisterDefaultEncoder(reflect.Struct, newDefaultStructCodec()). + RegisterDefaultEncoder(reflect.Ptr, NewPointerCodec()). + RegisterHookEncoder(tValueMarshaler, ValueEncoderFunc(dve.ValueMarshalerEncodeValue)). + RegisterHookEncoder(tMarshaler, ValueEncoderFunc(dve.MarshalerEncodeValue)). + RegisterHookEncoder(tProxy, ValueEncoderFunc(dve.ProxyEncodeValue)) +} + +// BooleanEncodeValue is the ValueEncoderFunc for bool types. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (dve DefaultValueEncoders) BooleanEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Kind() != reflect.Bool { + return ValueEncoderError{Name: "BooleanEncodeValue", Kinds: []reflect.Kind{reflect.Bool}, Received: val} + } + return vw.WriteBoolean(val.Bool()) +} + +func fitsIn32Bits(i int64) bool { + return math.MinInt32 <= i && i <= math.MaxInt32 +} + +// IntEncodeValue is the ValueEncoderFunc for int types. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (dve DefaultValueEncoders) IntEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + switch val.Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32: + return vw.WriteInt32(int32(val.Int())) + case reflect.Int: + i64 := val.Int() + if fitsIn32Bits(i64) { + return vw.WriteInt32(int32(i64)) + } + return vw.WriteInt64(i64) + case reflect.Int64: + i64 := val.Int() + if ec.MinSize && fitsIn32Bits(i64) { + return vw.WriteInt32(int32(i64)) + } + return vw.WriteInt64(i64) + } + + return ValueEncoderError{ + Name: "IntEncodeValue", + Kinds: []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int}, + Received: val, + } +} + +// UintEncodeValue is the ValueEncoderFunc for uint types. +// +// Deprecated: UintEncodeValue is not registered by default. Use UintCodec.EncodeValue instead. +func (dve DefaultValueEncoders) UintEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + switch val.Kind() { + case reflect.Uint8, reflect.Uint16: + return vw.WriteInt32(int32(val.Uint())) + case reflect.Uint, reflect.Uint32, reflect.Uint64: + u64 := val.Uint() + if ec.MinSize && u64 <= math.MaxInt32 { + return vw.WriteInt32(int32(u64)) + } + if u64 > math.MaxInt64 { + return fmt.Errorf("%d overflows int64", u64) + } + return vw.WriteInt64(int64(u64)) + } + + return ValueEncoderError{ + Name: "UintEncodeValue", + Kinds: []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint}, + Received: val, + } +} + +// FloatEncodeValue is the ValueEncoderFunc for float types. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (dve DefaultValueEncoders) FloatEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + switch val.Kind() { + case reflect.Float32, reflect.Float64: + return vw.WriteDouble(val.Float()) + } + + return ValueEncoderError{Name: "FloatEncodeValue", Kinds: []reflect.Kind{reflect.Float32, reflect.Float64}, Received: val} +} + +// StringEncodeValue is the ValueEncoderFunc for string types. +// +// Deprecated: StringEncodeValue is not registered by default. Use StringCodec.EncodeValue instead. +func (dve DefaultValueEncoders) StringEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if val.Kind() != reflect.String { + return ValueEncoderError{ + Name: "StringEncodeValue", + Kinds: []reflect.Kind{reflect.String}, + Received: val, + } + } + + return vw.WriteString(val.String()) +} + +// ObjectIDEncodeValue is the ValueEncoderFunc for primitive.ObjectID. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (dve DefaultValueEncoders) ObjectIDEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tOID { + return ValueEncoderError{Name: "ObjectIDEncodeValue", Types: []reflect.Type{tOID}, Received: val} + } + return vw.WriteObjectID(val.Interface().(primitive.ObjectID)) +} + +// Decimal128EncodeValue is the ValueEncoderFunc for primitive.Decimal128. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (dve DefaultValueEncoders) Decimal128EncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tDecimal { + return ValueEncoderError{Name: "Decimal128EncodeValue", Types: []reflect.Type{tDecimal}, Received: val} + } + return vw.WriteDecimal128(val.Interface().(primitive.Decimal128)) +} + +// JSONNumberEncodeValue is the ValueEncoderFunc for json.Number. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (dve DefaultValueEncoders) JSONNumberEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tJSONNumber { + return ValueEncoderError{Name: "JSONNumberEncodeValue", Types: []reflect.Type{tJSONNumber}, Received: val} + } + jsnum := val.Interface().(json.Number) + + // Attempt int first, then float64 + if i64, err := jsnum.Int64(); err == nil { + return dve.IntEncodeValue(ec, vw, reflect.ValueOf(i64)) + } + + f64, err := jsnum.Float64() + if err != nil { + return err + } + + return dve.FloatEncodeValue(ec, vw, reflect.ValueOf(f64)) +} + +// URLEncodeValue is the ValueEncoderFunc for url.URL. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (dve DefaultValueEncoders) URLEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tURL { + return ValueEncoderError{Name: "URLEncodeValue", Types: []reflect.Type{tURL}, Received: val} + } + u := val.Interface().(url.URL) + return vw.WriteString(u.String()) +} + +// TimeEncodeValue is the ValueEncoderFunc for time.TIme. +// +// Deprecated: TimeEncodeValue is not registered by default. Use TimeCodec.EncodeValue instead. +func (dve DefaultValueEncoders) TimeEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tTime { + return ValueEncoderError{Name: "TimeEncodeValue", Types: []reflect.Type{tTime}, Received: val} + } + tt := val.Interface().(time.Time) + dt := primitive.NewDateTimeFromTime(tt) + return vw.WriteDateTime(int64(dt)) +} + +// ByteSliceEncodeValue is the ValueEncoderFunc for []byte. +// +// Deprecated: ByteSliceEncodeValue is not registered by default. Use ByteSliceCodec.EncodeValue instead. +func (dve DefaultValueEncoders) ByteSliceEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tByteSlice { + return ValueEncoderError{Name: "ByteSliceEncodeValue", Types: []reflect.Type{tByteSlice}, Received: val} + } + if val.IsNil() { + return vw.WriteNull() + } + return vw.WriteBinary(val.Interface().([]byte)) +} + +// MapEncodeValue is the ValueEncoderFunc for map[string]* types. +// +// Deprecated: MapEncodeValue is not registered by default. Use MapCodec.EncodeValue instead. +func (dve DefaultValueEncoders) MapEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Kind() != reflect.Map || val.Type().Key().Kind() != reflect.String { + return ValueEncoderError{Name: "MapEncodeValue", Kinds: []reflect.Kind{reflect.Map}, Received: val} + } + + if val.IsNil() { + // If we have a nill map but we can't WriteNull, that means we're probably trying to encode + // to a TopLevel document. We can't currently tell if this is what actually happened, but if + // there's a deeper underlying problem, the error will also be returned from WriteDocument, + // so just continue. The operations on a map reflection value are valid, so we can call + // MapKeys within mapEncodeValue without a problem. + err := vw.WriteNull() + if err == nil { + return nil + } + } + + dw, err := vw.WriteDocument() + if err != nil { + return err + } + + return dve.mapEncodeValue(ec, dw, val, nil) +} + +// mapEncodeValue handles encoding of the values of a map. The collisionFn returns +// true if the provided key exists, this is mainly used for inline maps in the +// struct codec. +func (dve DefaultValueEncoders) mapEncodeValue(ec EncodeContext, dw bsonrw.DocumentWriter, val reflect.Value, collisionFn func(string) bool) error { + + elemType := val.Type().Elem() + encoder, err := ec.LookupEncoder(elemType) + if err != nil && elemType.Kind() != reflect.Interface { + return err + } + + keys := val.MapKeys() + for _, key := range keys { + if collisionFn != nil && collisionFn(key.String()) { + return fmt.Errorf("Key %s of inlined map conflicts with a struct field name", key) + } + + currEncoder, currVal, lookupErr := dve.lookupElementEncoder(ec, encoder, val.MapIndex(key)) + if lookupErr != nil && !errors.Is(lookupErr, errInvalidValue) { + return lookupErr + } + + vw, err := dw.WriteDocumentElement(key.String()) + if err != nil { + return err + } + + if errors.Is(lookupErr, errInvalidValue) { + err = vw.WriteNull() + if err != nil { + return err + } + continue + } + + err = currEncoder.EncodeValue(ec, vw, currVal) + if err != nil { + return err + } + } + + return dw.WriteDocumentEnd() +} + +// ArrayEncodeValue is the ValueEncoderFunc for array types. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (dve DefaultValueEncoders) ArrayEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Kind() != reflect.Array { + return ValueEncoderError{Name: "ArrayEncodeValue", Kinds: []reflect.Kind{reflect.Array}, Received: val} + } + + // If we have a []primitive.E we want to treat it as a document instead of as an array. + if val.Type().Elem() == tE { + dw, err := vw.WriteDocument() + if err != nil { + return err + } + + for idx := 0; idx < val.Len(); idx++ { + e := val.Index(idx).Interface().(primitive.E) + err = encodeElement(ec, dw, e) + if err != nil { + return err + } + } + + return dw.WriteDocumentEnd() + } + + // If we have a []byte we want to treat it as a binary instead of as an array. + if val.Type().Elem() == tByte { + var byteSlice []byte + for idx := 0; idx < val.Len(); idx++ { + byteSlice = append(byteSlice, val.Index(idx).Interface().(byte)) + } + return vw.WriteBinary(byteSlice) + } + + aw, err := vw.WriteArray() + if err != nil { + return err + } + + elemType := val.Type().Elem() + encoder, err := ec.LookupEncoder(elemType) + if err != nil && elemType.Kind() != reflect.Interface { + return err + } + + for idx := 0; idx < val.Len(); idx++ { + currEncoder, currVal, lookupErr := dve.lookupElementEncoder(ec, encoder, val.Index(idx)) + if lookupErr != nil && !errors.Is(lookupErr, errInvalidValue) { + return lookupErr + } + + vw, err := aw.WriteArrayElement() + if err != nil { + return err + } + + if errors.Is(lookupErr, errInvalidValue) { + err = vw.WriteNull() + if err != nil { + return err + } + continue + } + + err = currEncoder.EncodeValue(ec, vw, currVal) + if err != nil { + return err + } + } + return aw.WriteArrayEnd() +} + +// SliceEncodeValue is the ValueEncoderFunc for slice types. +// +// Deprecated: SliceEncodeValue is not registered by default. Use SliceCodec.EncodeValue instead. +func (dve DefaultValueEncoders) SliceEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Kind() != reflect.Slice { + return ValueEncoderError{Name: "SliceEncodeValue", Kinds: []reflect.Kind{reflect.Slice}, Received: val} + } + + if val.IsNil() { + return vw.WriteNull() + } + + // If we have a []primitive.E we want to treat it as a document instead of as an array. + if val.Type().ConvertibleTo(tD) { + d := val.Convert(tD).Interface().(primitive.D) + + dw, err := vw.WriteDocument() + if err != nil { + return err + } + + for _, e := range d { + err = encodeElement(ec, dw, e) + if err != nil { + return err + } + } + + return dw.WriteDocumentEnd() + } + + aw, err := vw.WriteArray() + if err != nil { + return err + } + + elemType := val.Type().Elem() + encoder, err := ec.LookupEncoder(elemType) + if err != nil && elemType.Kind() != reflect.Interface { + return err + } + + for idx := 0; idx < val.Len(); idx++ { + currEncoder, currVal, lookupErr := dve.lookupElementEncoder(ec, encoder, val.Index(idx)) + if lookupErr != nil && !errors.Is(lookupErr, errInvalidValue) { + return lookupErr + } + + vw, err := aw.WriteArrayElement() + if err != nil { + return err + } + + if errors.Is(lookupErr, errInvalidValue) { + err = vw.WriteNull() + if err != nil { + return err + } + continue + } + + err = currEncoder.EncodeValue(ec, vw, currVal) + if err != nil { + return err + } + } + return aw.WriteArrayEnd() +} + +func (dve DefaultValueEncoders) lookupElementEncoder(ec EncodeContext, origEncoder ValueEncoder, currVal reflect.Value) (ValueEncoder, reflect.Value, error) { + if origEncoder != nil || (currVal.Kind() != reflect.Interface) { + return origEncoder, currVal, nil + } + currVal = currVal.Elem() + if !currVal.IsValid() { + return nil, currVal, errInvalidValue + } + currEncoder, err := ec.LookupEncoder(currVal.Type()) + + return currEncoder, currVal, err +} + +// EmptyInterfaceEncodeValue is the ValueEncoderFunc for interface{}. +// +// Deprecated: EmptyInterfaceEncodeValue is not registered by default. Use EmptyInterfaceCodec.EncodeValue instead. +func (dve DefaultValueEncoders) EmptyInterfaceEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tEmpty { + return ValueEncoderError{Name: "EmptyInterfaceEncodeValue", Types: []reflect.Type{tEmpty}, Received: val} + } + + if val.IsNil() { + return vw.WriteNull() + } + encoder, err := ec.LookupEncoder(val.Elem().Type()) + if err != nil { + return err + } + + return encoder.EncodeValue(ec, vw, val.Elem()) +} + +// ValueMarshalerEncodeValue is the ValueEncoderFunc for ValueMarshaler implementations. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (dve DefaultValueEncoders) ValueMarshalerEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + // Either val or a pointer to val must implement ValueMarshaler + switch { + case !val.IsValid(): + return ValueEncoderError{Name: "ValueMarshalerEncodeValue", Types: []reflect.Type{tValueMarshaler}, Received: val} + case val.Type().Implements(tValueMarshaler): + // If ValueMarshaler is implemented on a concrete type, make sure that val isn't a nil pointer + if isImplementationNil(val, tValueMarshaler) { + return vw.WriteNull() + } + case reflect.PtrTo(val.Type()).Implements(tValueMarshaler) && val.CanAddr(): + val = val.Addr() + default: + return ValueEncoderError{Name: "ValueMarshalerEncodeValue", Types: []reflect.Type{tValueMarshaler}, Received: val} + } + + m, ok := val.Interface().(ValueMarshaler) + if !ok { + return vw.WriteNull() + } + t, data, err := m.MarshalBSONValue() + if err != nil { + return err + } + return bsonrw.Copier{}.CopyValueFromBytes(vw, t, data) +} + +// MarshalerEncodeValue is the ValueEncoderFunc for Marshaler implementations. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (dve DefaultValueEncoders) MarshalerEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + // Either val or a pointer to val must implement Marshaler + switch { + case !val.IsValid(): + return ValueEncoderError{Name: "MarshalerEncodeValue", Types: []reflect.Type{tMarshaler}, Received: val} + case val.Type().Implements(tMarshaler): + // If Marshaler is implemented on a concrete type, make sure that val isn't a nil pointer + if isImplementationNil(val, tMarshaler) { + return vw.WriteNull() + } + case reflect.PtrTo(val.Type()).Implements(tMarshaler) && val.CanAddr(): + val = val.Addr() + default: + return ValueEncoderError{Name: "MarshalerEncodeValue", Types: []reflect.Type{tMarshaler}, Received: val} + } + + m, ok := val.Interface().(Marshaler) + if !ok { + return vw.WriteNull() + } + data, err := m.MarshalBSON() + if err != nil { + return err + } + return bsonrw.Copier{}.CopyValueFromBytes(vw, bsontype.EmbeddedDocument, data) +} + +// ProxyEncodeValue is the ValueEncoderFunc for Proxy implementations. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (dve DefaultValueEncoders) ProxyEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + // Either val or a pointer to val must implement Proxy + switch { + case !val.IsValid(): + return ValueEncoderError{Name: "ProxyEncodeValue", Types: []reflect.Type{tProxy}, Received: val} + case val.Type().Implements(tProxy): + // If Proxy is implemented on a concrete type, make sure that val isn't a nil pointer + if isImplementationNil(val, tProxy) { + return vw.WriteNull() + } + case reflect.PtrTo(val.Type()).Implements(tProxy) && val.CanAddr(): + val = val.Addr() + default: + return ValueEncoderError{Name: "ProxyEncodeValue", Types: []reflect.Type{tProxy}, Received: val} + } + + m, ok := val.Interface().(Proxy) + if !ok { + return vw.WriteNull() + } + v, err := m.ProxyBSON() + if err != nil { + return err + } + if v == nil { + encoder, err := ec.LookupEncoder(nil) + if err != nil { + return err + } + return encoder.EncodeValue(ec, vw, reflect.ValueOf(nil)) + } + vv := reflect.ValueOf(v) + switch vv.Kind() { + case reflect.Ptr, reflect.Interface: + vv = vv.Elem() + } + encoder, err := ec.LookupEncoder(vv.Type()) + if err != nil { + return err + } + return encoder.EncodeValue(ec, vw, vv) +} + +// JavaScriptEncodeValue is the ValueEncoderFunc for the primitive.JavaScript type. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (DefaultValueEncoders) JavaScriptEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tJavaScript { + return ValueEncoderError{Name: "JavaScriptEncodeValue", Types: []reflect.Type{tJavaScript}, Received: val} + } + + return vw.WriteJavascript(val.String()) +} + +// SymbolEncodeValue is the ValueEncoderFunc for the primitive.Symbol type. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (DefaultValueEncoders) SymbolEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tSymbol { + return ValueEncoderError{Name: "SymbolEncodeValue", Types: []reflect.Type{tSymbol}, Received: val} + } + + return vw.WriteSymbol(val.String()) +} + +// BinaryEncodeValue is the ValueEncoderFunc for Binary. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (DefaultValueEncoders) BinaryEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tBinary { + return ValueEncoderError{Name: "BinaryEncodeValue", Types: []reflect.Type{tBinary}, Received: val} + } + b := val.Interface().(primitive.Binary) + + return vw.WriteBinaryWithSubtype(b.Data, b.Subtype) +} + +// UndefinedEncodeValue is the ValueEncoderFunc for Undefined. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (DefaultValueEncoders) UndefinedEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tUndefined { + return ValueEncoderError{Name: "UndefinedEncodeValue", Types: []reflect.Type{tUndefined}, Received: val} + } + + return vw.WriteUndefined() +} + +// DateTimeEncodeValue is the ValueEncoderFunc for DateTime. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (DefaultValueEncoders) DateTimeEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tDateTime { + return ValueEncoderError{Name: "DateTimeEncodeValue", Types: []reflect.Type{tDateTime}, Received: val} + } + + return vw.WriteDateTime(val.Int()) +} + +// NullEncodeValue is the ValueEncoderFunc for Null. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (DefaultValueEncoders) NullEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tNull { + return ValueEncoderError{Name: "NullEncodeValue", Types: []reflect.Type{tNull}, Received: val} + } + + return vw.WriteNull() +} + +// RegexEncodeValue is the ValueEncoderFunc for Regex. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (DefaultValueEncoders) RegexEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tRegex { + return ValueEncoderError{Name: "RegexEncodeValue", Types: []reflect.Type{tRegex}, Received: val} + } + + regex := val.Interface().(primitive.Regex) + + return vw.WriteRegex(regex.Pattern, regex.Options) +} + +// DBPointerEncodeValue is the ValueEncoderFunc for DBPointer. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (DefaultValueEncoders) DBPointerEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tDBPointer { + return ValueEncoderError{Name: "DBPointerEncodeValue", Types: []reflect.Type{tDBPointer}, Received: val} + } + + dbp := val.Interface().(primitive.DBPointer) + + return vw.WriteDBPointer(dbp.DB, dbp.Pointer) +} + +// TimestampEncodeValue is the ValueEncoderFunc for Timestamp. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (DefaultValueEncoders) TimestampEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tTimestamp { + return ValueEncoderError{Name: "TimestampEncodeValue", Types: []reflect.Type{tTimestamp}, Received: val} + } + + ts := val.Interface().(primitive.Timestamp) + + return vw.WriteTimestamp(ts.T, ts.I) +} + +// MinKeyEncodeValue is the ValueEncoderFunc for MinKey. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (DefaultValueEncoders) MinKeyEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tMinKey { + return ValueEncoderError{Name: "MinKeyEncodeValue", Types: []reflect.Type{tMinKey}, Received: val} + } + + return vw.WriteMinKey() +} + +// MaxKeyEncodeValue is the ValueEncoderFunc for MaxKey. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (DefaultValueEncoders) MaxKeyEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tMaxKey { + return ValueEncoderError{Name: "MaxKeyEncodeValue", Types: []reflect.Type{tMaxKey}, Received: val} + } + + return vw.WriteMaxKey() +} + +// CoreDocumentEncodeValue is the ValueEncoderFunc for bsoncore.Document. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (DefaultValueEncoders) CoreDocumentEncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tCoreDocument { + return ValueEncoderError{Name: "CoreDocumentEncodeValue", Types: []reflect.Type{tCoreDocument}, Received: val} + } + + cdoc := val.Interface().(bsoncore.Document) + + return bsonrw.Copier{}.CopyDocumentFromBytes(vw, cdoc) +} + +// CodeWithScopeEncodeValue is the ValueEncoderFunc for CodeWithScope. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with all default +// value encoders registered. +func (dve DefaultValueEncoders) CodeWithScopeEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tCodeWithScope { + return ValueEncoderError{Name: "CodeWithScopeEncodeValue", Types: []reflect.Type{tCodeWithScope}, Received: val} + } + + cws := val.Interface().(primitive.CodeWithScope) + + dw, err := vw.WriteCodeWithScope(string(cws.Code)) + if err != nil { + return err + } + + sw := sliceWriterPool.Get().(*bsonrw.SliceWriter) + defer sliceWriterPool.Put(sw) + *sw = (*sw)[:0] + + scopeVW := bvwPool.Get(sw) + defer bvwPool.Put(scopeVW) + + encoder, err := ec.LookupEncoder(reflect.TypeOf(cws.Scope)) + if err != nil { + return err + } + + err = encoder.EncodeValue(ec, scopeVW, reflect.ValueOf(cws.Scope)) + if err != nil { + return err + } + + err = bsonrw.Copier{}.CopyBytesToDocumentWriter(dw, *sw) + if err != nil { + return err + } + return dw.WriteDocumentEnd() +} + +// isImplementationNil returns if val is a nil pointer and inter is implemented on a concrete type +func isImplementationNil(val reflect.Value, inter reflect.Type) bool { + vt := val.Type() + for vt.Kind() == reflect.Ptr { + vt = vt.Elem() + } + return vt.Implements(inter) && val.Kind() == reflect.Ptr && val.IsNil() +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/doc.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/doc.go new file mode 100644 index 0000000000..4613e5a1ec --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/doc.go @@ -0,0 +1,95 @@ +// Copyright (C) MongoDB, Inc. 2022-present. +// +// 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 + +// Package bsoncodec provides a system for encoding values to BSON representations and decoding +// values from BSON representations. This package considers both binary BSON and ExtendedJSON as +// BSON representations. The types in this package enable a flexible system for handling this +// encoding and decoding. +// +// The codec system is composed of two parts: +// +// 1) ValueEncoders and ValueDecoders that handle encoding and decoding Go values to and from BSON +// representations. +// +// 2) A Registry that holds these ValueEncoders and ValueDecoders and provides methods for +// retrieving them. +// +// # ValueEncoders and ValueDecoders +// +// The ValueEncoder interface is implemented by types that can encode a provided Go type to BSON. +// The value to encode is provided as a reflect.Value and a bsonrw.ValueWriter is used within the +// EncodeValue method to actually create the BSON representation. For convenience, ValueEncoderFunc +// is provided to allow use of a function with the correct signature as a ValueEncoder. An +// EncodeContext instance is provided to allow implementations to lookup further ValueEncoders and +// to provide configuration information. +// +// The ValueDecoder interface is the inverse of the ValueEncoder. Implementations should ensure that +// the value they receive is settable. Similar to ValueEncoderFunc, ValueDecoderFunc is provided to +// allow the use of a function with the correct signature as a ValueDecoder. A DecodeContext +// instance is provided and serves similar functionality to the EncodeContext. +// +// # Registry +// +// A Registry is a store for ValueEncoders, ValueDecoders, and a type map. See the Registry type +// documentation for examples of registering various custom encoders and decoders. A Registry can +// have three main types of codecs: +// +// 1. Type encoders/decoders - These can be registered using the RegisterTypeEncoder and +// RegisterTypeDecoder methods. The registered codec will be invoked when encoding/decoding a value +// whose type matches the registered type exactly. +// If the registered type is an interface, the codec will be invoked when encoding or decoding +// values whose type is the interface, but not for values with concrete types that implement the +// interface. +// +// 2. Hook encoders/decoders - These can be registered using the RegisterHookEncoder and +// RegisterHookDecoder methods. These methods only accept interface types and the registered codecs +// will be invoked when encoding or decoding values whose types implement the interface. An example +// of a hook defined by the driver is bson.Marshaler. The driver will call the MarshalBSON method +// for any value whose type implements bson.Marshaler, regardless of the value's concrete type. +// +// 3. Type map entries - This can be used to associate a BSON type with a Go type. These type +// associations are used when decoding into a bson.D/bson.M or a struct field of type interface{}. +// For example, by default, BSON int32 and int64 values decode as Go int32 and int64 instances, +// respectively, when decoding into a bson.D. The following code would change the behavior so these +// values decode as Go int instances instead: +// +// intType := reflect.TypeOf(int(0)) +// registry.RegisterTypeMapEntry(bsontype.Int32, intType).RegisterTypeMapEntry(bsontype.Int64, intType) +// +// 4. Kind encoder/decoders - These can be registered using the RegisterDefaultEncoder and +// RegisterDefaultDecoder methods. The registered codec will be invoked when encoding or decoding +// values whose reflect.Kind matches the registered reflect.Kind as long as the value's type doesn't +// match a registered type or hook encoder/decoder first. These methods should be used to change the +// behavior for all values for a specific kind. +// +// # Registry Lookup Procedure +// +// When looking up an encoder in a Registry, the precedence rules are as follows: +// +// 1. A type encoder registered for the exact type of the value. +// +// 2. A hook encoder registered for an interface that is implemented by the value or by a pointer to +// the value. If the value matches multiple hooks (e.g. the type implements bsoncodec.Marshaler and +// bsoncodec.ValueMarshaler), the first one registered will be selected. Note that registries +// constructed using bson.NewRegistry have driver-defined hooks registered for the +// bsoncodec.Marshaler, bsoncodec.ValueMarshaler, and bsoncodec.Proxy interfaces, so those will take +// precedence over any new hooks. +// +// 3. A kind encoder registered for the value's kind. +// +// If all of these lookups fail to find an encoder, an error of type ErrNoEncoder is returned. The +// same precedence rules apply for decoders, with the exception that an error of type ErrNoDecoder +// will be returned if no decoder is found. +// +// # DefaultValueEncoders and DefaultValueDecoders +// +// The DefaultValueEncoders and DefaultValueDecoders types provide a full set of ValueEncoders and +// ValueDecoders for handling a wide range of Go types, including all of the types within the +// primitive package. To make registering these codecs easier, a helper method on each type is +// provided. For the DefaultValueEncoders type the method is called RegisterDefaultEncoders and for +// the DefaultValueDecoders type the method is called RegisterDefaultDecoders, this method also +// handles registering type map entries for each BSON type. +package bsoncodec diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/empty_interface_codec.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/empty_interface_codec.go new file mode 100644 index 0000000000..098368f071 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/empty_interface_codec.go @@ -0,0 +1,173 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "reflect" + + "go.mongodb.org/mongo-driver/bson/bsonoptions" + "go.mongodb.org/mongo-driver/bson/bsonrw" + "go.mongodb.org/mongo-driver/bson/bsontype" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +// EmptyInterfaceCodec is the Codec used for interface{} values. +// +// Deprecated: EmptyInterfaceCodec will not be directly configurable in Go +// Driver 2.0. To configure the empty interface encode and decode behavior, use +// the configuration methods on a [go.mongodb.org/mongo-driver/bson.Encoder] or +// [go.mongodb.org/mongo-driver/bson.Decoder]. To configure the empty interface +// encode and decode behavior for a mongo.Client, use +// [go.mongodb.org/mongo-driver/mongo/options.ClientOptions.SetBSONOptions]. +// +// For example, to configure a mongo.Client to unmarshal BSON binary field +// values as a Go byte slice, use: +// +// opt := options.Client().SetBSONOptions(&options.BSONOptions{ +// BinaryAsSlice: true, +// }) +// +// See the deprecation notice for each field in EmptyInterfaceCodec for the +// corresponding settings. +type EmptyInterfaceCodec struct { + // DecodeBinaryAsSlice causes DecodeValue to unmarshal BSON binary field values that are the + // "Generic" or "Old" BSON binary subtype as a Go byte slice instead of a primitive.Binary. + // + // Deprecated: Use bson.Decoder.BinaryAsSlice or options.BSONOptions.BinaryAsSlice instead. + DecodeBinaryAsSlice bool +} + +var ( + defaultEmptyInterfaceCodec = NewEmptyInterfaceCodec() + + // Assert that defaultEmptyInterfaceCodec satisfies the typeDecoder interface, which allows it + // to be used by collection type decoders (e.g. map, slice, etc) to set individual values in a + // collection. + _ typeDecoder = defaultEmptyInterfaceCodec +) + +// NewEmptyInterfaceCodec returns a EmptyInterfaceCodec with options opts. +// +// Deprecated: NewEmptyInterfaceCodec will not be available in Go Driver 2.0. See +// [EmptyInterfaceCodec] for more details. +func NewEmptyInterfaceCodec(opts ...*bsonoptions.EmptyInterfaceCodecOptions) *EmptyInterfaceCodec { + interfaceOpt := bsonoptions.MergeEmptyInterfaceCodecOptions(opts...) + + codec := EmptyInterfaceCodec{} + if interfaceOpt.DecodeBinaryAsSlice != nil { + codec.DecodeBinaryAsSlice = *interfaceOpt.DecodeBinaryAsSlice + } + return &codec +} + +// EncodeValue is the ValueEncoderFunc for interface{}. +func (eic EmptyInterfaceCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tEmpty { + return ValueEncoderError{Name: "EmptyInterfaceEncodeValue", Types: []reflect.Type{tEmpty}, Received: val} + } + + if val.IsNil() { + return vw.WriteNull() + } + encoder, err := ec.LookupEncoder(val.Elem().Type()) + if err != nil { + return err + } + + return encoder.EncodeValue(ec, vw, val.Elem()) +} + +func (eic EmptyInterfaceCodec) getEmptyInterfaceDecodeType(dc DecodeContext, valueType bsontype.Type) (reflect.Type, error) { + isDocument := valueType == bsontype.Type(0) || valueType == bsontype.EmbeddedDocument + if isDocument { + if dc.defaultDocumentType != nil { + // If the bsontype is an embedded document and the DocumentType is set on the DecodeContext, then return + // that type. + return dc.defaultDocumentType, nil + } + if dc.Ancestor != nil { + // Using ancestor information rather than looking up the type map entry forces consistent decoding. + // If we're decoding into a bson.D, subdocuments should also be decoded as bson.D, even if a type map entry + // has been registered. + return dc.Ancestor, nil + } + } + + rtype, err := dc.LookupTypeMapEntry(valueType) + if err == nil { + return rtype, nil + } + + if isDocument { + // For documents, fallback to looking up a type map entry for bsontype.Type(0) or bsontype.EmbeddedDocument, + // depending on the original valueType. + var lookupType bsontype.Type + switch valueType { + case bsontype.Type(0): + lookupType = bsontype.EmbeddedDocument + case bsontype.EmbeddedDocument: + lookupType = bsontype.Type(0) + } + + rtype, err = dc.LookupTypeMapEntry(lookupType) + if err == nil { + return rtype, nil + } + } + + return nil, err +} + +func (eic EmptyInterfaceCodec) decodeType(dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tEmpty { + return emptyValue, ValueDecoderError{Name: "EmptyInterfaceDecodeValue", Types: []reflect.Type{tEmpty}, Received: reflect.Zero(t)} + } + + rtype, err := eic.getEmptyInterfaceDecodeType(dc, vr.Type()) + if err != nil { + switch vr.Type() { + case bsontype.Null: + return reflect.Zero(t), vr.ReadNull() + default: + return emptyValue, err + } + } + + decoder, err := dc.LookupDecoder(rtype) + if err != nil { + return emptyValue, err + } + + elem, err := decodeTypeOrValue(decoder, dc, vr, rtype) + if err != nil { + return emptyValue, err + } + + if (eic.DecodeBinaryAsSlice || dc.binaryAsSlice) && rtype == tBinary { + binElem := elem.Interface().(primitive.Binary) + if binElem.Subtype == bsontype.BinaryGeneric || binElem.Subtype == bsontype.BinaryBinaryOld { + elem = reflect.ValueOf(binElem.Data) + } + } + + return elem, nil +} + +// DecodeValue is the ValueDecoderFunc for interface{}. +func (eic EmptyInterfaceCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tEmpty { + return ValueDecoderError{Name: "EmptyInterfaceDecodeValue", Types: []reflect.Type{tEmpty}, Received: val} + } + + elem, err := eic.decodeType(dc, vr, val.Type()) + if err != nil { + return err + } + + val.Set(elem) + return nil +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/map_codec.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/map_codec.go new file mode 100644 index 0000000000..d7e00ffa8d --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/map_codec.go @@ -0,0 +1,343 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "encoding" + "errors" + "fmt" + "reflect" + "strconv" + + "go.mongodb.org/mongo-driver/bson/bsonoptions" + "go.mongodb.org/mongo-driver/bson/bsonrw" + "go.mongodb.org/mongo-driver/bson/bsontype" +) + +var defaultMapCodec = NewMapCodec() + +// MapCodec is the Codec used for map values. +// +// Deprecated: MapCodec will not be directly configurable in Go Driver 2.0. To +// configure the map encode and decode behavior, use the configuration methods +// on a [go.mongodb.org/mongo-driver/bson.Encoder] or +// [go.mongodb.org/mongo-driver/bson.Decoder]. To configure the map encode and +// decode behavior for a mongo.Client, use +// [go.mongodb.org/mongo-driver/mongo/options.ClientOptions.SetBSONOptions]. +// +// For example, to configure a mongo.Client to marshal nil Go maps as empty BSON +// documents, use: +// +// opt := options.Client().SetBSONOptions(&options.BSONOptions{ +// NilMapAsEmpty: true, +// }) +// +// See the deprecation notice for each field in MapCodec for the corresponding +// settings. +type MapCodec struct { + // DecodeZerosMap causes DecodeValue to delete any existing values from Go maps in the destination + // value passed to Decode before unmarshaling BSON documents into them. + // + // Deprecated: Use bson.Decoder.ZeroMaps or options.BSONOptions.ZeroMaps instead. + DecodeZerosMap bool + + // EncodeNilAsEmpty causes EncodeValue to marshal nil Go maps as empty BSON documents instead of + // BSON null. + // + // Deprecated: Use bson.Encoder.NilMapAsEmpty or options.BSONOptions.NilMapAsEmpty instead. + EncodeNilAsEmpty bool + + // EncodeKeysWithStringer causes the Encoder to convert Go map keys to BSON document field name + // strings using fmt.Sprintf() instead of the default string conversion logic. + // + // Deprecated: Use bson.Encoder.StringifyMapKeysWithFmt or + // options.BSONOptions.StringifyMapKeysWithFmt instead. + EncodeKeysWithStringer bool +} + +// KeyMarshaler is the interface implemented by an object that can marshal itself into a string key. +// This applies to types used as map keys and is similar to encoding.TextMarshaler. +type KeyMarshaler interface { + MarshalKey() (key string, err error) +} + +// KeyUnmarshaler is the interface implemented by an object that can unmarshal a string representation +// of itself. This applies to types used as map keys and is similar to encoding.TextUnmarshaler. +// +// UnmarshalKey must be able to decode the form generated by MarshalKey. +// UnmarshalKey must copy the text if it wishes to retain the text +// after returning. +type KeyUnmarshaler interface { + UnmarshalKey(key string) error +} + +// NewMapCodec returns a MapCodec with options opts. +// +// Deprecated: NewMapCodec will not be available in Go Driver 2.0. See +// [MapCodec] for more details. +func NewMapCodec(opts ...*bsonoptions.MapCodecOptions) *MapCodec { + mapOpt := bsonoptions.MergeMapCodecOptions(opts...) + + codec := MapCodec{} + if mapOpt.DecodeZerosMap != nil { + codec.DecodeZerosMap = *mapOpt.DecodeZerosMap + } + if mapOpt.EncodeNilAsEmpty != nil { + codec.EncodeNilAsEmpty = *mapOpt.EncodeNilAsEmpty + } + if mapOpt.EncodeKeysWithStringer != nil { + codec.EncodeKeysWithStringer = *mapOpt.EncodeKeysWithStringer + } + return &codec +} + +// EncodeValue is the ValueEncoder for map[*]* types. +func (mc *MapCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Kind() != reflect.Map { + return ValueEncoderError{Name: "MapEncodeValue", Kinds: []reflect.Kind{reflect.Map}, Received: val} + } + + if val.IsNil() && !mc.EncodeNilAsEmpty && !ec.nilMapAsEmpty { + // If we have a nil map but we can't WriteNull, that means we're probably trying to encode + // to a TopLevel document. We can't currently tell if this is what actually happened, but if + // there's a deeper underlying problem, the error will also be returned from WriteDocument, + // so just continue. The operations on a map reflection value are valid, so we can call + // MapKeys within mapEncodeValue without a problem. + err := vw.WriteNull() + if err == nil { + return nil + } + } + + dw, err := vw.WriteDocument() + if err != nil { + return err + } + + return mc.mapEncodeValue(ec, dw, val, nil) +} + +// mapEncodeValue handles encoding of the values of a map. The collisionFn returns +// true if the provided key exists, this is mainly used for inline maps in the +// struct codec. +func (mc *MapCodec) mapEncodeValue(ec EncodeContext, dw bsonrw.DocumentWriter, val reflect.Value, collisionFn func(string) bool) error { + + elemType := val.Type().Elem() + encoder, err := ec.LookupEncoder(elemType) + if err != nil && elemType.Kind() != reflect.Interface { + return err + } + + keys := val.MapKeys() + for _, key := range keys { + keyStr, err := mc.encodeKey(key, ec.stringifyMapKeysWithFmt) + if err != nil { + return err + } + + if collisionFn != nil && collisionFn(keyStr) { + return fmt.Errorf("Key %s of inlined map conflicts with a struct field name", key) + } + + currEncoder, currVal, lookupErr := defaultValueEncoders.lookupElementEncoder(ec, encoder, val.MapIndex(key)) + if lookupErr != nil && !errors.Is(lookupErr, errInvalidValue) { + return lookupErr + } + + vw, err := dw.WriteDocumentElement(keyStr) + if err != nil { + return err + } + + if errors.Is(lookupErr, errInvalidValue) { + err = vw.WriteNull() + if err != nil { + return err + } + continue + } + + err = currEncoder.EncodeValue(ec, vw, currVal) + if err != nil { + return err + } + } + + return dw.WriteDocumentEnd() +} + +// DecodeValue is the ValueDecoder for map[string/decimal]* types. +func (mc *MapCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if val.Kind() != reflect.Map || (!val.CanSet() && val.IsNil()) { + return ValueDecoderError{Name: "MapDecodeValue", Kinds: []reflect.Kind{reflect.Map}, Received: val} + } + + switch vrType := vr.Type(); vrType { + case bsontype.Type(0), bsontype.EmbeddedDocument: + case bsontype.Null: + val.Set(reflect.Zero(val.Type())) + return vr.ReadNull() + case bsontype.Undefined: + val.Set(reflect.Zero(val.Type())) + return vr.ReadUndefined() + default: + return fmt.Errorf("cannot decode %v into a %s", vrType, val.Type()) + } + + dr, err := vr.ReadDocument() + if err != nil { + return err + } + + if val.IsNil() { + val.Set(reflect.MakeMap(val.Type())) + } + + if val.Len() > 0 && (mc.DecodeZerosMap || dc.zeroMaps) { + clearMap(val) + } + + eType := val.Type().Elem() + decoder, err := dc.LookupDecoder(eType) + if err != nil { + return err + } + eTypeDecoder, _ := decoder.(typeDecoder) + + if eType == tEmpty { + dc.Ancestor = val.Type() + } + + keyType := val.Type().Key() + + for { + key, vr, err := dr.ReadElement() + if errors.Is(err, bsonrw.ErrEOD) { + break + } + if err != nil { + return err + } + + k, err := mc.decodeKey(key, keyType) + if err != nil { + return err + } + + elem, err := decodeTypeOrValueWithInfo(decoder, eTypeDecoder, dc, vr, eType, true) + if err != nil { + return newDecodeError(key, err) + } + + val.SetMapIndex(k, elem) + } + return nil +} + +func clearMap(m reflect.Value) { + var none reflect.Value + for _, k := range m.MapKeys() { + m.SetMapIndex(k, none) + } +} + +func (mc *MapCodec) encodeKey(val reflect.Value, encodeKeysWithStringer bool) (string, error) { + if mc.EncodeKeysWithStringer || encodeKeysWithStringer { + return fmt.Sprint(val), nil + } + + // keys of any string type are used directly + if val.Kind() == reflect.String { + return val.String(), nil + } + // KeyMarshalers are marshaled + if km, ok := val.Interface().(KeyMarshaler); ok { + if val.Kind() == reflect.Ptr && val.IsNil() { + return "", nil + } + buf, err := km.MarshalKey() + if err == nil { + return buf, nil + } + return "", err + } + // keys implement encoding.TextMarshaler are marshaled. + if km, ok := val.Interface().(encoding.TextMarshaler); ok { + if val.Kind() == reflect.Ptr && val.IsNil() { + return "", nil + } + + buf, err := km.MarshalText() + if err != nil { + return "", err + } + + return string(buf), nil + } + + switch val.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.FormatInt(val.Int(), 10), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return strconv.FormatUint(val.Uint(), 10), nil + } + return "", fmt.Errorf("unsupported key type: %v", val.Type()) +} + +var keyUnmarshalerType = reflect.TypeOf((*KeyUnmarshaler)(nil)).Elem() +var textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() + +func (mc *MapCodec) decodeKey(key string, keyType reflect.Type) (reflect.Value, error) { + keyVal := reflect.ValueOf(key) + var err error + switch { + // First, if EncodeKeysWithStringer is not enabled, try to decode withKeyUnmarshaler + case !mc.EncodeKeysWithStringer && reflect.PtrTo(keyType).Implements(keyUnmarshalerType): + keyVal = reflect.New(keyType) + v := keyVal.Interface().(KeyUnmarshaler) + err = v.UnmarshalKey(key) + keyVal = keyVal.Elem() + // Try to decode encoding.TextUnmarshalers. + case reflect.PtrTo(keyType).Implements(textUnmarshalerType): + keyVal = reflect.New(keyType) + v := keyVal.Interface().(encoding.TextUnmarshaler) + err = v.UnmarshalText([]byte(key)) + keyVal = keyVal.Elem() + // Otherwise, go to type specific behavior + default: + switch keyType.Kind() { + case reflect.String: + keyVal = reflect.ValueOf(key).Convert(keyType) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + n, parseErr := strconv.ParseInt(key, 10, 64) + if parseErr != nil || reflect.Zero(keyType).OverflowInt(n) { + err = fmt.Errorf("failed to unmarshal number key %v", key) + } + keyVal = reflect.ValueOf(n).Convert(keyType) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + n, parseErr := strconv.ParseUint(key, 10, 64) + if parseErr != nil || reflect.Zero(keyType).OverflowUint(n) { + err = fmt.Errorf("failed to unmarshal number key %v", key) + break + } + keyVal = reflect.ValueOf(n).Convert(keyType) + case reflect.Float32, reflect.Float64: + if mc.EncodeKeysWithStringer { + parsed, err := strconv.ParseFloat(key, 64) + if err != nil { + return keyVal, fmt.Errorf("Map key is defined to be a decimal type (%v) but got error %w", keyType.Kind(), err) + } + keyVal = reflect.ValueOf(parsed) + break + } + fallthrough + default: + return keyVal, fmt.Errorf("unsupported key type: %v", keyType) + } + } + return keyVal, err +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/mode.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/mode.go new file mode 100644 index 0000000000..fbd9f0a9e9 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/mode.go @@ -0,0 +1,65 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import "fmt" + +type mode int + +const ( + _ mode = iota + mTopLevel + mDocument + mArray + mValue + mElement + mCodeWithScope + mSpacer +) + +func (m mode) String() string { + var str string + + switch m { + case mTopLevel: + str = "TopLevel" + case mDocument: + str = "DocumentMode" + case mArray: + str = "ArrayMode" + case mValue: + str = "ValueMode" + case mElement: + str = "ElementMode" + case mCodeWithScope: + str = "CodeWithScopeMode" + case mSpacer: + str = "CodeWithScopeSpacerFrame" + default: + str = "UnknownMode" + } + + return str +} + +// TransitionError is an error returned when an invalid progressing a +// ValueReader or ValueWriter state machine occurs. +type TransitionError struct { + parent mode + current mode + destination mode +} + +func (te TransitionError) Error() string { + if te.destination == mode(0) { + return fmt.Sprintf("invalid state transition: cannot read/write value while in %s", te.current) + } + if te.parent == mode(0) { + return fmt.Sprintf("invalid state transition: %s -> %s", te.current, te.destination) + } + return fmt.Sprintf("invalid state transition: %s -> %s; parent %s", te.current, te.destination, te.parent) +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/pointer_codec.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/pointer_codec.go new file mode 100644 index 0000000000..ddfa4a33e1 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/pointer_codec.go @@ -0,0 +1,108 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "reflect" + + "go.mongodb.org/mongo-driver/bson/bsonrw" + "go.mongodb.org/mongo-driver/bson/bsontype" +) + +var _ ValueEncoder = &PointerCodec{} +var _ ValueDecoder = &PointerCodec{} + +// PointerCodec is the Codec used for pointers. +// +// Deprecated: PointerCodec will not be directly accessible in Go Driver 2.0. To +// override the default pointer encode and decode behavior, create a new registry +// with [go.mongodb.org/mongo-driver/bson.NewRegistry] and register a new +// encoder and decoder for pointers. +// +// For example, +// +// reg := bson.NewRegistry() +// reg.RegisterKindEncoder(reflect.Ptr, myPointerEncoder) +// reg.RegisterKindDecoder(reflect.Ptr, myPointerDecoder) +type PointerCodec struct { + ecache typeEncoderCache + dcache typeDecoderCache +} + +// NewPointerCodec returns a PointerCodec that has been initialized. +// +// Deprecated: NewPointerCodec will not be available in Go Driver 2.0. See +// [PointerCodec] for more details. +func NewPointerCodec() *PointerCodec { + return &PointerCodec{} +} + +// EncodeValue handles encoding a pointer by either encoding it to BSON Null if the pointer is nil +// or looking up an encoder for the type of value the pointer points to. +func (pc *PointerCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if val.Kind() != reflect.Ptr { + if !val.IsValid() { + return vw.WriteNull() + } + return ValueEncoderError{Name: "PointerCodec.EncodeValue", Kinds: []reflect.Kind{reflect.Ptr}, Received: val} + } + + if val.IsNil() { + return vw.WriteNull() + } + + typ := val.Type() + if v, ok := pc.ecache.Load(typ); ok { + if v == nil { + return ErrNoEncoder{Type: typ} + } + return v.EncodeValue(ec, vw, val.Elem()) + } + // TODO(charlie): handle concurrent requests for the same type + enc, err := ec.LookupEncoder(typ.Elem()) + enc = pc.ecache.LoadOrStore(typ, enc) + if err != nil { + return err + } + return enc.EncodeValue(ec, vw, val.Elem()) +} + +// DecodeValue handles decoding a pointer by looking up a decoder for the type it points to and +// using that to decode. If the BSON value is Null, this method will set the pointer to nil. +func (pc *PointerCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Kind() != reflect.Ptr { + return ValueDecoderError{Name: "PointerCodec.DecodeValue", Kinds: []reflect.Kind{reflect.Ptr}, Received: val} + } + + typ := val.Type() + if vr.Type() == bsontype.Null { + val.Set(reflect.Zero(typ)) + return vr.ReadNull() + } + if vr.Type() == bsontype.Undefined { + val.Set(reflect.Zero(typ)) + return vr.ReadUndefined() + } + + if val.IsNil() { + val.Set(reflect.New(typ.Elem())) + } + + if v, ok := pc.dcache.Load(typ); ok { + if v == nil { + return ErrNoDecoder{Type: typ} + } + return v.DecodeValue(dc, vr, val.Elem()) + } + // TODO(charlie): handle concurrent requests for the same type + dec, err := dc.LookupDecoder(typ.Elem()) + dec = pc.dcache.LoadOrStore(typ, dec) + if err != nil { + return err + } + return dec.DecodeValue(dc, vr, val.Elem()) +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/proxy.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/proxy.go new file mode 100644 index 0000000000..4cf2b01ab4 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/proxy.go @@ -0,0 +1,14 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +// Proxy is an interface implemented by types that cannot themselves be directly encoded. Types +// that implement this interface with have ProxyBSON called during the encoding process and that +// value will be encoded in place for the implementer. +type Proxy interface { + ProxyBSON() (interface{}, error) +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/registry.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/registry.go new file mode 100644 index 0000000000..196c491bbb --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/registry.go @@ -0,0 +1,524 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "errors" + "fmt" + "reflect" + "sync" + + "go.mongodb.org/mongo-driver/bson/bsontype" +) + +// ErrNilType is returned when nil is passed to either LookupEncoder or LookupDecoder. +// +// Deprecated: ErrNilType will not be supported in Go Driver 2.0. +var ErrNilType = errors.New("cannot perform a decoder lookup on ") + +// ErrNotPointer is returned when a non-pointer type is provided to LookupDecoder. +// +// Deprecated: ErrNotPointer will not be supported in Go Driver 2.0. +var ErrNotPointer = errors.New("non-pointer provided to LookupDecoder") + +// ErrNoEncoder is returned when there wasn't an encoder available for a type. +// +// Deprecated: ErrNoEncoder will not be supported in Go Driver 2.0. +type ErrNoEncoder struct { + Type reflect.Type +} + +func (ene ErrNoEncoder) Error() string { + if ene.Type == nil { + return "no encoder found for " + } + return "no encoder found for " + ene.Type.String() +} + +// ErrNoDecoder is returned when there wasn't a decoder available for a type. +// +// Deprecated: ErrNoDecoder will not be supported in Go Driver 2.0. +type ErrNoDecoder struct { + Type reflect.Type +} + +func (end ErrNoDecoder) Error() string { + return "no decoder found for " + end.Type.String() +} + +// ErrNoTypeMapEntry is returned when there wasn't a type available for the provided BSON type. +// +// Deprecated: ErrNoTypeMapEntry will not be supported in Go Driver 2.0. +type ErrNoTypeMapEntry struct { + Type bsontype.Type +} + +func (entme ErrNoTypeMapEntry) Error() string { + return "no type map entry found for " + entme.Type.String() +} + +// ErrNotInterface is returned when the provided type is not an interface. +// +// Deprecated: ErrNotInterface will not be supported in Go Driver 2.0. +var ErrNotInterface = errors.New("The provided type is not an interface") + +// A RegistryBuilder is used to build a Registry. This type is not goroutine +// safe. +// +// Deprecated: Use Registry instead. +type RegistryBuilder struct { + registry *Registry +} + +// NewRegistryBuilder creates a new empty RegistryBuilder. +// +// Deprecated: Use NewRegistry instead. +func NewRegistryBuilder() *RegistryBuilder { + return &RegistryBuilder{ + registry: NewRegistry(), + } +} + +// RegisterCodec will register the provided ValueCodec for the provided type. +// +// Deprecated: Use Registry.RegisterTypeEncoder and Registry.RegisterTypeDecoder instead. +func (rb *RegistryBuilder) RegisterCodec(t reflect.Type, codec ValueCodec) *RegistryBuilder { + rb.RegisterTypeEncoder(t, codec) + rb.RegisterTypeDecoder(t, codec) + return rb +} + +// RegisterTypeEncoder will register the provided ValueEncoder for the provided type. +// +// The type will be used directly, so an encoder can be registered for a type and a different encoder can be registered +// for a pointer to that type. +// +// If the given type is an interface, the encoder will be called when marshaling a type that is that interface. It +// will not be called when marshaling a non-interface type that implements the interface. +// +// Deprecated: Use Registry.RegisterTypeEncoder instead. +func (rb *RegistryBuilder) RegisterTypeEncoder(t reflect.Type, enc ValueEncoder) *RegistryBuilder { + rb.registry.RegisterTypeEncoder(t, enc) + return rb +} + +// RegisterHookEncoder will register an encoder for the provided interface type t. This encoder will be called when +// marshaling a type if the type implements t or a pointer to the type implements t. If the provided type is not +// an interface (i.e. t.Kind() != reflect.Interface), this method will panic. +// +// Deprecated: Use Registry.RegisterInterfaceEncoder instead. +func (rb *RegistryBuilder) RegisterHookEncoder(t reflect.Type, enc ValueEncoder) *RegistryBuilder { + rb.registry.RegisterInterfaceEncoder(t, enc) + return rb +} + +// RegisterTypeDecoder will register the provided ValueDecoder for the provided type. +// +// The type will be used directly, so a decoder can be registered for a type and a different decoder can be registered +// for a pointer to that type. +// +// If the given type is an interface, the decoder will be called when unmarshaling into a type that is that interface. +// It will not be called when unmarshaling into a non-interface type that implements the interface. +// +// Deprecated: Use Registry.RegisterTypeDecoder instead. +func (rb *RegistryBuilder) RegisterTypeDecoder(t reflect.Type, dec ValueDecoder) *RegistryBuilder { + rb.registry.RegisterTypeDecoder(t, dec) + return rb +} + +// RegisterHookDecoder will register an decoder for the provided interface type t. This decoder will be called when +// unmarshaling into a type if the type implements t or a pointer to the type implements t. If the provided type is not +// an interface (i.e. t.Kind() != reflect.Interface), this method will panic. +// +// Deprecated: Use Registry.RegisterInterfaceDecoder instead. +func (rb *RegistryBuilder) RegisterHookDecoder(t reflect.Type, dec ValueDecoder) *RegistryBuilder { + rb.registry.RegisterInterfaceDecoder(t, dec) + return rb +} + +// RegisterEncoder registers the provided type and encoder pair. +// +// Deprecated: Use Registry.RegisterTypeEncoder or Registry.RegisterInterfaceEncoder instead. +func (rb *RegistryBuilder) RegisterEncoder(t reflect.Type, enc ValueEncoder) *RegistryBuilder { + if t == tEmpty { + rb.registry.RegisterTypeEncoder(t, enc) + return rb + } + switch t.Kind() { + case reflect.Interface: + rb.registry.RegisterInterfaceEncoder(t, enc) + default: + rb.registry.RegisterTypeEncoder(t, enc) + } + return rb +} + +// RegisterDecoder registers the provided type and decoder pair. +// +// Deprecated: Use Registry.RegisterTypeDecoder or Registry.RegisterInterfaceDecoder instead. +func (rb *RegistryBuilder) RegisterDecoder(t reflect.Type, dec ValueDecoder) *RegistryBuilder { + if t == nil { + rb.registry.RegisterTypeDecoder(t, dec) + return rb + } + if t == tEmpty { + rb.registry.RegisterTypeDecoder(t, dec) + return rb + } + switch t.Kind() { + case reflect.Interface: + rb.registry.RegisterInterfaceDecoder(t, dec) + default: + rb.registry.RegisterTypeDecoder(t, dec) + } + return rb +} + +// RegisterDefaultEncoder will register the provided ValueEncoder to the provided +// kind. +// +// Deprecated: Use Registry.RegisterKindEncoder instead. +func (rb *RegistryBuilder) RegisterDefaultEncoder(kind reflect.Kind, enc ValueEncoder) *RegistryBuilder { + rb.registry.RegisterKindEncoder(kind, enc) + return rb +} + +// RegisterDefaultDecoder will register the provided ValueDecoder to the +// provided kind. +// +// Deprecated: Use Registry.RegisterKindDecoder instead. +func (rb *RegistryBuilder) RegisterDefaultDecoder(kind reflect.Kind, dec ValueDecoder) *RegistryBuilder { + rb.registry.RegisterKindDecoder(kind, dec) + return rb +} + +// RegisterTypeMapEntry will register the provided type to the BSON type. The primary usage for this +// mapping is decoding situations where an empty interface is used and a default type needs to be +// created and decoded into. +// +// By default, BSON documents will decode into interface{} values as bson.D. To change the default type for BSON +// documents, a type map entry for bsontype.EmbeddedDocument should be registered. For example, to force BSON documents +// to decode to bson.Raw, use the following code: +// +// rb.RegisterTypeMapEntry(bsontype.EmbeddedDocument, reflect.TypeOf(bson.Raw{})) +// +// Deprecated: Use Registry.RegisterTypeMapEntry instead. +func (rb *RegistryBuilder) RegisterTypeMapEntry(bt bsontype.Type, rt reflect.Type) *RegistryBuilder { + rb.registry.RegisterTypeMapEntry(bt, rt) + return rb +} + +// Build creates a Registry from the current state of this RegistryBuilder. +// +// Deprecated: Use NewRegistry instead. +func (rb *RegistryBuilder) Build() *Registry { + r := &Registry{ + interfaceEncoders: append([]interfaceValueEncoder(nil), rb.registry.interfaceEncoders...), + interfaceDecoders: append([]interfaceValueDecoder(nil), rb.registry.interfaceDecoders...), + typeEncoders: rb.registry.typeEncoders.Clone(), + typeDecoders: rb.registry.typeDecoders.Clone(), + kindEncoders: rb.registry.kindEncoders.Clone(), + kindDecoders: rb.registry.kindDecoders.Clone(), + } + rb.registry.typeMap.Range(func(k, v interface{}) bool { + if k != nil && v != nil { + r.typeMap.Store(k, v) + } + return true + }) + return r +} + +// A Registry is used to store and retrieve codecs for types and interfaces. This type is the main +// typed passed around and Encoders and Decoders are constructed from it. +type Registry struct { + interfaceEncoders []interfaceValueEncoder + interfaceDecoders []interfaceValueDecoder + typeEncoders *typeEncoderCache + typeDecoders *typeDecoderCache + kindEncoders *kindEncoderCache + kindDecoders *kindDecoderCache + typeMap sync.Map // map[bsontype.Type]reflect.Type +} + +// NewRegistry creates a new empty Registry. +func NewRegistry() *Registry { + return &Registry{ + typeEncoders: new(typeEncoderCache), + typeDecoders: new(typeDecoderCache), + kindEncoders: new(kindEncoderCache), + kindDecoders: new(kindDecoderCache), + } +} + +// RegisterTypeEncoder registers the provided ValueEncoder for the provided type. +// +// The type will be used as provided, so an encoder can be registered for a type and a different +// encoder can be registered for a pointer to that type. +// +// If the given type is an interface, the encoder will be called when marshaling a type that is +// that interface. It will not be called when marshaling a non-interface type that implements the +// interface. To get the latter behavior, call RegisterHookEncoder instead. +// +// RegisterTypeEncoder should not be called concurrently with any other Registry method. +func (r *Registry) RegisterTypeEncoder(valueType reflect.Type, enc ValueEncoder) { + r.typeEncoders.Store(valueType, enc) +} + +// RegisterTypeDecoder registers the provided ValueDecoder for the provided type. +// +// The type will be used as provided, so a decoder can be registered for a type and a different +// decoder can be registered for a pointer to that type. +// +// If the given type is an interface, the decoder will be called when unmarshaling into a type that +// is that interface. It will not be called when unmarshaling into a non-interface type that +// implements the interface. To get the latter behavior, call RegisterHookDecoder instead. +// +// RegisterTypeDecoder should not be called concurrently with any other Registry method. +func (r *Registry) RegisterTypeDecoder(valueType reflect.Type, dec ValueDecoder) { + r.typeDecoders.Store(valueType, dec) +} + +// RegisterKindEncoder registers the provided ValueEncoder for the provided kind. +// +// Use RegisterKindEncoder to register an encoder for any type with the same underlying kind. For +// example, consider the type MyInt defined as +// +// type MyInt int32 +// +// To define an encoder for MyInt and int32, use RegisterKindEncoder like +// +// reg.RegisterKindEncoder(reflect.Int32, myEncoder) +// +// RegisterKindEncoder should not be called concurrently with any other Registry method. +func (r *Registry) RegisterKindEncoder(kind reflect.Kind, enc ValueEncoder) { + r.kindEncoders.Store(kind, enc) +} + +// RegisterKindDecoder registers the provided ValueDecoder for the provided kind. +// +// Use RegisterKindDecoder to register a decoder for any type with the same underlying kind. For +// example, consider the type MyInt defined as +// +// type MyInt int32 +// +// To define an decoder for MyInt and int32, use RegisterKindDecoder like +// +// reg.RegisterKindDecoder(reflect.Int32, myDecoder) +// +// RegisterKindDecoder should not be called concurrently with any other Registry method. +func (r *Registry) RegisterKindDecoder(kind reflect.Kind, dec ValueDecoder) { + r.kindDecoders.Store(kind, dec) +} + +// RegisterInterfaceEncoder registers an encoder for the provided interface type iface. This encoder will +// be called when marshaling a type if the type implements iface or a pointer to the type +// implements iface. If the provided type is not an interface +// (i.e. iface.Kind() != reflect.Interface), this method will panic. +// +// RegisterInterfaceEncoder should not be called concurrently with any other Registry method. +func (r *Registry) RegisterInterfaceEncoder(iface reflect.Type, enc ValueEncoder) { + if iface.Kind() != reflect.Interface { + panicStr := fmt.Errorf("RegisterInterfaceEncoder expects a type with kind reflect.Interface, "+ + "got type %s with kind %s", iface, iface.Kind()) + panic(panicStr) + } + + for idx, encoder := range r.interfaceEncoders { + if encoder.i == iface { + r.interfaceEncoders[idx].ve = enc + return + } + } + + r.interfaceEncoders = append(r.interfaceEncoders, interfaceValueEncoder{i: iface, ve: enc}) +} + +// RegisterInterfaceDecoder registers an decoder for the provided interface type iface. This decoder will +// be called when unmarshaling into a type if the type implements iface or a pointer to the type +// implements iface. If the provided type is not an interface (i.e. iface.Kind() != reflect.Interface), +// this method will panic. +// +// RegisterInterfaceDecoder should not be called concurrently with any other Registry method. +func (r *Registry) RegisterInterfaceDecoder(iface reflect.Type, dec ValueDecoder) { + if iface.Kind() != reflect.Interface { + panicStr := fmt.Errorf("RegisterInterfaceDecoder expects a type with kind reflect.Interface, "+ + "got type %s with kind %s", iface, iface.Kind()) + panic(panicStr) + } + + for idx, decoder := range r.interfaceDecoders { + if decoder.i == iface { + r.interfaceDecoders[idx].vd = dec + return + } + } + + r.interfaceDecoders = append(r.interfaceDecoders, interfaceValueDecoder{i: iface, vd: dec}) +} + +// RegisterTypeMapEntry will register the provided type to the BSON type. The primary usage for this +// mapping is decoding situations where an empty interface is used and a default type needs to be +// created and decoded into. +// +// By default, BSON documents will decode into interface{} values as bson.D. To change the default type for BSON +// documents, a type map entry for bsontype.EmbeddedDocument should be registered. For example, to force BSON documents +// to decode to bson.Raw, use the following code: +// +// reg.RegisterTypeMapEntry(bsontype.EmbeddedDocument, reflect.TypeOf(bson.Raw{})) +func (r *Registry) RegisterTypeMapEntry(bt bsontype.Type, rt reflect.Type) { + r.typeMap.Store(bt, rt) +} + +// LookupEncoder returns the first matching encoder in the Registry. It uses the following lookup +// order: +// +// 1. An encoder registered for the exact type. If the given type is an interface, an encoder +// registered using RegisterTypeEncoder for that interface will be selected. +// +// 2. An encoder registered using RegisterInterfaceEncoder for an interface implemented by the type +// or by a pointer to the type. +// +// 3. An encoder registered using RegisterKindEncoder for the kind of value. +// +// If no encoder is found, an error of type ErrNoEncoder is returned. LookupEncoder is safe for +// concurrent use by multiple goroutines after all codecs and encoders are registered. +func (r *Registry) LookupEncoder(valueType reflect.Type) (ValueEncoder, error) { + if valueType == nil { + return nil, ErrNoEncoder{Type: valueType} + } + enc, found := r.lookupTypeEncoder(valueType) + if found { + if enc == nil { + return nil, ErrNoEncoder{Type: valueType} + } + return enc, nil + } + + enc, found = r.lookupInterfaceEncoder(valueType, true) + if found { + return r.typeEncoders.LoadOrStore(valueType, enc), nil + } + + if v, ok := r.kindEncoders.Load(valueType.Kind()); ok { + return r.storeTypeEncoder(valueType, v), nil + } + return nil, ErrNoEncoder{Type: valueType} +} + +func (r *Registry) storeTypeEncoder(rt reflect.Type, enc ValueEncoder) ValueEncoder { + return r.typeEncoders.LoadOrStore(rt, enc) +} + +func (r *Registry) lookupTypeEncoder(rt reflect.Type) (ValueEncoder, bool) { + return r.typeEncoders.Load(rt) +} + +func (r *Registry) lookupInterfaceEncoder(valueType reflect.Type, allowAddr bool) (ValueEncoder, bool) { + if valueType == nil { + return nil, false + } + for _, ienc := range r.interfaceEncoders { + if valueType.Implements(ienc.i) { + return ienc.ve, true + } + if allowAddr && valueType.Kind() != reflect.Ptr && reflect.PtrTo(valueType).Implements(ienc.i) { + // if *t implements an interface, this will catch if t implements an interface further + // ahead in interfaceEncoders + defaultEnc, found := r.lookupInterfaceEncoder(valueType, false) + if !found { + defaultEnc, _ = r.kindEncoders.Load(valueType.Kind()) + } + return newCondAddrEncoder(ienc.ve, defaultEnc), true + } + } + return nil, false +} + +// LookupDecoder returns the first matching decoder in the Registry. It uses the following lookup +// order: +// +// 1. A decoder registered for the exact type. If the given type is an interface, a decoder +// registered using RegisterTypeDecoder for that interface will be selected. +// +// 2. A decoder registered using RegisterInterfaceDecoder for an interface implemented by the type or by +// a pointer to the type. +// +// 3. A decoder registered using RegisterKindDecoder for the kind of value. +// +// If no decoder is found, an error of type ErrNoDecoder is returned. LookupDecoder is safe for +// concurrent use by multiple goroutines after all codecs and decoders are registered. +func (r *Registry) LookupDecoder(valueType reflect.Type) (ValueDecoder, error) { + if valueType == nil { + return nil, ErrNilType + } + dec, found := r.lookupTypeDecoder(valueType) + if found { + if dec == nil { + return nil, ErrNoDecoder{Type: valueType} + } + return dec, nil + } + + dec, found = r.lookupInterfaceDecoder(valueType, true) + if found { + return r.storeTypeDecoder(valueType, dec), nil + } + + if v, ok := r.kindDecoders.Load(valueType.Kind()); ok { + return r.storeTypeDecoder(valueType, v), nil + } + return nil, ErrNoDecoder{Type: valueType} +} + +func (r *Registry) lookupTypeDecoder(valueType reflect.Type) (ValueDecoder, bool) { + return r.typeDecoders.Load(valueType) +} + +func (r *Registry) storeTypeDecoder(typ reflect.Type, dec ValueDecoder) ValueDecoder { + return r.typeDecoders.LoadOrStore(typ, dec) +} + +func (r *Registry) lookupInterfaceDecoder(valueType reflect.Type, allowAddr bool) (ValueDecoder, bool) { + for _, idec := range r.interfaceDecoders { + if valueType.Implements(idec.i) { + return idec.vd, true + } + if allowAddr && valueType.Kind() != reflect.Ptr && reflect.PtrTo(valueType).Implements(idec.i) { + // if *t implements an interface, this will catch if t implements an interface further + // ahead in interfaceDecoders + defaultDec, found := r.lookupInterfaceDecoder(valueType, false) + if !found { + defaultDec, _ = r.kindDecoders.Load(valueType.Kind()) + } + return newCondAddrDecoder(idec.vd, defaultDec), true + } + } + return nil, false +} + +// LookupTypeMapEntry inspects the registry's type map for a Go type for the corresponding BSON +// type. If no type is found, ErrNoTypeMapEntry is returned. +// +// LookupTypeMapEntry should not be called concurrently with any other Registry method. +func (r *Registry) LookupTypeMapEntry(bt bsontype.Type) (reflect.Type, error) { + v, ok := r.typeMap.Load(bt) + if v == nil || !ok { + return nil, ErrNoTypeMapEntry{Type: bt} + } + return v.(reflect.Type), nil +} + +type interfaceValueEncoder struct { + i reflect.Type + ve ValueEncoder +} + +type interfaceValueDecoder struct { + i reflect.Type + vd ValueDecoder +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/slice_codec.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/slice_codec.go new file mode 100644 index 0000000000..14c9fd2564 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/slice_codec.go @@ -0,0 +1,214 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "errors" + "fmt" + "reflect" + + "go.mongodb.org/mongo-driver/bson/bsonoptions" + "go.mongodb.org/mongo-driver/bson/bsonrw" + "go.mongodb.org/mongo-driver/bson/bsontype" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +var defaultSliceCodec = NewSliceCodec() + +// SliceCodec is the Codec used for slice values. +// +// Deprecated: SliceCodec will not be directly configurable in Go Driver 2.0. To +// configure the slice encode and decode behavior, use the configuration methods +// on a [go.mongodb.org/mongo-driver/bson.Encoder] or +// [go.mongodb.org/mongo-driver/bson.Decoder]. To configure the slice encode and +// decode behavior for a mongo.Client, use +// [go.mongodb.org/mongo-driver/mongo/options.ClientOptions.SetBSONOptions]. +// +// For example, to configure a mongo.Client to marshal nil Go slices as empty +// BSON arrays, use: +// +// opt := options.Client().SetBSONOptions(&options.BSONOptions{ +// NilSliceAsEmpty: true, +// }) +// +// See the deprecation notice for each field in SliceCodec for the corresponding +// settings. +type SliceCodec struct { + // EncodeNilAsEmpty causes EncodeValue to marshal nil Go slices as empty BSON arrays instead of + // BSON null. + // + // Deprecated: Use bson.Encoder.NilSliceAsEmpty instead. + EncodeNilAsEmpty bool +} + +// NewSliceCodec returns a MapCodec with options opts. +// +// Deprecated: NewSliceCodec will not be available in Go Driver 2.0. See +// [SliceCodec] for more details. +func NewSliceCodec(opts ...*bsonoptions.SliceCodecOptions) *SliceCodec { + sliceOpt := bsonoptions.MergeSliceCodecOptions(opts...) + + codec := SliceCodec{} + if sliceOpt.EncodeNilAsEmpty != nil { + codec.EncodeNilAsEmpty = *sliceOpt.EncodeNilAsEmpty + } + return &codec +} + +// EncodeValue is the ValueEncoder for slice types. +func (sc SliceCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Kind() != reflect.Slice { + return ValueEncoderError{Name: "SliceEncodeValue", Kinds: []reflect.Kind{reflect.Slice}, Received: val} + } + + if val.IsNil() && !sc.EncodeNilAsEmpty && !ec.nilSliceAsEmpty { + return vw.WriteNull() + } + + // If we have a []byte we want to treat it as a binary instead of as an array. + if val.Type().Elem() == tByte { + byteSlice := make([]byte, val.Len()) + reflect.Copy(reflect.ValueOf(byteSlice), val) + return vw.WriteBinary(byteSlice) + } + + // If we have a []primitive.E we want to treat it as a document instead of as an array. + if val.Type() == tD || val.Type().ConvertibleTo(tD) { + d := val.Convert(tD).Interface().(primitive.D) + + dw, err := vw.WriteDocument() + if err != nil { + return err + } + + for _, e := range d { + err = encodeElement(ec, dw, e) + if err != nil { + return err + } + } + + return dw.WriteDocumentEnd() + } + + aw, err := vw.WriteArray() + if err != nil { + return err + } + + elemType := val.Type().Elem() + encoder, err := ec.LookupEncoder(elemType) + if err != nil && elemType.Kind() != reflect.Interface { + return err + } + + for idx := 0; idx < val.Len(); idx++ { + currEncoder, currVal, lookupErr := defaultValueEncoders.lookupElementEncoder(ec, encoder, val.Index(idx)) + if lookupErr != nil && !errors.Is(lookupErr, errInvalidValue) { + return lookupErr + } + + vw, err := aw.WriteArrayElement() + if err != nil { + return err + } + + if errors.Is(lookupErr, errInvalidValue) { + err = vw.WriteNull() + if err != nil { + return err + } + continue + } + + err = currEncoder.EncodeValue(ec, vw, currVal) + if err != nil { + return err + } + } + return aw.WriteArrayEnd() +} + +// DecodeValue is the ValueDecoder for slice types. +func (sc *SliceCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Kind() != reflect.Slice { + return ValueDecoderError{Name: "SliceDecodeValue", Kinds: []reflect.Kind{reflect.Slice}, Received: val} + } + + switch vrType := vr.Type(); vrType { + case bsontype.Array: + case bsontype.Null: + val.Set(reflect.Zero(val.Type())) + return vr.ReadNull() + case bsontype.Undefined: + val.Set(reflect.Zero(val.Type())) + return vr.ReadUndefined() + case bsontype.Type(0), bsontype.EmbeddedDocument: + if val.Type().Elem() != tE { + return fmt.Errorf("cannot decode document into %s", val.Type()) + } + case bsontype.Binary: + if val.Type().Elem() != tByte { + return fmt.Errorf("SliceDecodeValue can only decode a binary into a byte array, got %v", vrType) + } + data, subtype, err := vr.ReadBinary() + if err != nil { + return err + } + if subtype != bsontype.BinaryGeneric && subtype != bsontype.BinaryBinaryOld { + return fmt.Errorf("SliceDecodeValue can only be used to decode subtype 0x00 or 0x02 for %s, got %v", bsontype.Binary, subtype) + } + + if val.IsNil() { + val.Set(reflect.MakeSlice(val.Type(), 0, len(data))) + } + val.SetLen(0) + val.Set(reflect.AppendSlice(val, reflect.ValueOf(data))) + return nil + case bsontype.String: + if sliceType := val.Type().Elem(); sliceType != tByte { + return fmt.Errorf("SliceDecodeValue can only decode a string into a byte array, got %v", sliceType) + } + str, err := vr.ReadString() + if err != nil { + return err + } + byteStr := []byte(str) + + if val.IsNil() { + val.Set(reflect.MakeSlice(val.Type(), 0, len(byteStr))) + } + val.SetLen(0) + val.Set(reflect.AppendSlice(val, reflect.ValueOf(byteStr))) + return nil + default: + return fmt.Errorf("cannot decode %v into a slice", vrType) + } + + var elemsFunc func(DecodeContext, bsonrw.ValueReader, reflect.Value) ([]reflect.Value, error) + switch val.Type().Elem() { + case tE: + dc.Ancestor = val.Type() + elemsFunc = defaultValueDecoders.decodeD + default: + elemsFunc = defaultValueDecoders.decodeDefault + } + + elems, err := elemsFunc(dc, vr, val) + if err != nil { + return err + } + + if val.IsNil() { + val.Set(reflect.MakeSlice(val.Type(), 0, len(elems))) + } + + val.SetLen(0) + val.Set(reflect.Append(val, elems...)) + + return nil +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/string_codec.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/string_codec.go new file mode 100644 index 0000000000..a8f885a854 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/string_codec.go @@ -0,0 +1,140 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "fmt" + "reflect" + + "go.mongodb.org/mongo-driver/bson/bsonoptions" + "go.mongodb.org/mongo-driver/bson/bsonrw" + "go.mongodb.org/mongo-driver/bson/bsontype" +) + +// StringCodec is the Codec used for string values. +// +// Deprecated: StringCodec will not be directly accessible in Go Driver 2.0. To +// override the default string encode and decode behavior, create a new registry +// with [go.mongodb.org/mongo-driver/bson.NewRegistry] and register a new +// encoder and decoder for strings. +// +// For example, +// +// reg := bson.NewRegistry() +// reg.RegisterKindEncoder(reflect.String, myStringEncoder) +// reg.RegisterKindDecoder(reflect.String, myStringDecoder) +type StringCodec struct { + // DecodeObjectIDAsHex specifies if object IDs should be decoded as their hex representation. + // If false, a string made from the raw object ID bytes will be used. Defaults to true. + // + // Deprecated: Decoding object IDs as raw bytes will not be supported in Go Driver 2.0. + DecodeObjectIDAsHex bool +} + +var ( + defaultStringCodec = NewStringCodec() + + // Assert that defaultStringCodec satisfies the typeDecoder interface, which allows it to be + // used by collection type decoders (e.g. map, slice, etc) to set individual values in a + // collection. + _ typeDecoder = defaultStringCodec +) + +// NewStringCodec returns a StringCodec with options opts. +// +// Deprecated: NewStringCodec will not be available in Go Driver 2.0. See +// [StringCodec] for more details. +func NewStringCodec(opts ...*bsonoptions.StringCodecOptions) *StringCodec { + stringOpt := bsonoptions.MergeStringCodecOptions(opts...) + return &StringCodec{*stringOpt.DecodeObjectIDAsHex} +} + +// EncodeValue is the ValueEncoder for string types. +func (sc *StringCodec) EncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if val.Kind() != reflect.String { + return ValueEncoderError{ + Name: "StringEncodeValue", + Kinds: []reflect.Kind{reflect.String}, + Received: val, + } + } + + return vw.WriteString(val.String()) +} + +func (sc *StringCodec) decodeType(_ DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t.Kind() != reflect.String { + return emptyValue, ValueDecoderError{ + Name: "StringDecodeValue", + Kinds: []reflect.Kind{reflect.String}, + Received: reflect.Zero(t), + } + } + + var str string + var err error + switch vr.Type() { + case bsontype.String: + str, err = vr.ReadString() + if err != nil { + return emptyValue, err + } + case bsontype.ObjectID: + oid, err := vr.ReadObjectID() + if err != nil { + return emptyValue, err + } + if sc.DecodeObjectIDAsHex { + str = oid.Hex() + } else { + // TODO(GODRIVER-2796): Return an error here instead of decoding to a garbled string. + byteArray := [12]byte(oid) + str = string(byteArray[:]) + } + case bsontype.Symbol: + str, err = vr.ReadSymbol() + if err != nil { + return emptyValue, err + } + case bsontype.Binary: + data, subtype, err := vr.ReadBinary() + if err != nil { + return emptyValue, err + } + if subtype != bsontype.BinaryGeneric && subtype != bsontype.BinaryBinaryOld { + return emptyValue, decodeBinaryError{subtype: subtype, typeName: "string"} + } + str = string(data) + case bsontype.Null: + if err = vr.ReadNull(); err != nil { + return emptyValue, err + } + case bsontype.Undefined: + if err = vr.ReadUndefined(); err != nil { + return emptyValue, err + } + default: + return emptyValue, fmt.Errorf("cannot decode %v into a string type", vr.Type()) + } + + return reflect.ValueOf(str), nil +} + +// DecodeValue is the ValueDecoder for string types. +func (sc *StringCodec) DecodeValue(dctx DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Kind() != reflect.String { + return ValueDecoderError{Name: "StringDecodeValue", Kinds: []reflect.Kind{reflect.String}, Received: val} + } + + elem, err := sc.decodeType(dctx, vr, val.Type()) + if err != nil { + return err + } + + val.SetString(elem.String()) + return nil +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/struct_codec.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/struct_codec.go new file mode 100644 index 0000000000..f8d9690c13 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/struct_codec.go @@ -0,0 +1,736 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "errors" + "fmt" + "reflect" + "sort" + "strings" + "sync" + "time" + + "go.mongodb.org/mongo-driver/bson/bsonoptions" + "go.mongodb.org/mongo-driver/bson/bsonrw" + "go.mongodb.org/mongo-driver/bson/bsontype" +) + +// DecodeError represents an error that occurs when unmarshalling BSON bytes into a native Go type. +type DecodeError struct { + keys []string + wrapped error +} + +// Unwrap returns the underlying error +func (de *DecodeError) Unwrap() error { + return de.wrapped +} + +// Error implements the error interface. +func (de *DecodeError) Error() string { + // The keys are stored in reverse order because the de.keys slice is builtup while propagating the error up the + // stack of BSON keys, so we call de.Keys(), which reverses them. + keyPath := strings.Join(de.Keys(), ".") + return fmt.Sprintf("error decoding key %s: %v", keyPath, de.wrapped) +} + +// Keys returns the BSON key path that caused an error as a slice of strings. The keys in the slice are in top-down +// order. For example, if the document being unmarshalled was {a: {b: {c: 1}}} and the value for c was supposed to be +// a string, the keys slice will be ["a", "b", "c"]. +func (de *DecodeError) Keys() []string { + reversedKeys := make([]string, 0, len(de.keys)) + for idx := len(de.keys) - 1; idx >= 0; idx-- { + reversedKeys = append(reversedKeys, de.keys[idx]) + } + + return reversedKeys +} + +// Zeroer allows custom struct types to implement a report of zero +// state. All struct types that don't implement Zeroer or where IsZero +// returns false are considered to be not zero. +type Zeroer interface { + IsZero() bool +} + +// StructCodec is the Codec used for struct values. +// +// Deprecated: StructCodec will not be directly configurable in Go Driver 2.0. +// To configure the struct encode and decode behavior, use the configuration +// methods on a [go.mongodb.org/mongo-driver/bson.Encoder] or +// [go.mongodb.org/mongo-driver/bson.Decoder]. To configure the struct encode +// and decode behavior for a mongo.Client, use +// [go.mongodb.org/mongo-driver/mongo/options.ClientOptions.SetBSONOptions]. +// +// For example, to configure a mongo.Client to omit zero-value structs when +// using the "omitempty" struct tag, use: +// +// opt := options.Client().SetBSONOptions(&options.BSONOptions{ +// OmitZeroStruct: true, +// }) +// +// See the deprecation notice for each field in StructCodec for the corresponding +// settings. +type StructCodec struct { + cache sync.Map // map[reflect.Type]*structDescription + parser StructTagParser + + // DecodeZeroStruct causes DecodeValue to delete any existing values from Go structs in the + // destination value passed to Decode before unmarshaling BSON documents into them. + // + // Deprecated: Use bson.Decoder.ZeroStructs or options.BSONOptions.ZeroStructs instead. + DecodeZeroStruct bool + + // DecodeDeepZeroInline causes DecodeValue to delete any existing values from Go structs in the + // destination value passed to Decode before unmarshaling BSON documents into them. + // + // Deprecated: DecodeDeepZeroInline will not be supported in Go Driver 2.0. + DecodeDeepZeroInline bool + + // EncodeOmitDefaultStruct causes the Encoder to consider the zero value for a struct (e.g. + // MyStruct{}) as empty and omit it from the marshaled BSON when the "omitempty" struct tag + // option is set. + // + // Deprecated: Use bson.Encoder.OmitZeroStruct or options.BSONOptions.OmitZeroStruct instead. + EncodeOmitDefaultStruct bool + + // AllowUnexportedFields allows encoding and decoding values from un-exported struct fields. + // + // Deprecated: AllowUnexportedFields does not work on recent versions of Go and will not be + // supported in Go Driver 2.0. + AllowUnexportedFields bool + + // OverwriteDuplicatedInlinedFields, if false, causes EncodeValue to return an error if there is + // a duplicate field in the marshaled BSON when the "inline" struct tag option is set. The + // default value is true. + // + // Deprecated: Use bson.Encoder.ErrorOnInlineDuplicates or + // options.BSONOptions.ErrorOnInlineDuplicates instead. + OverwriteDuplicatedInlinedFields bool +} + +var _ ValueEncoder = &StructCodec{} +var _ ValueDecoder = &StructCodec{} + +// NewStructCodec returns a StructCodec that uses p for struct tag parsing. +// +// Deprecated: NewStructCodec will not be available in Go Driver 2.0. See +// [StructCodec] for more details. +func NewStructCodec(p StructTagParser, opts ...*bsonoptions.StructCodecOptions) (*StructCodec, error) { + if p == nil { + return nil, errors.New("a StructTagParser must be provided to NewStructCodec") + } + + structOpt := bsonoptions.MergeStructCodecOptions(opts...) + + codec := &StructCodec{ + parser: p, + } + + if structOpt.DecodeZeroStruct != nil { + codec.DecodeZeroStruct = *structOpt.DecodeZeroStruct + } + if structOpt.DecodeDeepZeroInline != nil { + codec.DecodeDeepZeroInline = *structOpt.DecodeDeepZeroInline + } + if structOpt.EncodeOmitDefaultStruct != nil { + codec.EncodeOmitDefaultStruct = *structOpt.EncodeOmitDefaultStruct + } + if structOpt.OverwriteDuplicatedInlinedFields != nil { + codec.OverwriteDuplicatedInlinedFields = *structOpt.OverwriteDuplicatedInlinedFields + } + if structOpt.AllowUnexportedFields != nil { + codec.AllowUnexportedFields = *structOpt.AllowUnexportedFields + } + + return codec, nil +} + +// EncodeValue handles encoding generic struct types. +func (sc *StructCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Kind() != reflect.Struct { + return ValueEncoderError{Name: "StructCodec.EncodeValue", Kinds: []reflect.Kind{reflect.Struct}, Received: val} + } + + sd, err := sc.describeStruct(ec.Registry, val.Type(), ec.useJSONStructTags, ec.errorOnInlineDuplicates) + if err != nil { + return err + } + + dw, err := vw.WriteDocument() + if err != nil { + return err + } + var rv reflect.Value + for _, desc := range sd.fl { + if desc.inline == nil { + rv = val.Field(desc.idx) + } else { + rv, err = fieldByIndexErr(val, desc.inline) + if err != nil { + continue + } + } + + desc.encoder, rv, err = defaultValueEncoders.lookupElementEncoder(ec, desc.encoder, rv) + + if err != nil && !errors.Is(err, errInvalidValue) { + return err + } + + if errors.Is(err, errInvalidValue) { + if desc.omitEmpty { + continue + } + vw2, err := dw.WriteDocumentElement(desc.name) + if err != nil { + return err + } + err = vw2.WriteNull() + if err != nil { + return err + } + continue + } + + if desc.encoder == nil { + return ErrNoEncoder{Type: rv.Type()} + } + + encoder := desc.encoder + + var empty bool + if cz, ok := encoder.(CodecZeroer); ok { + empty = cz.IsTypeZero(rv.Interface()) + } else if rv.Kind() == reflect.Interface { + // isEmpty will not treat an interface rv as an interface, so we need to check for the + // nil interface separately. + empty = rv.IsNil() + } else { + empty = isEmpty(rv, sc.EncodeOmitDefaultStruct || ec.omitZeroStruct) + } + if desc.omitEmpty && empty { + continue + } + + vw2, err := dw.WriteDocumentElement(desc.name) + if err != nil { + return err + } + + ectx := EncodeContext{ + Registry: ec.Registry, + MinSize: desc.minSize || ec.MinSize, + errorOnInlineDuplicates: ec.errorOnInlineDuplicates, + stringifyMapKeysWithFmt: ec.stringifyMapKeysWithFmt, + nilMapAsEmpty: ec.nilMapAsEmpty, + nilSliceAsEmpty: ec.nilSliceAsEmpty, + nilByteSliceAsEmpty: ec.nilByteSliceAsEmpty, + omitZeroStruct: ec.omitZeroStruct, + useJSONStructTags: ec.useJSONStructTags, + } + err = encoder.EncodeValue(ectx, vw2, rv) + if err != nil { + return err + } + } + + if sd.inlineMap >= 0 { + rv := val.Field(sd.inlineMap) + collisionFn := func(key string) bool { + _, exists := sd.fm[key] + return exists + } + + return defaultMapCodec.mapEncodeValue(ec, dw, rv, collisionFn) + } + + return dw.WriteDocumentEnd() +} + +func newDecodeError(key string, original error) error { + var de *DecodeError + if !errors.As(original, &de) { + return &DecodeError{ + keys: []string{key}, + wrapped: original, + } + } + + de.keys = append(de.keys, key) + return de +} + +// DecodeValue implements the Codec interface. +// By default, map types in val will not be cleared. If a map has existing key/value pairs, it will be extended with the new ones from vr. +// For slices, the decoder will set the length of the slice to zero and append all elements. The underlying array will not be cleared. +func (sc *StructCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Kind() != reflect.Struct { + return ValueDecoderError{Name: "StructCodec.DecodeValue", Kinds: []reflect.Kind{reflect.Struct}, Received: val} + } + + switch vrType := vr.Type(); vrType { + case bsontype.Type(0), bsontype.EmbeddedDocument: + case bsontype.Null: + if err := vr.ReadNull(); err != nil { + return err + } + + val.Set(reflect.Zero(val.Type())) + return nil + case bsontype.Undefined: + if err := vr.ReadUndefined(); err != nil { + return err + } + + val.Set(reflect.Zero(val.Type())) + return nil + default: + return fmt.Errorf("cannot decode %v into a %s", vrType, val.Type()) + } + + sd, err := sc.describeStruct(dc.Registry, val.Type(), dc.useJSONStructTags, false) + if err != nil { + return err + } + + if sc.DecodeZeroStruct || dc.zeroStructs { + val.Set(reflect.Zero(val.Type())) + } + if sc.DecodeDeepZeroInline && sd.inline { + val.Set(deepZero(val.Type())) + } + + var decoder ValueDecoder + var inlineMap reflect.Value + if sd.inlineMap >= 0 { + inlineMap = val.Field(sd.inlineMap) + decoder, err = dc.LookupDecoder(inlineMap.Type().Elem()) + if err != nil { + return err + } + } + + dr, err := vr.ReadDocument() + if err != nil { + return err + } + + for { + name, vr, err := dr.ReadElement() + if errors.Is(err, bsonrw.ErrEOD) { + break + } + if err != nil { + return err + } + + fd, exists := sd.fm[name] + if !exists { + // if the original name isn't found in the struct description, try again with the name in lowercase + // this could match if a BSON tag isn't specified because by default, describeStruct lowercases all field + // names + fd, exists = sd.fm[strings.ToLower(name)] + } + + if !exists { + if sd.inlineMap < 0 { + // The encoding/json package requires a flag to return on error for non-existent fields. + // This functionality seems appropriate for the struct codec. + err = vr.Skip() + if err != nil { + return err + } + continue + } + + if inlineMap.IsNil() { + inlineMap.Set(reflect.MakeMap(inlineMap.Type())) + } + + elem := reflect.New(inlineMap.Type().Elem()).Elem() + dc.Ancestor = inlineMap.Type() + err = decoder.DecodeValue(dc, vr, elem) + if err != nil { + return err + } + inlineMap.SetMapIndex(reflect.ValueOf(name), elem) + continue + } + + var field reflect.Value + if fd.inline == nil { + field = val.Field(fd.idx) + } else { + field, err = getInlineField(val, fd.inline) + if err != nil { + return err + } + } + + if !field.CanSet() { // Being settable is a super set of being addressable. + innerErr := fmt.Errorf("field %v is not settable", field) + return newDecodeError(fd.name, innerErr) + } + if field.Kind() == reflect.Ptr && field.IsNil() { + field.Set(reflect.New(field.Type().Elem())) + } + field = field.Addr() + + dctx := DecodeContext{ + Registry: dc.Registry, + Truncate: fd.truncate || dc.Truncate, + defaultDocumentType: dc.defaultDocumentType, + binaryAsSlice: dc.binaryAsSlice, + useJSONStructTags: dc.useJSONStructTags, + useLocalTimeZone: dc.useLocalTimeZone, + zeroMaps: dc.zeroMaps, + zeroStructs: dc.zeroStructs, + } + + if fd.decoder == nil { + return newDecodeError(fd.name, ErrNoDecoder{Type: field.Elem().Type()}) + } + + err = fd.decoder.DecodeValue(dctx, vr, field.Elem()) + if err != nil { + return newDecodeError(fd.name, err) + } + } + + return nil +} + +func isEmpty(v reflect.Value, omitZeroStruct bool) bool { + kind := v.Kind() + if (kind != reflect.Ptr || !v.IsNil()) && v.Type().Implements(tZeroer) { + return v.Interface().(Zeroer).IsZero() + } + switch kind { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Struct: + if !omitZeroStruct { + return false + } + vt := v.Type() + if vt == tTime { + return v.Interface().(time.Time).IsZero() + } + numField := vt.NumField() + for i := 0; i < numField; i++ { + ff := vt.Field(i) + if ff.PkgPath != "" && !ff.Anonymous { + continue // Private field + } + if !isEmpty(v.Field(i), omitZeroStruct) { + return false + } + } + return true + } + return !v.IsValid() || v.IsZero() +} + +type structDescription struct { + fm map[string]fieldDescription + fl []fieldDescription + inlineMap int + inline bool +} + +type fieldDescription struct { + name string // BSON key name + fieldName string // struct field name + idx int + omitEmpty bool + minSize bool + truncate bool + inline []int + encoder ValueEncoder + decoder ValueDecoder +} + +type byIndex []fieldDescription + +func (bi byIndex) Len() int { return len(bi) } + +func (bi byIndex) Swap(i, j int) { bi[i], bi[j] = bi[j], bi[i] } + +func (bi byIndex) Less(i, j int) bool { + // If a field is inlined, its index in the top level struct is stored at inline[0] + iIdx, jIdx := bi[i].idx, bi[j].idx + if len(bi[i].inline) > 0 { + iIdx = bi[i].inline[0] + } + if len(bi[j].inline) > 0 { + jIdx = bi[j].inline[0] + } + if iIdx != jIdx { + return iIdx < jIdx + } + for k, biik := range bi[i].inline { + if k >= len(bi[j].inline) { + return false + } + if biik != bi[j].inline[k] { + return biik < bi[j].inline[k] + } + } + return len(bi[i].inline) < len(bi[j].inline) +} + +func (sc *StructCodec) describeStruct( + r *Registry, + t reflect.Type, + useJSONStructTags bool, + errorOnDuplicates bool, +) (*structDescription, error) { + // We need to analyze the struct, including getting the tags, collecting + // information about inlining, and create a map of the field name to the field. + if v, ok := sc.cache.Load(t); ok { + return v.(*structDescription), nil + } + // TODO(charlie): Only describe the struct once when called + // concurrently with the same type. + ds, err := sc.describeStructSlow(r, t, useJSONStructTags, errorOnDuplicates) + if err != nil { + return nil, err + } + if v, loaded := sc.cache.LoadOrStore(t, ds); loaded { + ds = v.(*structDescription) + } + return ds, nil +} + +func (sc *StructCodec) describeStructSlow( + r *Registry, + t reflect.Type, + useJSONStructTags bool, + errorOnDuplicates bool, +) (*structDescription, error) { + numFields := t.NumField() + sd := &structDescription{ + fm: make(map[string]fieldDescription, numFields), + fl: make([]fieldDescription, 0, numFields), + inlineMap: -1, + } + + var fields []fieldDescription + for i := 0; i < numFields; i++ { + sf := t.Field(i) + if sf.PkgPath != "" && (!sc.AllowUnexportedFields || !sf.Anonymous) { + // field is private or unexported fields aren't allowed, ignore + continue + } + + sfType := sf.Type + encoder, err := r.LookupEncoder(sfType) + if err != nil { + encoder = nil + } + decoder, err := r.LookupDecoder(sfType) + if err != nil { + decoder = nil + } + + description := fieldDescription{ + fieldName: sf.Name, + idx: i, + encoder: encoder, + decoder: decoder, + } + + var stags StructTags + // If the caller requested that we use JSON struct tags, use the JSONFallbackStructTagParser + // instead of the parser defined on the codec. + if useJSONStructTags { + stags, err = JSONFallbackStructTagParser.ParseStructTags(sf) + } else { + stags, err = sc.parser.ParseStructTags(sf) + } + if err != nil { + return nil, err + } + if stags.Skip { + continue + } + description.name = stags.Name + description.omitEmpty = stags.OmitEmpty + description.minSize = stags.MinSize + description.truncate = stags.Truncate + + if stags.Inline { + sd.inline = true + switch sfType.Kind() { + case reflect.Map: + if sd.inlineMap >= 0 { + return nil, errors.New("(struct " + t.String() + ") multiple inline maps") + } + if sfType.Key() != tString { + return nil, errors.New("(struct " + t.String() + ") inline map must have a string keys") + } + sd.inlineMap = description.idx + case reflect.Ptr: + sfType = sfType.Elem() + if sfType.Kind() != reflect.Struct { + return nil, fmt.Errorf("(struct %s) inline fields must be a struct, a struct pointer, or a map", t.String()) + } + fallthrough + case reflect.Struct: + inlinesf, err := sc.describeStruct(r, sfType, useJSONStructTags, errorOnDuplicates) + if err != nil { + return nil, err + } + for _, fd := range inlinesf.fl { + if fd.inline == nil { + fd.inline = []int{i, fd.idx} + } else { + fd.inline = append([]int{i}, fd.inline...) + } + fields = append(fields, fd) + + } + default: + return nil, fmt.Errorf("(struct %s) inline fields must be a struct, a struct pointer, or a map", t.String()) + } + continue + } + fields = append(fields, description) + } + + // Sort fieldDescriptions by name and use dominance rules to determine which should be added for each name + sort.Slice(fields, func(i, j int) bool { + x := fields + // sort field by name, breaking ties with depth, then + // breaking ties with index sequence. + if x[i].name != x[j].name { + return x[i].name < x[j].name + } + if len(x[i].inline) != len(x[j].inline) { + return len(x[i].inline) < len(x[j].inline) + } + return byIndex(x).Less(i, j) + }) + + for advance, i := 0, 0; i < len(fields); i += advance { + // One iteration per name. + // Find the sequence of fields with the name of this first field. + fi := fields[i] + name := fi.name + for advance = 1; i+advance < len(fields); advance++ { + fj := fields[i+advance] + if fj.name != name { + break + } + } + if advance == 1 { // Only one field with this name + sd.fl = append(sd.fl, fi) + sd.fm[name] = fi + continue + } + dominant, ok := dominantField(fields[i : i+advance]) + if !ok || !sc.OverwriteDuplicatedInlinedFields || errorOnDuplicates { + return nil, fmt.Errorf("struct %s has duplicated key %s", t.String(), name) + } + sd.fl = append(sd.fl, dominant) + sd.fm[name] = dominant + } + + sort.Sort(byIndex(sd.fl)) + + return sd, nil +} + +// dominantField looks through the fields, all of which are known to +// have the same name, to find the single field that dominates the +// others using Go's inlining rules. If there are multiple top-level +// fields, the boolean will be false: This condition is an error in Go +// and we skip all the fields. +func dominantField(fields []fieldDescription) (fieldDescription, bool) { + // The fields are sorted in increasing index-length order, then by presence of tag. + // That means that the first field is the dominant one. We need only check + // for error cases: two fields at top level. + if len(fields) > 1 && + len(fields[0].inline) == len(fields[1].inline) { + return fieldDescription{}, false + } + return fields[0], true +} + +func fieldByIndexErr(v reflect.Value, index []int) (result reflect.Value, err error) { + defer func() { + if recovered := recover(); recovered != nil { + switch r := recovered.(type) { + case string: + err = fmt.Errorf("%s", r) + case error: + err = r + } + } + }() + + result = v.FieldByIndex(index) + return +} + +func getInlineField(val reflect.Value, index []int) (reflect.Value, error) { + field, err := fieldByIndexErr(val, index) + if err == nil { + return field, nil + } + + // if parent of this element doesn't exist, fix its parent + inlineParent := index[:len(index)-1] + var fParent reflect.Value + if fParent, err = fieldByIndexErr(val, inlineParent); err != nil { + fParent, err = getInlineField(val, inlineParent) + if err != nil { + return fParent, err + } + } + fParent.Set(reflect.New(fParent.Type().Elem())) + + return fieldByIndexErr(val, index) +} + +// DeepZero returns recursive zero object +func deepZero(st reflect.Type) (result reflect.Value) { + if st.Kind() == reflect.Struct { + numField := st.NumField() + for i := 0; i < numField; i++ { + if result == emptyValue { + result = reflect.Indirect(reflect.New(st)) + } + f := result.Field(i) + if f.CanInterface() { + if f.Type().Kind() == reflect.Struct { + result.Field(i).Set(recursivePointerTo(deepZero(f.Type().Elem()))) + } + } + } + } + return result +} + +// recursivePointerTo calls reflect.New(v.Type) but recursively for its fields inside +func recursivePointerTo(v reflect.Value) reflect.Value { + v = reflect.Indirect(v) + result := reflect.New(v.Type()) + if v.Kind() == reflect.Struct { + for i := 0; i < v.NumField(); i++ { + if f := v.Field(i); f.Kind() == reflect.Ptr { + if f.Elem().Kind() == reflect.Struct { + result.Elem().Field(i).Set(recursivePointerTo(f)) + } + } + } + } + + return result +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/struct_tag_parser.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/struct_tag_parser.go new file mode 100644 index 0000000000..18d85bfb03 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/struct_tag_parser.go @@ -0,0 +1,148 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "reflect" + "strings" +) + +// StructTagParser returns the struct tags for a given struct field. +// +// Deprecated: Defining custom BSON struct tag parsers will not be supported in Go Driver 2.0. +type StructTagParser interface { + ParseStructTags(reflect.StructField) (StructTags, error) +} + +// StructTagParserFunc is an adapter that allows a generic function to be used +// as a StructTagParser. +// +// Deprecated: Defining custom BSON struct tag parsers will not be supported in Go Driver 2.0. +type StructTagParserFunc func(reflect.StructField) (StructTags, error) + +// ParseStructTags implements the StructTagParser interface. +func (stpf StructTagParserFunc) ParseStructTags(sf reflect.StructField) (StructTags, error) { + return stpf(sf) +} + +// StructTags represents the struct tag fields that the StructCodec uses during +// the encoding and decoding process. +// +// In the case of a struct, the lowercased field name is used as the key for each exported +// field but this behavior may be changed using a struct tag. The tag may also contain flags to +// adjust the marshalling behavior for the field. +// +// The properties are defined below: +// +// OmitEmpty Only include the field if it's not set to the zero value for the type or to +// empty slices or maps. +// +// MinSize Marshal an integer of a type larger than 32 bits value as an int32, if that's +// feasible while preserving the numeric value. +// +// Truncate When unmarshaling a BSON double, it is permitted to lose precision to fit within +// a float32. +// +// Inline Inline the field, which must be a struct or a map, causing all of its fields +// or keys to be processed as if they were part of the outer struct. For maps, +// keys must not conflict with the bson keys of other struct fields. +// +// Skip This struct field should be skipped. This is usually denoted by parsing a "-" +// for the name. +// +// Deprecated: Defining custom BSON struct tag parsers will not be supported in Go Driver 2.0. +type StructTags struct { + Name string + OmitEmpty bool + MinSize bool + Truncate bool + Inline bool + Skip bool +} + +// DefaultStructTagParser is the StructTagParser used by the StructCodec by default. +// It will handle the bson struct tag. See the documentation for StructTags to see +// what each of the returned fields means. +// +// If there is no name in the struct tag fields, the struct field name is lowercased. +// The tag formats accepted are: +// +// "[][,[,]]" +// +// `(...) bson:"[][,[,]]" (...)` +// +// An example: +// +// type T struct { +// A bool +// B int "myb" +// C string "myc,omitempty" +// D string `bson:",omitempty" json:"jsonkey"` +// E int64 ",minsize" +// F int64 "myf,omitempty,minsize" +// } +// +// A struct tag either consisting entirely of '-' or with a bson key with a +// value consisting entirely of '-' will return a StructTags with Skip true and +// the remaining fields will be their default values. +// +// Deprecated: DefaultStructTagParser will be removed in Go Driver 2.0. +var DefaultStructTagParser StructTagParserFunc = func(sf reflect.StructField) (StructTags, error) { + key := strings.ToLower(sf.Name) + tag, ok := sf.Tag.Lookup("bson") + if !ok && !strings.Contains(string(sf.Tag), ":") && len(sf.Tag) > 0 { + tag = string(sf.Tag) + } + return parseTags(key, tag) +} + +func parseTags(key string, tag string) (StructTags, error) { + var st StructTags + if tag == "-" { + st.Skip = true + return st, nil + } + + for idx, str := range strings.Split(tag, ",") { + if idx == 0 && str != "" { + key = str + } + switch str { + case "omitempty": + st.OmitEmpty = true + case "minsize": + st.MinSize = true + case "truncate": + st.Truncate = true + case "inline": + st.Inline = true + } + } + + st.Name = key + + return st, nil +} + +// JSONFallbackStructTagParser has the same behavior as DefaultStructTagParser +// but will also fallback to parsing the json tag instead on a field where the +// bson tag isn't available. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.UseJSONStructTags] and +// [go.mongodb.org/mongo-driver/bson.Decoder.UseJSONStructTags] instead. +var JSONFallbackStructTagParser StructTagParserFunc = func(sf reflect.StructField) (StructTags, error) { + key := strings.ToLower(sf.Name) + tag, ok := sf.Tag.Lookup("bson") + if !ok { + tag, ok = sf.Tag.Lookup("json") + } + if !ok && !strings.Contains(string(sf.Tag), ":") && len(sf.Tag) > 0 { + tag = string(sf.Tag) + } + + return parseTags(key, tag) +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/time_codec.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/time_codec.go new file mode 100644 index 0000000000..22fb762c41 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/time_codec.go @@ -0,0 +1,151 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "fmt" + "reflect" + "time" + + "go.mongodb.org/mongo-driver/bson/bsonoptions" + "go.mongodb.org/mongo-driver/bson/bsonrw" + "go.mongodb.org/mongo-driver/bson/bsontype" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +const ( + timeFormatString = "2006-01-02T15:04:05.999Z07:00" +) + +// TimeCodec is the Codec used for time.Time values. +// +// Deprecated: TimeCodec will not be directly configurable in Go Driver 2.0. +// To configure the time.Time encode and decode behavior, use the configuration +// methods on a [go.mongodb.org/mongo-driver/bson.Encoder] or +// [go.mongodb.org/mongo-driver/bson.Decoder]. To configure the time.Time encode +// and decode behavior for a mongo.Client, use +// [go.mongodb.org/mongo-driver/mongo/options.ClientOptions.SetBSONOptions]. +// +// For example, to configure a mongo.Client to ..., use: +// +// opt := options.Client().SetBSONOptions(&options.BSONOptions{ +// UseLocalTimeZone: true, +// }) +// +// See the deprecation notice for each field in TimeCodec for the corresponding +// settings. +type TimeCodec struct { + // UseLocalTimeZone specifies if we should decode into the local time zone. Defaults to false. + // + // Deprecated: Use bson.Decoder.UseLocalTimeZone or options.BSONOptions.UseLocalTimeZone + // instead. + UseLocalTimeZone bool +} + +var ( + defaultTimeCodec = NewTimeCodec() + + // Assert that defaultTimeCodec satisfies the typeDecoder interface, which allows it to be used + // by collection type decoders (e.g. map, slice, etc) to set individual values in a collection. + _ typeDecoder = defaultTimeCodec +) + +// NewTimeCodec returns a TimeCodec with options opts. +// +// Deprecated: NewTimeCodec will not be available in Go Driver 2.0. See +// [TimeCodec] for more details. +func NewTimeCodec(opts ...*bsonoptions.TimeCodecOptions) *TimeCodec { + timeOpt := bsonoptions.MergeTimeCodecOptions(opts...) + + codec := TimeCodec{} + if timeOpt.UseLocalTimeZone != nil { + codec.UseLocalTimeZone = *timeOpt.UseLocalTimeZone + } + return &codec +} + +func (tc *TimeCodec) decodeType(dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + if t != tTime { + return emptyValue, ValueDecoderError{ + Name: "TimeDecodeValue", + Types: []reflect.Type{tTime}, + Received: reflect.Zero(t), + } + } + + var timeVal time.Time + switch vrType := vr.Type(); vrType { + case bsontype.DateTime: + dt, err := vr.ReadDateTime() + if err != nil { + return emptyValue, err + } + timeVal = time.Unix(dt/1000, dt%1000*1000000) + case bsontype.String: + // assume strings are in the isoTimeFormat + timeStr, err := vr.ReadString() + if err != nil { + return emptyValue, err + } + timeVal, err = time.Parse(timeFormatString, timeStr) + if err != nil { + return emptyValue, err + } + case bsontype.Int64: + i64, err := vr.ReadInt64() + if err != nil { + return emptyValue, err + } + timeVal = time.Unix(i64/1000, i64%1000*1000000) + case bsontype.Timestamp: + t, _, err := vr.ReadTimestamp() + if err != nil { + return emptyValue, err + } + timeVal = time.Unix(int64(t), 0) + case bsontype.Null: + if err := vr.ReadNull(); err != nil { + return emptyValue, err + } + case bsontype.Undefined: + if err := vr.ReadUndefined(); err != nil { + return emptyValue, err + } + default: + return emptyValue, fmt.Errorf("cannot decode %v into a time.Time", vrType) + } + + if !tc.UseLocalTimeZone && !dc.useLocalTimeZone { + timeVal = timeVal.UTC() + } + return reflect.ValueOf(timeVal), nil +} + +// DecodeValue is the ValueDecoderFunc for time.Time. +func (tc *TimeCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() || val.Type() != tTime { + return ValueDecoderError{Name: "TimeDecodeValue", Types: []reflect.Type{tTime}, Received: val} + } + + elem, err := tc.decodeType(dc, vr, tTime) + if err != nil { + return err + } + + val.Set(elem) + return nil +} + +// EncodeValue is the ValueEncoderFunc for time.TIme. +func (tc *TimeCodec) EncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + if !val.IsValid() || val.Type() != tTime { + return ValueEncoderError{Name: "TimeEncodeValue", Types: []reflect.Type{tTime}, Received: val} + } + tt := val.Interface().(time.Time) + dt := primitive.NewDateTimeFromTime(tt) + return vw.WriteDateTime(int64(dt)) +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/types.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/types.go new file mode 100644 index 0000000000..6ade17b7d3 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/types.go @@ -0,0 +1,58 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "encoding/json" + "net/url" + "reflect" + "time" + + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" +) + +var tBool = reflect.TypeOf(false) +var tFloat64 = reflect.TypeOf(float64(0)) +var tInt32 = reflect.TypeOf(int32(0)) +var tInt64 = reflect.TypeOf(int64(0)) +var tString = reflect.TypeOf("") +var tTime = reflect.TypeOf(time.Time{}) + +var tEmpty = reflect.TypeOf((*interface{})(nil)).Elem() +var tByteSlice = reflect.TypeOf([]byte(nil)) +var tByte = reflect.TypeOf(byte(0x00)) +var tURL = reflect.TypeOf(url.URL{}) +var tJSONNumber = reflect.TypeOf(json.Number("")) + +var tValueMarshaler = reflect.TypeOf((*ValueMarshaler)(nil)).Elem() +var tValueUnmarshaler = reflect.TypeOf((*ValueUnmarshaler)(nil)).Elem() +var tMarshaler = reflect.TypeOf((*Marshaler)(nil)).Elem() +var tUnmarshaler = reflect.TypeOf((*Unmarshaler)(nil)).Elem() +var tProxy = reflect.TypeOf((*Proxy)(nil)).Elem() +var tZeroer = reflect.TypeOf((*Zeroer)(nil)).Elem() + +var tBinary = reflect.TypeOf(primitive.Binary{}) +var tUndefined = reflect.TypeOf(primitive.Undefined{}) +var tOID = reflect.TypeOf(primitive.ObjectID{}) +var tDateTime = reflect.TypeOf(primitive.DateTime(0)) +var tNull = reflect.TypeOf(primitive.Null{}) +var tRegex = reflect.TypeOf(primitive.Regex{}) +var tCodeWithScope = reflect.TypeOf(primitive.CodeWithScope{}) +var tDBPointer = reflect.TypeOf(primitive.DBPointer{}) +var tJavaScript = reflect.TypeOf(primitive.JavaScript("")) +var tSymbol = reflect.TypeOf(primitive.Symbol("")) +var tTimestamp = reflect.TypeOf(primitive.Timestamp{}) +var tDecimal = reflect.TypeOf(primitive.Decimal128{}) +var tMinKey = reflect.TypeOf(primitive.MinKey{}) +var tMaxKey = reflect.TypeOf(primitive.MaxKey{}) +var tD = reflect.TypeOf(primitive.D{}) +var tA = reflect.TypeOf(primitive.A{}) +var tE = reflect.TypeOf(primitive.E{}) + +var tCoreDocument = reflect.TypeOf(bsoncore.Document{}) +var tCoreArray = reflect.TypeOf(bsoncore.Array{}) diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/uint_codec.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/uint_codec.go new file mode 100644 index 0000000000..39b07135b1 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/uint_codec.go @@ -0,0 +1,202 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsoncodec + +import ( + "fmt" + "math" + "reflect" + + "go.mongodb.org/mongo-driver/bson/bsonoptions" + "go.mongodb.org/mongo-driver/bson/bsonrw" + "go.mongodb.org/mongo-driver/bson/bsontype" +) + +// UIntCodec is the Codec used for uint values. +// +// Deprecated: UIntCodec will not be directly configurable in Go Driver 2.0. To +// configure the uint encode and decode behavior, use the configuration methods +// on a [go.mongodb.org/mongo-driver/bson.Encoder] or +// [go.mongodb.org/mongo-driver/bson.Decoder]. To configure the uint encode and +// decode behavior for a mongo.Client, use +// [go.mongodb.org/mongo-driver/mongo/options.ClientOptions.SetBSONOptions]. +// +// For example, to configure a mongo.Client to marshal Go uint values as the +// minimum BSON int size that can represent the value, use: +// +// opt := options.Client().SetBSONOptions(&options.BSONOptions{ +// IntMinSize: true, +// }) +// +// See the deprecation notice for each field in UIntCodec for the corresponding +// settings. +type UIntCodec struct { + // EncodeToMinSize causes EncodeValue to marshal Go uint values (excluding uint64) as the + // minimum BSON int size (either 32-bit or 64-bit) that can represent the integer value. + // + // Deprecated: Use bson.Encoder.IntMinSize or options.BSONOptions.IntMinSize instead. + EncodeToMinSize bool +} + +var ( + defaultUIntCodec = NewUIntCodec() + + // Assert that defaultUIntCodec satisfies the typeDecoder interface, which allows it to be used + // by collection type decoders (e.g. map, slice, etc) to set individual values in a collection. + _ typeDecoder = defaultUIntCodec +) + +// NewUIntCodec returns a UIntCodec with options opts. +// +// Deprecated: NewUIntCodec will not be available in Go Driver 2.0. See +// [UIntCodec] for more details. +func NewUIntCodec(opts ...*bsonoptions.UIntCodecOptions) *UIntCodec { + uintOpt := bsonoptions.MergeUIntCodecOptions(opts...) + + codec := UIntCodec{} + if uintOpt.EncodeToMinSize != nil { + codec.EncodeToMinSize = *uintOpt.EncodeToMinSize + } + return &codec +} + +// EncodeValue is the ValueEncoder for uint types. +func (uic *UIntCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { + switch val.Kind() { + case reflect.Uint8, reflect.Uint16: + return vw.WriteInt32(int32(val.Uint())) + case reflect.Uint, reflect.Uint32, reflect.Uint64: + u64 := val.Uint() + + // If ec.MinSize or if encodeToMinSize is true for a non-uint64 value we should write val as an int32 + useMinSize := ec.MinSize || (uic.EncodeToMinSize && val.Kind() != reflect.Uint64) + + if u64 <= math.MaxInt32 && useMinSize { + return vw.WriteInt32(int32(u64)) + } + if u64 > math.MaxInt64 { + return fmt.Errorf("%d overflows int64", u64) + } + return vw.WriteInt64(int64(u64)) + } + + return ValueEncoderError{ + Name: "UintEncodeValue", + Kinds: []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint}, + Received: val, + } +} + +func (uic *UIntCodec) decodeType(dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) { + var i64 int64 + var err error + switch vrType := vr.Type(); vrType { + case bsontype.Int32: + i32, err := vr.ReadInt32() + if err != nil { + return emptyValue, err + } + i64 = int64(i32) + case bsontype.Int64: + i64, err = vr.ReadInt64() + if err != nil { + return emptyValue, err + } + case bsontype.Double: + f64, err := vr.ReadDouble() + if err != nil { + return emptyValue, err + } + if !dc.Truncate && math.Floor(f64) != f64 { + return emptyValue, errCannotTruncate + } + if f64 > float64(math.MaxInt64) { + return emptyValue, fmt.Errorf("%g overflows int64", f64) + } + i64 = int64(f64) + case bsontype.Boolean: + b, err := vr.ReadBoolean() + if err != nil { + return emptyValue, err + } + if b { + i64 = 1 + } + case bsontype.Null: + if err = vr.ReadNull(); err != nil { + return emptyValue, err + } + case bsontype.Undefined: + if err = vr.ReadUndefined(); err != nil { + return emptyValue, err + } + default: + return emptyValue, fmt.Errorf("cannot decode %v into an integer type", vrType) + } + + switch t.Kind() { + case reflect.Uint8: + if i64 < 0 || i64 > math.MaxUint8 { + return emptyValue, fmt.Errorf("%d overflows uint8", i64) + } + + return reflect.ValueOf(uint8(i64)), nil + case reflect.Uint16: + if i64 < 0 || i64 > math.MaxUint16 { + return emptyValue, fmt.Errorf("%d overflows uint16", i64) + } + + return reflect.ValueOf(uint16(i64)), nil + case reflect.Uint32: + if i64 < 0 || i64 > math.MaxUint32 { + return emptyValue, fmt.Errorf("%d overflows uint32", i64) + } + + return reflect.ValueOf(uint32(i64)), nil + case reflect.Uint64: + if i64 < 0 { + return emptyValue, fmt.Errorf("%d overflows uint64", i64) + } + + return reflect.ValueOf(uint64(i64)), nil + case reflect.Uint: + if i64 < 0 { + return emptyValue, fmt.Errorf("%d overflows uint", i64) + } + v := uint64(i64) + if v > math.MaxUint { // Can we fit this inside of an uint + return emptyValue, fmt.Errorf("%d overflows uint", i64) + } + + return reflect.ValueOf(uint(v)), nil + default: + return emptyValue, ValueDecoderError{ + Name: "UintDecodeValue", + Kinds: []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint}, + Received: reflect.Zero(t), + } + } +} + +// DecodeValue is the ValueDecoder for uint types. +func (uic *UIntCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + if !val.CanSet() { + return ValueDecoderError{ + Name: "UintDecodeValue", + Kinds: []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint}, + Received: val, + } + } + + elem, err := uic.decodeType(dc, vr, val.Type()) + if err != nil { + return err + } + + val.SetUint(elem.Uint()) + return nil +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/byte_slice_codec_options.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/byte_slice_codec_options.go new file mode 100644 index 0000000000..996bd17127 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/byte_slice_codec_options.go @@ -0,0 +1,49 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsonoptions + +// ByteSliceCodecOptions represents all possible options for byte slice encoding and decoding. +// +// Deprecated: Use the bson.Encoder and bson.Decoder configuration methods to set the desired BSON marshal +// and unmarshal behavior instead. +type ByteSliceCodecOptions struct { + EncodeNilAsEmpty *bool // Specifies if a nil byte slice should encode as an empty binary instead of null. Defaults to false. +} + +// ByteSliceCodec creates a new *ByteSliceCodecOptions +// +// Deprecated: Use the bson.Encoder and bson.Decoder configuration methods to set the desired BSON marshal +// and unmarshal behavior instead. +func ByteSliceCodec() *ByteSliceCodecOptions { + return &ByteSliceCodecOptions{} +} + +// SetEncodeNilAsEmpty specifies if a nil byte slice should encode as an empty binary instead of null. Defaults to false. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.NilByteSliceAsEmpty] instead. +func (bs *ByteSliceCodecOptions) SetEncodeNilAsEmpty(b bool) *ByteSliceCodecOptions { + bs.EncodeNilAsEmpty = &b + return bs +} + +// MergeByteSliceCodecOptions combines the given *ByteSliceCodecOptions into a single *ByteSliceCodecOptions in a last one wins fashion. +// +// Deprecated: Merging options structs will not be supported in Go Driver 2.0. Users should create a +// single options struct instead. +func MergeByteSliceCodecOptions(opts ...*ByteSliceCodecOptions) *ByteSliceCodecOptions { + bs := ByteSliceCodec() + for _, opt := range opts { + if opt == nil { + continue + } + if opt.EncodeNilAsEmpty != nil { + bs.EncodeNilAsEmpty = opt.EncodeNilAsEmpty + } + } + + return bs +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/doc.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/doc.go new file mode 100644 index 0000000000..c40973c8d4 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/doc.go @@ -0,0 +1,8 @@ +// Copyright (C) MongoDB, Inc. 2022-present. +// +// 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 + +// Package bsonoptions defines the optional configurations for the BSON codecs. +package bsonoptions diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/empty_interface_codec_options.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/empty_interface_codec_options.go new file mode 100644 index 0000000000..f522c7e03f --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/empty_interface_codec_options.go @@ -0,0 +1,49 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsonoptions + +// EmptyInterfaceCodecOptions represents all possible options for interface{} encoding and decoding. +// +// Deprecated: Use the bson.Encoder and bson.Decoder configuration methods to set the desired BSON marshal +// and unmarshal behavior instead. +type EmptyInterfaceCodecOptions struct { + DecodeBinaryAsSlice *bool // Specifies if Old and Generic type binarys should default to []slice instead of primitive.Binary. Defaults to false. +} + +// EmptyInterfaceCodec creates a new *EmptyInterfaceCodecOptions +// +// Deprecated: Use the bson.Encoder and bson.Decoder configuration methods to set the desired BSON marshal +// and unmarshal behavior instead. +func EmptyInterfaceCodec() *EmptyInterfaceCodecOptions { + return &EmptyInterfaceCodecOptions{} +} + +// SetDecodeBinaryAsSlice specifies if Old and Generic type binarys should default to []slice instead of primitive.Binary. Defaults to false. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.BinaryAsSlice] instead. +func (e *EmptyInterfaceCodecOptions) SetDecodeBinaryAsSlice(b bool) *EmptyInterfaceCodecOptions { + e.DecodeBinaryAsSlice = &b + return e +} + +// MergeEmptyInterfaceCodecOptions combines the given *EmptyInterfaceCodecOptions into a single *EmptyInterfaceCodecOptions in a last one wins fashion. +// +// Deprecated: Merging options structs will not be supported in Go Driver 2.0. Users should create a +// single options struct instead. +func MergeEmptyInterfaceCodecOptions(opts ...*EmptyInterfaceCodecOptions) *EmptyInterfaceCodecOptions { + e := EmptyInterfaceCodec() + for _, opt := range opts { + if opt == nil { + continue + } + if opt.DecodeBinaryAsSlice != nil { + e.DecodeBinaryAsSlice = opt.DecodeBinaryAsSlice + } + } + + return e +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/map_codec_options.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/map_codec_options.go new file mode 100644 index 0000000000..a7a7c1d980 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/map_codec_options.go @@ -0,0 +1,82 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsonoptions + +// MapCodecOptions represents all possible options for map encoding and decoding. +// +// Deprecated: Use the bson.Encoder and bson.Decoder configuration methods to set the desired BSON marshal +// and unmarshal behavior instead. +type MapCodecOptions struct { + DecodeZerosMap *bool // Specifies if the map should be zeroed before decoding into it. Defaults to false. + EncodeNilAsEmpty *bool // Specifies if a nil map should encode as an empty document instead of null. Defaults to false. + // Specifies how keys should be handled. If false, the behavior matches encoding/json, where the encoding key type must + // either be a string, an integer type, or implement bsoncodec.KeyMarshaler and the decoding key type must either be a + // string, an integer type, or implement bsoncodec.KeyUnmarshaler. If true, keys are encoded with fmt.Sprint() and the + // encoding key type must be a string, an integer type, or a float. If true, the use of Stringer will override + // TextMarshaler/TextUnmarshaler. Defaults to false. + EncodeKeysWithStringer *bool +} + +// MapCodec creates a new *MapCodecOptions +// +// Deprecated: Use the bson.Encoder and bson.Decoder configuration methods to set the desired BSON marshal +// and unmarshal behavior instead. +func MapCodec() *MapCodecOptions { + return &MapCodecOptions{} +} + +// SetDecodeZerosMap specifies if the map should be zeroed before decoding into it. Defaults to false. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.ZeroMaps] instead. +func (t *MapCodecOptions) SetDecodeZerosMap(b bool) *MapCodecOptions { + t.DecodeZerosMap = &b + return t +} + +// SetEncodeNilAsEmpty specifies if a nil map should encode as an empty document instead of null. Defaults to false. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.NilMapAsEmpty] instead. +func (t *MapCodecOptions) SetEncodeNilAsEmpty(b bool) *MapCodecOptions { + t.EncodeNilAsEmpty = &b + return t +} + +// SetEncodeKeysWithStringer specifies how keys should be handled. If false, the behavior matches encoding/json, where the +// encoding key type must either be a string, an integer type, or implement bsoncodec.KeyMarshaler and the decoding key +// type must either be a string, an integer type, or implement bsoncodec.KeyUnmarshaler. If true, keys are encoded with +// fmt.Sprint() and the encoding key type must be a string, an integer type, or a float. If true, the use of Stringer +// will override TextMarshaler/TextUnmarshaler. Defaults to false. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.StringifyMapKeysWithFmt] instead. +func (t *MapCodecOptions) SetEncodeKeysWithStringer(b bool) *MapCodecOptions { + t.EncodeKeysWithStringer = &b + return t +} + +// MergeMapCodecOptions combines the given *MapCodecOptions into a single *MapCodecOptions in a last one wins fashion. +// +// Deprecated: Merging options structs will not be supported in Go Driver 2.0. Users should create a +// single options struct instead. +func MergeMapCodecOptions(opts ...*MapCodecOptions) *MapCodecOptions { + s := MapCodec() + for _, opt := range opts { + if opt == nil { + continue + } + if opt.DecodeZerosMap != nil { + s.DecodeZerosMap = opt.DecodeZerosMap + } + if opt.EncodeNilAsEmpty != nil { + s.EncodeNilAsEmpty = opt.EncodeNilAsEmpty + } + if opt.EncodeKeysWithStringer != nil { + s.EncodeKeysWithStringer = opt.EncodeKeysWithStringer + } + } + + return s +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/slice_codec_options.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/slice_codec_options.go new file mode 100644 index 0000000000..3c1e4f35ba --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/slice_codec_options.go @@ -0,0 +1,49 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsonoptions + +// SliceCodecOptions represents all possible options for slice encoding and decoding. +// +// Deprecated: Use the bson.Encoder and bson.Decoder configuration methods to set the desired BSON marshal +// and unmarshal behavior instead. +type SliceCodecOptions struct { + EncodeNilAsEmpty *bool // Specifies if a nil slice should encode as an empty array instead of null. Defaults to false. +} + +// SliceCodec creates a new *SliceCodecOptions +// +// Deprecated: Use the bson.Encoder and bson.Decoder configuration methods to set the desired BSON marshal +// and unmarshal behavior instead. +func SliceCodec() *SliceCodecOptions { + return &SliceCodecOptions{} +} + +// SetEncodeNilAsEmpty specifies if a nil slice should encode as an empty array instead of null. Defaults to false. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.NilSliceAsEmpty] instead. +func (s *SliceCodecOptions) SetEncodeNilAsEmpty(b bool) *SliceCodecOptions { + s.EncodeNilAsEmpty = &b + return s +} + +// MergeSliceCodecOptions combines the given *SliceCodecOptions into a single *SliceCodecOptions in a last one wins fashion. +// +// Deprecated: Merging options structs will not be supported in Go Driver 2.0. Users should create a +// single options struct instead. +func MergeSliceCodecOptions(opts ...*SliceCodecOptions) *SliceCodecOptions { + s := SliceCodec() + for _, opt := range opts { + if opt == nil { + continue + } + if opt.EncodeNilAsEmpty != nil { + s.EncodeNilAsEmpty = opt.EncodeNilAsEmpty + } + } + + return s +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/string_codec_options.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/string_codec_options.go new file mode 100644 index 0000000000..f8b76f996e --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/string_codec_options.go @@ -0,0 +1,52 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsonoptions + +var defaultDecodeOIDAsHex = true + +// StringCodecOptions represents all possible options for string encoding and decoding. +// +// Deprecated: Use the bson.Encoder and bson.Decoder configuration methods to set the desired BSON marshal +// and unmarshal behavior instead. +type StringCodecOptions struct { + DecodeObjectIDAsHex *bool // Specifies if we should decode ObjectID as the hex value. Defaults to true. +} + +// StringCodec creates a new *StringCodecOptions +// +// Deprecated: Use the bson.Encoder and bson.Decoder configuration methods to set the desired BSON marshal +// and unmarshal behavior instead. +func StringCodec() *StringCodecOptions { + return &StringCodecOptions{} +} + +// SetDecodeObjectIDAsHex specifies if object IDs should be decoded as their hex representation. If false, a string made +// from the raw object ID bytes will be used. Defaults to true. +// +// Deprecated: Decoding object IDs as raw bytes will not be supported in Go Driver 2.0. +func (t *StringCodecOptions) SetDecodeObjectIDAsHex(b bool) *StringCodecOptions { + t.DecodeObjectIDAsHex = &b + return t +} + +// MergeStringCodecOptions combines the given *StringCodecOptions into a single *StringCodecOptions in a last one wins fashion. +// +// Deprecated: Merging options structs will not be supported in Go Driver 2.0. Users should create a +// single options struct instead. +func MergeStringCodecOptions(opts ...*StringCodecOptions) *StringCodecOptions { + s := &StringCodecOptions{&defaultDecodeOIDAsHex} + for _, opt := range opts { + if opt == nil { + continue + } + if opt.DecodeObjectIDAsHex != nil { + s.DecodeObjectIDAsHex = opt.DecodeObjectIDAsHex + } + } + + return s +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/struct_codec_options.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/struct_codec_options.go new file mode 100644 index 0000000000..1cbfa32e8b --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/struct_codec_options.go @@ -0,0 +1,107 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsonoptions + +var defaultOverwriteDuplicatedInlinedFields = true + +// StructCodecOptions represents all possible options for struct encoding and decoding. +// +// Deprecated: Use the bson.Encoder and bson.Decoder configuration methods to set the desired BSON marshal +// and unmarshal behavior instead. +type StructCodecOptions struct { + DecodeZeroStruct *bool // Specifies if structs should be zeroed before decoding into them. Defaults to false. + DecodeDeepZeroInline *bool // Specifies if structs should be recursively zeroed when a inline value is decoded. Defaults to false. + EncodeOmitDefaultStruct *bool // Specifies if default structs should be considered empty by omitempty. Defaults to false. + AllowUnexportedFields *bool // Specifies if unexported fields should be marshaled/unmarshaled. Defaults to false. + OverwriteDuplicatedInlinedFields *bool // Specifies if fields in inlined structs can be overwritten by higher level struct fields with the same key. Defaults to true. +} + +// StructCodec creates a new *StructCodecOptions +// +// Deprecated: Use the bson.Encoder and bson.Decoder configuration methods to set the desired BSON marshal +// and unmarshal behavior instead. +func StructCodec() *StructCodecOptions { + return &StructCodecOptions{} +} + +// SetDecodeZeroStruct specifies if structs should be zeroed before decoding into them. Defaults to false. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.ZeroStructs] instead. +func (t *StructCodecOptions) SetDecodeZeroStruct(b bool) *StructCodecOptions { + t.DecodeZeroStruct = &b + return t +} + +// SetDecodeDeepZeroInline specifies if structs should be zeroed before decoding into them. Defaults to false. +// +// Deprecated: DecodeDeepZeroInline will not be supported in Go Driver 2.0. +func (t *StructCodecOptions) SetDecodeDeepZeroInline(b bool) *StructCodecOptions { + t.DecodeDeepZeroInline = &b + return t +} + +// SetEncodeOmitDefaultStruct specifies if default structs should be considered empty by omitempty. A default struct has all +// its values set to their default value. Defaults to false. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.OmitZeroStruct] instead. +func (t *StructCodecOptions) SetEncodeOmitDefaultStruct(b bool) *StructCodecOptions { + t.EncodeOmitDefaultStruct = &b + return t +} + +// SetOverwriteDuplicatedInlinedFields specifies if inlined struct fields can be overwritten by higher level struct fields with the +// same bson key. When true and decoding, values will be written to the outermost struct with a matching key, and when +// encoding, keys will have the value of the top-most matching field. When false, decoding and encoding will error if +// there are duplicate keys after the struct is inlined. Defaults to true. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.ErrorOnInlineDuplicates] instead. +func (t *StructCodecOptions) SetOverwriteDuplicatedInlinedFields(b bool) *StructCodecOptions { + t.OverwriteDuplicatedInlinedFields = &b + return t +} + +// SetAllowUnexportedFields specifies if unexported fields should be marshaled/unmarshaled. Defaults to false. +// +// Deprecated: AllowUnexportedFields does not work on recent versions of Go and will not be +// supported in Go Driver 2.0. +func (t *StructCodecOptions) SetAllowUnexportedFields(b bool) *StructCodecOptions { + t.AllowUnexportedFields = &b + return t +} + +// MergeStructCodecOptions combines the given *StructCodecOptions into a single *StructCodecOptions in a last one wins fashion. +// +// Deprecated: Merging options structs will not be supported in Go Driver 2.0. Users should create a +// single options struct instead. +func MergeStructCodecOptions(opts ...*StructCodecOptions) *StructCodecOptions { + s := &StructCodecOptions{ + OverwriteDuplicatedInlinedFields: &defaultOverwriteDuplicatedInlinedFields, + } + for _, opt := range opts { + if opt == nil { + continue + } + + if opt.DecodeZeroStruct != nil { + s.DecodeZeroStruct = opt.DecodeZeroStruct + } + if opt.DecodeDeepZeroInline != nil { + s.DecodeDeepZeroInline = opt.DecodeDeepZeroInline + } + if opt.EncodeOmitDefaultStruct != nil { + s.EncodeOmitDefaultStruct = opt.EncodeOmitDefaultStruct + } + if opt.OverwriteDuplicatedInlinedFields != nil { + s.OverwriteDuplicatedInlinedFields = opt.OverwriteDuplicatedInlinedFields + } + if opt.AllowUnexportedFields != nil { + s.AllowUnexportedFields = opt.AllowUnexportedFields + } + } + + return s +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/time_codec_options.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/time_codec_options.go new file mode 100644 index 0000000000..3f38433d22 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/time_codec_options.go @@ -0,0 +1,49 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsonoptions + +// TimeCodecOptions represents all possible options for time.Time encoding and decoding. +// +// Deprecated: Use the bson.Encoder and bson.Decoder configuration methods to set the desired BSON marshal +// and unmarshal behavior instead. +type TimeCodecOptions struct { + UseLocalTimeZone *bool // Specifies if we should decode into the local time zone. Defaults to false. +} + +// TimeCodec creates a new *TimeCodecOptions +// +// Deprecated: Use the bson.Encoder and bson.Decoder configuration methods to set the desired BSON marshal +// and unmarshal behavior instead. +func TimeCodec() *TimeCodecOptions { + return &TimeCodecOptions{} +} + +// SetUseLocalTimeZone specifies if we should decode into the local time zone. Defaults to false. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.UseLocalTimeZone] instead. +func (t *TimeCodecOptions) SetUseLocalTimeZone(b bool) *TimeCodecOptions { + t.UseLocalTimeZone = &b + return t +} + +// MergeTimeCodecOptions combines the given *TimeCodecOptions into a single *TimeCodecOptions in a last one wins fashion. +// +// Deprecated: Merging options structs will not be supported in Go Driver 2.0. Users should create a +// single options struct instead. +func MergeTimeCodecOptions(opts ...*TimeCodecOptions) *TimeCodecOptions { + t := TimeCodec() + for _, opt := range opts { + if opt == nil { + continue + } + if opt.UseLocalTimeZone != nil { + t.UseLocalTimeZone = opt.UseLocalTimeZone + } + } + + return t +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/uint_codec_options.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/uint_codec_options.go new file mode 100644 index 0000000000..5091e4d963 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonoptions/uint_codec_options.go @@ -0,0 +1,49 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsonoptions + +// UIntCodecOptions represents all possible options for uint encoding and decoding. +// +// Deprecated: Use the bson.Encoder and bson.Decoder configuration methods to set the desired BSON marshal +// and unmarshal behavior instead. +type UIntCodecOptions struct { + EncodeToMinSize *bool // Specifies if all uints except uint64 should be decoded to minimum size bsontype. Defaults to false. +} + +// UIntCodec creates a new *UIntCodecOptions +// +// Deprecated: Use the bson.Encoder and bson.Decoder configuration methods to set the desired BSON marshal +// and unmarshal behavior instead. +func UIntCodec() *UIntCodecOptions { + return &UIntCodecOptions{} +} + +// SetEncodeToMinSize specifies if all uints except uint64 should be decoded to minimum size bsontype. Defaults to false. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.IntMinSize] instead. +func (u *UIntCodecOptions) SetEncodeToMinSize(b bool) *UIntCodecOptions { + u.EncodeToMinSize = &b + return u +} + +// MergeUIntCodecOptions combines the given *UIntCodecOptions into a single *UIntCodecOptions in a last one wins fashion. +// +// Deprecated: Merging options structs will not be supported in Go Driver 2.0. Users should create a +// single options struct instead. +func MergeUIntCodecOptions(opts ...*UIntCodecOptions) *UIntCodecOptions { + u := UIntCodec() + for _, opt := range opts { + if opt == nil { + continue + } + if opt.EncodeToMinSize != nil { + u.EncodeToMinSize = opt.EncodeToMinSize + } + } + + return u +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/copier.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/copier.go new file mode 100644 index 0000000000..1e25570b85 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/copier.go @@ -0,0 +1,489 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsonrw + +import ( + "errors" + "fmt" + "io" + + "go.mongodb.org/mongo-driver/bson/bsontype" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" +) + +// Copier is a type that allows copying between ValueReaders, ValueWriters, and +// []byte values. +// +// Deprecated: Copying BSON documents using the ValueWriter and ValueReader interfaces will not be +// supported in Go Driver 2.0. +type Copier struct{} + +// NewCopier creates a new copier with the given registry. If a nil registry is provided +// a default registry is used. +// +// Deprecated: Copying BSON documents using the ValueWriter and ValueReader interfaces will not be +// supported in Go Driver 2.0. +func NewCopier() Copier { + return Copier{} +} + +// CopyDocument handles copying a document from src to dst. +// +// Deprecated: Copying BSON documents using the ValueWriter and ValueReader interfaces will not be +// supported in Go Driver 2.0. +func CopyDocument(dst ValueWriter, src ValueReader) error { + return Copier{}.CopyDocument(dst, src) +} + +// CopyDocument handles copying one document from the src to the dst. +// +// Deprecated: Copying BSON documents using the ValueWriter and ValueReader interfaces will not be +// supported in Go Driver 2.0. +func (c Copier) CopyDocument(dst ValueWriter, src ValueReader) error { + dr, err := src.ReadDocument() + if err != nil { + return err + } + + dw, err := dst.WriteDocument() + if err != nil { + return err + } + + return c.copyDocumentCore(dw, dr) +} + +// CopyArrayFromBytes copies the values from a BSON array represented as a +// []byte to a ValueWriter. +// +// Deprecated: Copying BSON arrays using the ValueWriter and ValueReader interfaces will not be +// supported in Go Driver 2.0. +func (c Copier) CopyArrayFromBytes(dst ValueWriter, src []byte) error { + aw, err := dst.WriteArray() + if err != nil { + return err + } + + err = c.CopyBytesToArrayWriter(aw, src) + if err != nil { + return err + } + + return aw.WriteArrayEnd() +} + +// CopyDocumentFromBytes copies the values from a BSON document represented as a +// []byte to a ValueWriter. +// +// Deprecated: Copying BSON documents using the ValueWriter and ValueReader interfaces will not be +// supported in Go Driver 2.0. +func (c Copier) CopyDocumentFromBytes(dst ValueWriter, src []byte) error { + dw, err := dst.WriteDocument() + if err != nil { + return err + } + + err = c.CopyBytesToDocumentWriter(dw, src) + if err != nil { + return err + } + + return dw.WriteDocumentEnd() +} + +type writeElementFn func(key string) (ValueWriter, error) + +// CopyBytesToArrayWriter copies the values from a BSON Array represented as a []byte to an +// ArrayWriter. +// +// Deprecated: Copying BSON arrays using the ArrayWriter interface will not be supported in Go +// Driver 2.0. +func (c Copier) CopyBytesToArrayWriter(dst ArrayWriter, src []byte) error { + wef := func(_ string) (ValueWriter, error) { + return dst.WriteArrayElement() + } + + return c.copyBytesToValueWriter(src, wef) +} + +// CopyBytesToDocumentWriter copies the values from a BSON document represented as a []byte to a +// DocumentWriter. +// +// Deprecated: Copying BSON documents using the ValueWriter and ValueReader interfaces will not be +// supported in Go Driver 2.0. +func (c Copier) CopyBytesToDocumentWriter(dst DocumentWriter, src []byte) error { + wef := func(key string) (ValueWriter, error) { + return dst.WriteDocumentElement(key) + } + + return c.copyBytesToValueWriter(src, wef) +} + +func (c Copier) copyBytesToValueWriter(src []byte, wef writeElementFn) error { + // TODO(skriptble): Create errors types here. Anything that is a tag should be a property. + length, rem, ok := bsoncore.ReadLength(src) + if !ok { + return fmt.Errorf("couldn't read length from src, not enough bytes. length=%d", len(src)) + } + if len(src) < int(length) { + return fmt.Errorf("length read exceeds number of bytes available. length=%d bytes=%d", len(src), length) + } + rem = rem[:length-4] + + var t bsontype.Type + var key string + var val bsoncore.Value + for { + t, rem, ok = bsoncore.ReadType(rem) + if !ok { + return io.EOF + } + if t == bsontype.Type(0) { + if len(rem) != 0 { + return fmt.Errorf("document end byte found before end of document. remaining bytes=%v", rem) + } + break + } + + key, rem, ok = bsoncore.ReadKey(rem) + if !ok { + return fmt.Errorf("invalid key found. remaining bytes=%v", rem) + } + + // write as either array element or document element using writeElementFn + vw, err := wef(key) + if err != nil { + return err + } + + val, rem, ok = bsoncore.ReadValue(rem, t) + if !ok { + return fmt.Errorf("not enough bytes available to read type. bytes=%d type=%s", len(rem), t) + } + err = c.CopyValueFromBytes(vw, t, val.Data) + if err != nil { + return err + } + } + return nil +} + +// CopyDocumentToBytes copies an entire document from the ValueReader and +// returns it as bytes. +// +// Deprecated: Copying BSON documents using the ValueWriter and ValueReader interfaces will not be +// supported in Go Driver 2.0. +func (c Copier) CopyDocumentToBytes(src ValueReader) ([]byte, error) { + return c.AppendDocumentBytes(nil, src) +} + +// AppendDocumentBytes functions the same as CopyDocumentToBytes, but will +// append the result to dst. +// +// Deprecated: Copying BSON documents using the ValueWriter and ValueReader interfaces will not be +// supported in Go Driver 2.0. +func (c Copier) AppendDocumentBytes(dst []byte, src ValueReader) ([]byte, error) { + if br, ok := src.(BytesReader); ok { + _, dst, err := br.ReadValueBytes(dst) + return dst, err + } + + vw := vwPool.Get().(*valueWriter) + defer putValueWriter(vw) + + vw.reset(dst) + + err := c.CopyDocument(vw, src) + dst = vw.buf + return dst, err +} + +// AppendArrayBytes copies an array from the ValueReader to dst. +// +// Deprecated: Copying BSON arrays using the ValueWriter and ValueReader interfaces will not be +// supported in Go Driver 2.0. +func (c Copier) AppendArrayBytes(dst []byte, src ValueReader) ([]byte, error) { + if br, ok := src.(BytesReader); ok { + _, dst, err := br.ReadValueBytes(dst) + return dst, err + } + + vw := vwPool.Get().(*valueWriter) + defer putValueWriter(vw) + + vw.reset(dst) + + err := c.copyArray(vw, src) + dst = vw.buf + return dst, err +} + +// CopyValueFromBytes will write the value represtend by t and src to dst. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.UnmarshalValue] instead. +func (c Copier) CopyValueFromBytes(dst ValueWriter, t bsontype.Type, src []byte) error { + if wvb, ok := dst.(BytesWriter); ok { + return wvb.WriteValueBytes(t, src) + } + + vr := vrPool.Get().(*valueReader) + defer vrPool.Put(vr) + + vr.reset(src) + vr.pushElement(t) + + return c.CopyValue(dst, vr) +} + +// CopyValueToBytes copies a value from src and returns it as a bsontype.Type and a +// []byte. +// +// Deprecated: Use [go.mongodb.org/mongo-driver/bson.MarshalValue] instead. +func (c Copier) CopyValueToBytes(src ValueReader) (bsontype.Type, []byte, error) { + return c.AppendValueBytes(nil, src) +} + +// AppendValueBytes functions the same as CopyValueToBytes, but will append the +// result to dst. +// +// Deprecated: Appending individual BSON elements to an existing slice will not be supported in Go +// Driver 2.0. +func (c Copier) AppendValueBytes(dst []byte, src ValueReader) (bsontype.Type, []byte, error) { + if br, ok := src.(BytesReader); ok { + return br.ReadValueBytes(dst) + } + + vw := vwPool.Get().(*valueWriter) + defer putValueWriter(vw) + + start := len(dst) + + vw.reset(dst) + vw.push(mElement) + + err := c.CopyValue(vw, src) + if err != nil { + return 0, dst, err + } + + return bsontype.Type(vw.buf[start]), vw.buf[start+2:], nil +} + +// CopyValue will copy a single value from src to dst. +// +// Deprecated: Copying BSON values using the ValueWriter and ValueReader interfaces will not be +// supported in Go Driver 2.0. +func (c Copier) CopyValue(dst ValueWriter, src ValueReader) error { + var err error + switch src.Type() { + case bsontype.Double: + var f64 float64 + f64, err = src.ReadDouble() + if err != nil { + break + } + err = dst.WriteDouble(f64) + case bsontype.String: + var str string + str, err = src.ReadString() + if err != nil { + return err + } + err = dst.WriteString(str) + case bsontype.EmbeddedDocument: + err = c.CopyDocument(dst, src) + case bsontype.Array: + err = c.copyArray(dst, src) + case bsontype.Binary: + var data []byte + var subtype byte + data, subtype, err = src.ReadBinary() + if err != nil { + break + } + err = dst.WriteBinaryWithSubtype(data, subtype) + case bsontype.Undefined: + err = src.ReadUndefined() + if err != nil { + break + } + err = dst.WriteUndefined() + case bsontype.ObjectID: + var oid primitive.ObjectID + oid, err = src.ReadObjectID() + if err != nil { + break + } + err = dst.WriteObjectID(oid) + case bsontype.Boolean: + var b bool + b, err = src.ReadBoolean() + if err != nil { + break + } + err = dst.WriteBoolean(b) + case bsontype.DateTime: + var dt int64 + dt, err = src.ReadDateTime() + if err != nil { + break + } + err = dst.WriteDateTime(dt) + case bsontype.Null: + err = src.ReadNull() + if err != nil { + break + } + err = dst.WriteNull() + case bsontype.Regex: + var pattern, options string + pattern, options, err = src.ReadRegex() + if err != nil { + break + } + err = dst.WriteRegex(pattern, options) + case bsontype.DBPointer: + var ns string + var pointer primitive.ObjectID + ns, pointer, err = src.ReadDBPointer() + if err != nil { + break + } + err = dst.WriteDBPointer(ns, pointer) + case bsontype.JavaScript: + var js string + js, err = src.ReadJavascript() + if err != nil { + break + } + err = dst.WriteJavascript(js) + case bsontype.Symbol: + var symbol string + symbol, err = src.ReadSymbol() + if err != nil { + break + } + err = dst.WriteSymbol(symbol) + case bsontype.CodeWithScope: + var code string + var srcScope DocumentReader + code, srcScope, err = src.ReadCodeWithScope() + if err != nil { + break + } + + var dstScope DocumentWriter + dstScope, err = dst.WriteCodeWithScope(code) + if err != nil { + break + } + err = c.copyDocumentCore(dstScope, srcScope) + case bsontype.Int32: + var i32 int32 + i32, err = src.ReadInt32() + if err != nil { + break + } + err = dst.WriteInt32(i32) + case bsontype.Timestamp: + var t, i uint32 + t, i, err = src.ReadTimestamp() + if err != nil { + break + } + err = dst.WriteTimestamp(t, i) + case bsontype.Int64: + var i64 int64 + i64, err = src.ReadInt64() + if err != nil { + break + } + err = dst.WriteInt64(i64) + case bsontype.Decimal128: + var d128 primitive.Decimal128 + d128, err = src.ReadDecimal128() + if err != nil { + break + } + err = dst.WriteDecimal128(d128) + case bsontype.MinKey: + err = src.ReadMinKey() + if err != nil { + break + } + err = dst.WriteMinKey() + case bsontype.MaxKey: + err = src.ReadMaxKey() + if err != nil { + break + } + err = dst.WriteMaxKey() + default: + err = fmt.Errorf("Cannot copy unknown BSON type %s", src.Type()) + } + + return err +} + +func (c Copier) copyArray(dst ValueWriter, src ValueReader) error { + ar, err := src.ReadArray() + if err != nil { + return err + } + + aw, err := dst.WriteArray() + if err != nil { + return err + } + + for { + vr, err := ar.ReadValue() + if errors.Is(err, ErrEOA) { + break + } + if err != nil { + return err + } + + vw, err := aw.WriteArrayElement() + if err != nil { + return err + } + + err = c.CopyValue(vw, vr) + if err != nil { + return err + } + } + + return aw.WriteArrayEnd() +} + +func (c Copier) copyDocumentCore(dw DocumentWriter, dr DocumentReader) error { + for { + key, vr, err := dr.ReadElement() + if errors.Is(err, ErrEOD) { + break + } + if err != nil { + return err + } + + vw, err := dw.WriteDocumentElement(key) + if err != nil { + return err + } + + err = c.CopyValue(vw, vr) + if err != nil { + return err + } + } + + return dw.WriteDocumentEnd() +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/doc.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/doc.go new file mode 100644 index 0000000000..750b0d2af5 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/doc.go @@ -0,0 +1,9 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +// Package bsonrw contains abstractions for reading and writing +// BSON and BSON like types from sources. +package bsonrw // import "go.mongodb.org/mongo-driver/bson/bsonrw" diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_parser.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_parser.go new file mode 100644 index 0000000000..f0702d9d30 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_parser.go @@ -0,0 +1,806 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsonrw + +import ( + "encoding/base64" + "encoding/hex" + "errors" + "fmt" + "io" + "strings" + + "go.mongodb.org/mongo-driver/bson/bsontype" +) + +const maxNestingDepth = 200 + +// ErrInvalidJSON indicates the JSON input is invalid +var ErrInvalidJSON = errors.New("invalid JSON input") + +type jsonParseState byte + +const ( + jpsStartState jsonParseState = iota + jpsSawBeginObject + jpsSawEndObject + jpsSawBeginArray + jpsSawEndArray + jpsSawColon + jpsSawComma + jpsSawKey + jpsSawValue + jpsDoneState + jpsInvalidState +) + +type jsonParseMode byte + +const ( + jpmInvalidMode jsonParseMode = iota + jpmObjectMode + jpmArrayMode +) + +type extJSONValue struct { + t bsontype.Type + v interface{} +} + +type extJSONObject struct { + keys []string + values []*extJSONValue +} + +type extJSONParser struct { + js *jsonScanner + s jsonParseState + m []jsonParseMode + k string + v *extJSONValue + + err error + canonical bool + depth int + maxDepth int + + emptyObject bool + relaxedUUID bool +} + +// newExtJSONParser returns a new extended JSON parser, ready to to begin +// parsing from the first character of the argued json input. It will not +// perform any read-ahead and will therefore not report any errors about +// malformed JSON at this point. +func newExtJSONParser(r io.Reader, canonical bool) *extJSONParser { + return &extJSONParser{ + js: &jsonScanner{r: r}, + s: jpsStartState, + m: []jsonParseMode{}, + canonical: canonical, + maxDepth: maxNestingDepth, + } +} + +// peekType examines the next value and returns its BSON Type +func (ejp *extJSONParser) peekType() (bsontype.Type, error) { + var t bsontype.Type + var err error + initialState := ejp.s + + ejp.advanceState() + switch ejp.s { + case jpsSawValue: + t = ejp.v.t + case jpsSawBeginArray: + t = bsontype.Array + case jpsInvalidState: + err = ejp.err + case jpsSawComma: + // in array mode, seeing a comma means we need to progress again to actually observe a type + if ejp.peekMode() == jpmArrayMode { + return ejp.peekType() + } + case jpsSawEndArray: + // this would only be a valid state if we were in array mode, so return end-of-array error + err = ErrEOA + case jpsSawBeginObject: + // peek key to determine type + ejp.advanceState() + switch ejp.s { + case jpsSawEndObject: // empty embedded document + t = bsontype.EmbeddedDocument + ejp.emptyObject = true + case jpsInvalidState: + err = ejp.err + case jpsSawKey: + if initialState == jpsStartState { + return bsontype.EmbeddedDocument, nil + } + t = wrapperKeyBSONType(ejp.k) + + // if $uuid is encountered, parse as binary subtype 4 + if ejp.k == "$uuid" { + ejp.relaxedUUID = true + t = bsontype.Binary + } + + switch t { + case bsontype.JavaScript: + // just saw $code, need to check for $scope at same level + _, err = ejp.readValue(bsontype.JavaScript) + if err != nil { + break + } + + switch ejp.s { + case jpsSawEndObject: // type is TypeJavaScript + case jpsSawComma: + ejp.advanceState() + + if ejp.s == jpsSawKey && ejp.k == "$scope" { + t = bsontype.CodeWithScope + } else { + err = fmt.Errorf("invalid extended JSON: unexpected key %s in CodeWithScope object", ejp.k) + } + case jpsInvalidState: + err = ejp.err + default: + err = ErrInvalidJSON + } + case bsontype.CodeWithScope: + err = errors.New("invalid extended JSON: code with $scope must contain $code before $scope") + } + } + } + + return t, err +} + +// readKey parses the next key and its type and returns them +func (ejp *extJSONParser) readKey() (string, bsontype.Type, error) { + if ejp.emptyObject { + ejp.emptyObject = false + return "", 0, ErrEOD + } + + // advance to key (or return with error) + switch ejp.s { + case jpsStartState: + ejp.advanceState() + if ejp.s == jpsSawBeginObject { + ejp.advanceState() + } + case jpsSawBeginObject: + ejp.advanceState() + case jpsSawValue, jpsSawEndObject, jpsSawEndArray: + ejp.advanceState() + switch ejp.s { + case jpsSawBeginObject, jpsSawComma: + ejp.advanceState() + case jpsSawEndObject: + return "", 0, ErrEOD + case jpsDoneState: + return "", 0, io.EOF + case jpsInvalidState: + return "", 0, ejp.err + default: + return "", 0, ErrInvalidJSON + } + case jpsSawKey: // do nothing (key was peeked before) + default: + return "", 0, invalidRequestError("key") + } + + // read key + var key string + + switch ejp.s { + case jpsSawKey: + key = ejp.k + case jpsSawEndObject: + return "", 0, ErrEOD + case jpsInvalidState: + return "", 0, ejp.err + default: + return "", 0, invalidRequestError("key") + } + + // check for colon + ejp.advanceState() + if err := ensureColon(ejp.s, key); err != nil { + return "", 0, err + } + + // peek at the value to determine type + t, err := ejp.peekType() + if err != nil { + return "", 0, err + } + + return key, t, nil +} + +// readValue returns the value corresponding to the Type returned by peekType +func (ejp *extJSONParser) readValue(t bsontype.Type) (*extJSONValue, error) { + if ejp.s == jpsInvalidState { + return nil, ejp.err + } + + var v *extJSONValue + + switch t { + case bsontype.Null, bsontype.Boolean, bsontype.String: + if ejp.s != jpsSawValue { + return nil, invalidRequestError(t.String()) + } + v = ejp.v + case bsontype.Int32, bsontype.Int64, bsontype.Double: + // relaxed version allows these to be literal number values + if ejp.s == jpsSawValue { + v = ejp.v + break + } + fallthrough + case bsontype.Decimal128, bsontype.Symbol, bsontype.ObjectID, bsontype.MinKey, bsontype.MaxKey, bsontype.Undefined: + switch ejp.s { + case jpsSawKey: + // read colon + ejp.advanceState() + if err := ensureColon(ejp.s, ejp.k); err != nil { + return nil, err + } + + // read value + ejp.advanceState() + if ejp.s != jpsSawValue || !ejp.ensureExtValueType(t) { + return nil, invalidJSONErrorForType("value", t) + } + + v = ejp.v + + // read end object + ejp.advanceState() + if ejp.s != jpsSawEndObject { + return nil, invalidJSONErrorForType("} after value", t) + } + default: + return nil, invalidRequestError(t.String()) + } + case bsontype.Binary, bsontype.Regex, bsontype.Timestamp, bsontype.DBPointer: + if ejp.s != jpsSawKey { + return nil, invalidRequestError(t.String()) + } + // read colon + ejp.advanceState() + if err := ensureColon(ejp.s, ejp.k); err != nil { + return nil, err + } + + ejp.advanceState() + if t == bsontype.Binary && ejp.s == jpsSawValue { + // convert relaxed $uuid format + if ejp.relaxedUUID { + defer func() { ejp.relaxedUUID = false }() + uuid, err := ejp.v.parseSymbol() + if err != nil { + return nil, err + } + + // RFC 4122 defines the length of a UUID as 36 and the hyphens in a UUID as appearing + // in the 8th, 13th, 18th, and 23rd characters. + // + // See https://tools.ietf.org/html/rfc4122#section-3 + valid := len(uuid) == 36 && + string(uuid[8]) == "-" && + string(uuid[13]) == "-" && + string(uuid[18]) == "-" && + string(uuid[23]) == "-" + if !valid { + return nil, fmt.Errorf("$uuid value does not follow RFC 4122 format regarding length and hyphens") + } + + // remove hyphens + uuidNoHyphens := strings.ReplaceAll(uuid, "-", "") + if len(uuidNoHyphens) != 32 { + return nil, fmt.Errorf("$uuid value does not follow RFC 4122 format regarding length and hyphens") + } + + // convert hex to bytes + bytes, err := hex.DecodeString(uuidNoHyphens) + if err != nil { + return nil, fmt.Errorf("$uuid value does not follow RFC 4122 format regarding hex bytes: %w", err) + } + + ejp.advanceState() + if ejp.s != jpsSawEndObject { + return nil, invalidJSONErrorForType("$uuid and value and then }", bsontype.Binary) + } + + base64 := &extJSONValue{ + t: bsontype.String, + v: base64.StdEncoding.EncodeToString(bytes), + } + subType := &extJSONValue{ + t: bsontype.String, + v: "04", + } + + v = &extJSONValue{ + t: bsontype.EmbeddedDocument, + v: &extJSONObject{ + keys: []string{"base64", "subType"}, + values: []*extJSONValue{base64, subType}, + }, + } + + break + } + + // convert legacy $binary format + base64 := ejp.v + + ejp.advanceState() + if ejp.s != jpsSawComma { + return nil, invalidJSONErrorForType(",", bsontype.Binary) + } + + ejp.advanceState() + key, t, err := ejp.readKey() + if err != nil { + return nil, err + } + if key != "$type" { + return nil, invalidJSONErrorForType("$type", bsontype.Binary) + } + + subType, err := ejp.readValue(t) + if err != nil { + return nil, err + } + + ejp.advanceState() + if ejp.s != jpsSawEndObject { + return nil, invalidJSONErrorForType("2 key-value pairs and then }", bsontype.Binary) + } + + v = &extJSONValue{ + t: bsontype.EmbeddedDocument, + v: &extJSONObject{ + keys: []string{"base64", "subType"}, + values: []*extJSONValue{base64, subType}, + }, + } + break + } + + // read KV pairs + if ejp.s != jpsSawBeginObject { + return nil, invalidJSONErrorForType("{", t) + } + + keys, vals, err := ejp.readObject(2, true) + if err != nil { + return nil, err + } + + ejp.advanceState() + if ejp.s != jpsSawEndObject { + return nil, invalidJSONErrorForType("2 key-value pairs and then }", t) + } + + v = &extJSONValue{t: bsontype.EmbeddedDocument, v: &extJSONObject{keys: keys, values: vals}} + + case bsontype.DateTime: + switch ejp.s { + case jpsSawValue: + v = ejp.v + case jpsSawKey: + // read colon + ejp.advanceState() + if err := ensureColon(ejp.s, ejp.k); err != nil { + return nil, err + } + + ejp.advanceState() + switch ejp.s { + case jpsSawBeginObject: + keys, vals, err := ejp.readObject(1, true) + if err != nil { + return nil, err + } + v = &extJSONValue{t: bsontype.EmbeddedDocument, v: &extJSONObject{keys: keys, values: vals}} + case jpsSawValue: + if ejp.canonical { + return nil, invalidJSONError("{") + } + v = ejp.v + default: + if ejp.canonical { + return nil, invalidJSONErrorForType("object", t) + } + return nil, invalidJSONErrorForType("ISO-8601 Internet Date/Time Format as described in RFC-3339", t) + } + + ejp.advanceState() + if ejp.s != jpsSawEndObject { + return nil, invalidJSONErrorForType("value and then }", t) + } + default: + return nil, invalidRequestError(t.String()) + } + case bsontype.JavaScript: + switch ejp.s { + case jpsSawKey: + // read colon + ejp.advanceState() + if err := ensureColon(ejp.s, ejp.k); err != nil { + return nil, err + } + + // read value + ejp.advanceState() + if ejp.s != jpsSawValue { + return nil, invalidJSONErrorForType("value", t) + } + v = ejp.v + + // read end object or comma and just return + ejp.advanceState() + case jpsSawEndObject: + v = ejp.v + default: + return nil, invalidRequestError(t.String()) + } + case bsontype.CodeWithScope: + if ejp.s == jpsSawKey && ejp.k == "$scope" { + v = ejp.v // this is the $code string from earlier + + // read colon + ejp.advanceState() + if err := ensureColon(ejp.s, ejp.k); err != nil { + return nil, err + } + + // read { + ejp.advanceState() + if ejp.s != jpsSawBeginObject { + return nil, invalidJSONError("$scope to be embedded document") + } + } else { + return nil, invalidRequestError(t.String()) + } + case bsontype.EmbeddedDocument, bsontype.Array: + return nil, invalidRequestError(t.String()) + } + + return v, nil +} + +// readObject is a utility method for reading full objects of known (or expected) size +// it is useful for extended JSON types such as binary, datetime, regex, and timestamp +func (ejp *extJSONParser) readObject(numKeys int, started bool) ([]string, []*extJSONValue, error) { + keys := make([]string, numKeys) + vals := make([]*extJSONValue, numKeys) + + if !started { + ejp.advanceState() + if ejp.s != jpsSawBeginObject { + return nil, nil, invalidJSONError("{") + } + } + + for i := 0; i < numKeys; i++ { + key, t, err := ejp.readKey() + if err != nil { + return nil, nil, err + } + + switch ejp.s { + case jpsSawKey: + v, err := ejp.readValue(t) + if err != nil { + return nil, nil, err + } + + keys[i] = key + vals[i] = v + case jpsSawValue: + keys[i] = key + vals[i] = ejp.v + default: + return nil, nil, invalidJSONError("value") + } + } + + ejp.advanceState() + if ejp.s != jpsSawEndObject { + return nil, nil, invalidJSONError("}") + } + + return keys, vals, nil +} + +// advanceState reads the next JSON token from the scanner and transitions +// from the current state based on that token's type +func (ejp *extJSONParser) advanceState() { + if ejp.s == jpsDoneState || ejp.s == jpsInvalidState { + return + } + + jt, err := ejp.js.nextToken() + + if err != nil { + ejp.err = err + ejp.s = jpsInvalidState + return + } + + valid := ejp.validateToken(jt.t) + if !valid { + ejp.err = unexpectedTokenError(jt) + ejp.s = jpsInvalidState + return + } + + switch jt.t { + case jttBeginObject: + ejp.s = jpsSawBeginObject + ejp.pushMode(jpmObjectMode) + ejp.depth++ + + if ejp.depth > ejp.maxDepth { + ejp.err = nestingDepthError(jt.p, ejp.depth) + ejp.s = jpsInvalidState + } + case jttEndObject: + ejp.s = jpsSawEndObject + ejp.depth-- + + if ejp.popMode() != jpmObjectMode { + ejp.err = unexpectedTokenError(jt) + ejp.s = jpsInvalidState + } + case jttBeginArray: + ejp.s = jpsSawBeginArray + ejp.pushMode(jpmArrayMode) + case jttEndArray: + ejp.s = jpsSawEndArray + + if ejp.popMode() != jpmArrayMode { + ejp.err = unexpectedTokenError(jt) + ejp.s = jpsInvalidState + } + case jttColon: + ejp.s = jpsSawColon + case jttComma: + ejp.s = jpsSawComma + case jttEOF: + ejp.s = jpsDoneState + if len(ejp.m) != 0 { + ejp.err = unexpectedTokenError(jt) + ejp.s = jpsInvalidState + } + case jttString: + switch ejp.s { + case jpsSawComma: + if ejp.peekMode() == jpmArrayMode { + ejp.s = jpsSawValue + ejp.v = extendJSONToken(jt) + return + } + fallthrough + case jpsSawBeginObject: + ejp.s = jpsSawKey + ejp.k = jt.v.(string) + return + } + fallthrough + default: + ejp.s = jpsSawValue + ejp.v = extendJSONToken(jt) + } +} + +var jpsValidTransitionTokens = map[jsonParseState]map[jsonTokenType]bool{ + jpsStartState: { + jttBeginObject: true, + jttBeginArray: true, + jttInt32: true, + jttInt64: true, + jttDouble: true, + jttString: true, + jttBool: true, + jttNull: true, + jttEOF: true, + }, + jpsSawBeginObject: { + jttEndObject: true, + jttString: true, + }, + jpsSawEndObject: { + jttEndObject: true, + jttEndArray: true, + jttComma: true, + jttEOF: true, + }, + jpsSawBeginArray: { + jttBeginObject: true, + jttBeginArray: true, + jttEndArray: true, + jttInt32: true, + jttInt64: true, + jttDouble: true, + jttString: true, + jttBool: true, + jttNull: true, + }, + jpsSawEndArray: { + jttEndObject: true, + jttEndArray: true, + jttComma: true, + jttEOF: true, + }, + jpsSawColon: { + jttBeginObject: true, + jttBeginArray: true, + jttInt32: true, + jttInt64: true, + jttDouble: true, + jttString: true, + jttBool: true, + jttNull: true, + }, + jpsSawComma: { + jttBeginObject: true, + jttBeginArray: true, + jttInt32: true, + jttInt64: true, + jttDouble: true, + jttString: true, + jttBool: true, + jttNull: true, + }, + jpsSawKey: { + jttColon: true, + }, + jpsSawValue: { + jttEndObject: true, + jttEndArray: true, + jttComma: true, + jttEOF: true, + }, + jpsDoneState: {}, + jpsInvalidState: {}, +} + +func (ejp *extJSONParser) validateToken(jtt jsonTokenType) bool { + switch ejp.s { + case jpsSawEndObject: + // if we are at depth zero and the next token is a '{', + // we can consider it valid only if we are not in array mode. + if jtt == jttBeginObject && ejp.depth == 0 { + return ejp.peekMode() != jpmArrayMode + } + case jpsSawComma: + switch ejp.peekMode() { + // the only valid next token after a comma inside a document is a string (a key) + case jpmObjectMode: + return jtt == jttString + case jpmInvalidMode: + return false + } + } + + _, ok := jpsValidTransitionTokens[ejp.s][jtt] + return ok +} + +// ensureExtValueType returns true if the current value has the expected +// value type for single-key extended JSON types. For example, +// {"$numberInt": v} v must be TypeString +func (ejp *extJSONParser) ensureExtValueType(t bsontype.Type) bool { + switch t { + case bsontype.MinKey, bsontype.MaxKey: + return ejp.v.t == bsontype.Int32 + case bsontype.Undefined: + return ejp.v.t == bsontype.Boolean + case bsontype.Int32, bsontype.Int64, bsontype.Double, bsontype.Decimal128, bsontype.Symbol, bsontype.ObjectID: + return ejp.v.t == bsontype.String + default: + return false + } +} + +func (ejp *extJSONParser) pushMode(m jsonParseMode) { + ejp.m = append(ejp.m, m) +} + +func (ejp *extJSONParser) popMode() jsonParseMode { + l := len(ejp.m) + if l == 0 { + return jpmInvalidMode + } + + m := ejp.m[l-1] + ejp.m = ejp.m[:l-1] + + return m +} + +func (ejp *extJSONParser) peekMode() jsonParseMode { + l := len(ejp.m) + if l == 0 { + return jpmInvalidMode + } + + return ejp.m[l-1] +} + +func extendJSONToken(jt *jsonToken) *extJSONValue { + var t bsontype.Type + + switch jt.t { + case jttInt32: + t = bsontype.Int32 + case jttInt64: + t = bsontype.Int64 + case jttDouble: + t = bsontype.Double + case jttString: + t = bsontype.String + case jttBool: + t = bsontype.Boolean + case jttNull: + t = bsontype.Null + default: + return nil + } + + return &extJSONValue{t: t, v: jt.v} +} + +func ensureColon(s jsonParseState, key string) error { + if s != jpsSawColon { + return fmt.Errorf("invalid JSON input: missing colon after key \"%s\"", key) + } + + return nil +} + +func invalidRequestError(s string) error { + return fmt.Errorf("invalid request to read %s", s) +} + +func invalidJSONError(expected string) error { + return fmt.Errorf("invalid JSON input; expected %s", expected) +} + +func invalidJSONErrorForType(expected string, t bsontype.Type) error { + return fmt.Errorf("invalid JSON input; expected %s for %s", expected, t) +} + +func unexpectedTokenError(jt *jsonToken) error { + switch jt.t { + case jttInt32, jttInt64, jttDouble: + return fmt.Errorf("invalid JSON input; unexpected number (%v) at position %d", jt.v, jt.p) + case jttString: + return fmt.Errorf("invalid JSON input; unexpected string (\"%v\") at position %d", jt.v, jt.p) + case jttBool: + return fmt.Errorf("invalid JSON input; unexpected boolean literal (%v) at position %d", jt.v, jt.p) + case jttNull: + return fmt.Errorf("invalid JSON input; unexpected null literal at position %d", jt.p) + case jttEOF: + return fmt.Errorf("invalid JSON input; unexpected end of input at position %d", jt.p) + default: + return fmt.Errorf("invalid JSON input; unexpected %c at position %d", jt.v.(byte), jt.p) + } +} + +func nestingDepthError(p, depth int) error { + return fmt.Errorf("invalid JSON input; nesting too deep (%d levels) at position %d", depth, p) +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_reader.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_reader.go new file mode 100644 index 0000000000..59ddfc4485 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_reader.go @@ -0,0 +1,653 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +package bsonrw + +import ( + "errors" + "fmt" + "io" + "sync" + + "go.mongodb.org/mongo-driver/bson/bsontype" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +// ExtJSONValueReaderPool is a pool for ValueReaders that read ExtJSON. +// +// Deprecated: ExtJSONValueReaderPool will not be supported in Go Driver 2.0. +type ExtJSONValueReaderPool struct { + pool sync.Pool +} + +// NewExtJSONValueReaderPool instantiates a new ExtJSONValueReaderPool. +// +// Deprecated: ExtJSONValueReaderPool will not be supported in Go Driver 2.0. +func NewExtJSONValueReaderPool() *ExtJSONValueReaderPool { + return &ExtJSONValueReaderPool{ + pool: sync.Pool{ + New: func() interface{} { + return new(extJSONValueReader) + }, + }, + } +} + +// Get retrieves a ValueReader from the pool and uses src as the underlying ExtJSON. +// +// Deprecated: ExtJSONValueReaderPool will not be supported in Go Driver 2.0. +func (bvrp *ExtJSONValueReaderPool) Get(r io.Reader, canonical bool) (ValueReader, error) { + vr := bvrp.pool.Get().(*extJSONValueReader) + return vr.reset(r, canonical) +} + +// Put inserts a ValueReader into the pool. If the ValueReader is not a ExtJSON ValueReader nothing +// is inserted into the pool and ok will be false. +// +// Deprecated: ExtJSONValueReaderPool will not be supported in Go Driver 2.0. +func (bvrp *ExtJSONValueReaderPool) Put(vr ValueReader) (ok bool) { + bvr, ok := vr.(*extJSONValueReader) + if !ok { + return false + } + + bvr, _ = bvr.reset(nil, false) + bvrp.pool.Put(bvr) + return true +} + +type ejvrState struct { + mode mode + vType bsontype.Type + depth int +} + +// extJSONValueReader is for reading extended JSON. +type extJSONValueReader struct { + p *extJSONParser + + stack []ejvrState + frame int +} + +// NewExtJSONValueReader creates a new ValueReader from a given io.Reader +// It will interpret the JSON of r as canonical or relaxed according to the +// given canonical flag +func NewExtJSONValueReader(r io.Reader, canonical bool) (ValueReader, error) { + return newExtJSONValueReader(r, canonical) +} + +func newExtJSONValueReader(r io.Reader, canonical bool) (*extJSONValueReader, error) { + ejvr := new(extJSONValueReader) + return ejvr.reset(r, canonical) +} + +func (ejvr *extJSONValueReader) reset(r io.Reader, canonical bool) (*extJSONValueReader, error) { + p := newExtJSONParser(r, canonical) + typ, err := p.peekType() + + if err != nil { + return nil, ErrInvalidJSON + } + + var m mode + switch typ { + case bsontype.EmbeddedDocument: + m = mTopLevel + case bsontype.Array: + m = mArray + default: + m = mValue + } + + stack := make([]ejvrState, 1, 5) + stack[0] = ejvrState{ + mode: m, + vType: typ, + } + return &extJSONValueReader{ + p: p, + stack: stack, + }, nil +} + +func (ejvr *extJSONValueReader) advanceFrame() { + if ejvr.frame+1 >= len(ejvr.stack) { // We need to grow the stack + length := len(ejvr.stack) + if length+1 >= cap(ejvr.stack) { + // double it + buf := make([]ejvrState, 2*cap(ejvr.stack)+1) + copy(buf, ejvr.stack) + ejvr.stack = buf + } + ejvr.stack = ejvr.stack[:length+1] + } + ejvr.frame++ + + // Clean the stack + ejvr.stack[ejvr.frame].mode = 0 + ejvr.stack[ejvr.frame].vType = 0 + ejvr.stack[ejvr.frame].depth = 0 +} + +func (ejvr *extJSONValueReader) pushDocument() { + ejvr.advanceFrame() + + ejvr.stack[ejvr.frame].mode = mDocument + ejvr.stack[ejvr.frame].depth = ejvr.p.depth +} + +func (ejvr *extJSONValueReader) pushCodeWithScope() { + ejvr.advanceFrame() + + ejvr.stack[ejvr.frame].mode = mCodeWithScope +} + +func (ejvr *extJSONValueReader) pushArray() { + ejvr.advanceFrame() + + ejvr.stack[ejvr.frame].mode = mArray +} + +func (ejvr *extJSONValueReader) push(m mode, t bsontype.Type) { + ejvr.advanceFrame() + + ejvr.stack[ejvr.frame].mode = m + ejvr.stack[ejvr.frame].vType = t +} + +func (ejvr *extJSONValueReader) pop() { + switch ejvr.stack[ejvr.frame].mode { + case mElement, mValue: + ejvr.frame-- + case mDocument, mArray, mCodeWithScope: + ejvr.frame -= 2 // we pop twice to jump over the vrElement: vrDocument -> vrElement -> vrDocument/TopLevel/etc... + } +} + +func (ejvr *extJSONValueReader) skipObject() { + // read entire object until depth returns to 0 (last ending } or ] seen) + depth := 1 + for depth > 0 { + ejvr.p.advanceState() + + // If object is empty, raise depth and continue. When emptyObject is true, the + // parser has already read both the opening and closing brackets of an empty + // object ("{}"), so the next valid token will be part of the parent document, + // not part of the nested document. + // + // If there is a comma, there are remaining fields, emptyObject must be set back + // to false, and comma must be skipped with advanceState(). + if ejvr.p.emptyObject { + if ejvr.p.s == jpsSawComma { + ejvr.p.emptyObject = false + ejvr.p.advanceState() + } + depth-- + continue + } + + switch ejvr.p.s { + case jpsSawBeginObject, jpsSawBeginArray: + depth++ + case jpsSawEndObject, jpsSawEndArray: + depth-- + } + } +} + +func (ejvr *extJSONValueReader) invalidTransitionErr(destination mode, name string, modes []mode) error { + te := TransitionError{ + name: name, + current: ejvr.stack[ejvr.frame].mode, + destination: destination, + modes: modes, + action: "read", + } + if ejvr.frame != 0 { + te.parent = ejvr.stack[ejvr.frame-1].mode + } + return te +} + +func (ejvr *extJSONValueReader) typeError(t bsontype.Type) error { + return fmt.Errorf("positioned on %s, but attempted to read %s", ejvr.stack[ejvr.frame].vType, t) +} + +func (ejvr *extJSONValueReader) ensureElementValue(t bsontype.Type, destination mode, callerName string, addModes ...mode) error { + switch ejvr.stack[ejvr.frame].mode { + case mElement, mValue: + if ejvr.stack[ejvr.frame].vType != t { + return ejvr.typeError(t) + } + default: + modes := []mode{mElement, mValue} + if addModes != nil { + modes = append(modes, addModes...) + } + return ejvr.invalidTransitionErr(destination, callerName, modes) + } + + return nil +} + +func (ejvr *extJSONValueReader) Type() bsontype.Type { + return ejvr.stack[ejvr.frame].vType +} + +func (ejvr *extJSONValueReader) Skip() error { + switch ejvr.stack[ejvr.frame].mode { + case mElement, mValue: + default: + return ejvr.invalidTransitionErr(0, "Skip", []mode{mElement, mValue}) + } + + defer ejvr.pop() + + t := ejvr.stack[ejvr.frame].vType + switch t { + case bsontype.Array, bsontype.EmbeddedDocument, bsontype.CodeWithScope: + // read entire array, doc or CodeWithScope + ejvr.skipObject() + default: + _, err := ejvr.p.readValue(t) + if err != nil { + return err + } + } + + return nil +} + +func (ejvr *extJSONValueReader) ReadArray() (ArrayReader, error) { + switch ejvr.stack[ejvr.frame].mode { + case mTopLevel: // allow reading array from top level + case mArray: + return ejvr, nil + default: + if err := ejvr.ensureElementValue(bsontype.Array, mArray, "ReadArray", mTopLevel, mArray); err != nil { + return nil, err + } + } + + ejvr.pushArray() + + return ejvr, nil +} + +func (ejvr *extJSONValueReader) ReadBinary() (b []byte, btype byte, err error) { + if err := ejvr.ensureElementValue(bsontype.Binary, 0, "ReadBinary"); err != nil { + return nil, 0, err + } + + v, err := ejvr.p.readValue(bsontype.Binary) + if err != nil { + return nil, 0, err + } + + b, btype, err = v.parseBinary() + + ejvr.pop() + return b, btype, err +} + +func (ejvr *extJSONValueReader) ReadBoolean() (bool, error) { + if err := ejvr.ensureElementValue(bsontype.Boolean, 0, "ReadBoolean"); err != nil { + return false, err + } + + v, err := ejvr.p.readValue(bsontype.Boolean) + if err != nil { + return false, err + } + + if v.t != bsontype.Boolean { + return false, fmt.Errorf("expected type bool, but got type %s", v.t) + } + + ejvr.pop() + return v.v.(bool), nil +} + +func (ejvr *extJSONValueReader) ReadDocument() (DocumentReader, error) { + switch ejvr.stack[ejvr.frame].mode { + case mTopLevel: + return ejvr, nil + case mElement, mValue: + if ejvr.stack[ejvr.frame].vType != bsontype.EmbeddedDocument { + return nil, ejvr.typeError(bsontype.EmbeddedDocument) + } + + ejvr.pushDocument() + return ejvr, nil + default: + return nil, ejvr.invalidTransitionErr(mDocument, "ReadDocument", []mode{mTopLevel, mElement, mValue}) + } +} + +func (ejvr *extJSONValueReader) ReadCodeWithScope() (code string, dr DocumentReader, err error) { + if err = ejvr.ensureElementValue(bsontype.CodeWithScope, 0, "ReadCodeWithScope"); err != nil { + return "", nil, err + } + + v, err := ejvr.p.readValue(bsontype.CodeWithScope) + if err != nil { + return "", nil, err + } + + code, err = v.parseJavascript() + + ejvr.pushCodeWithScope() + return code, ejvr, err +} + +func (ejvr *extJSONValueReader) ReadDBPointer() (ns string, oid primitive.ObjectID, err error) { + if err = ejvr.ensureElementValue(bsontype.DBPointer, 0, "ReadDBPointer"); err != nil { + return "", primitive.NilObjectID, err + } + + v, err := ejvr.p.readValue(bsontype.DBPointer) + if err != nil { + return "", primitive.NilObjectID, err + } + + ns, oid, err = v.parseDBPointer() + + ejvr.pop() + return ns, oid, err +} + +func (ejvr *extJSONValueReader) ReadDateTime() (int64, error) { + if err := ejvr.ensureElementValue(bsontype.DateTime, 0, "ReadDateTime"); err != nil { + return 0, err + } + + v, err := ejvr.p.readValue(bsontype.DateTime) + if err != nil { + return 0, err + } + + d, err := v.parseDateTime() + + ejvr.pop() + return d, err +} + +func (ejvr *extJSONValueReader) ReadDecimal128() (primitive.Decimal128, error) { + if err := ejvr.ensureElementValue(bsontype.Decimal128, 0, "ReadDecimal128"); err != nil { + return primitive.Decimal128{}, err + } + + v, err := ejvr.p.readValue(bsontype.Decimal128) + if err != nil { + return primitive.Decimal128{}, err + } + + d, err := v.parseDecimal128() + + ejvr.pop() + return d, err +} + +func (ejvr *extJSONValueReader) ReadDouble() (float64, error) { + if err := ejvr.ensureElementValue(bsontype.Double, 0, "ReadDouble"); err != nil { + return 0, err + } + + v, err := ejvr.p.readValue(bsontype.Double) + if err != nil { + return 0, err + } + + d, err := v.parseDouble() + + ejvr.pop() + return d, err +} + +func (ejvr *extJSONValueReader) ReadInt32() (int32, error) { + if err := ejvr.ensureElementValue(bsontype.Int32, 0, "ReadInt32"); err != nil { + return 0, err + } + + v, err := ejvr.p.readValue(bsontype.Int32) + if err != nil { + return 0, err + } + + i, err := v.parseInt32() + + ejvr.pop() + return i, err +} + +func (ejvr *extJSONValueReader) ReadInt64() (int64, error) { + if err := ejvr.ensureElementValue(bsontype.Int64, 0, "ReadInt64"); err != nil { + return 0, err + } + + v, err := ejvr.p.readValue(bsontype.Int64) + if err != nil { + return 0, err + } + + i, err := v.parseInt64() + + ejvr.pop() + return i, err +} + +func (ejvr *extJSONValueReader) ReadJavascript() (code string, err error) { + if err = ejvr.ensureElementValue(bsontype.JavaScript, 0, "ReadJavascript"); err != nil { + return "", err + } + + v, err := ejvr.p.readValue(bsontype.JavaScript) + if err != nil { + return "", err + } + + code, err = v.parseJavascript() + + ejvr.pop() + return code, err +} + +func (ejvr *extJSONValueReader) ReadMaxKey() error { + if err := ejvr.ensureElementValue(bsontype.MaxKey, 0, "ReadMaxKey"); err != nil { + return err + } + + v, err := ejvr.p.readValue(bsontype.MaxKey) + if err != nil { + return err + } + + err = v.parseMinMaxKey("max") + + ejvr.pop() + return err +} + +func (ejvr *extJSONValueReader) ReadMinKey() error { + if err := ejvr.ensureElementValue(bsontype.MinKey, 0, "ReadMinKey"); err != nil { + return err + } + + v, err := ejvr.p.readValue(bsontype.MinKey) + if err != nil { + return err + } + + err = v.parseMinMaxKey("min") + + ejvr.pop() + return err +} + +func (ejvr *extJSONValueReader) ReadNull() error { + if err := ejvr.ensureElementValue(bsontype.Null, 0, "ReadNull"); err != nil { + return err + } + + v, err := ejvr.p.readValue(bsontype.Null) + if err != nil { + return err + } + + if v.t != bsontype.Null { + return fmt.Errorf("expected type null but got type %s", v.t) + } + + ejvr.pop() + return nil +} + +func (ejvr *extJSONValueReader) ReadObjectID() (primitive.ObjectID, error) { + if err := ejvr.ensureElementValue(bsontype.ObjectID, 0, "ReadObjectID"); err != nil { + return primitive.ObjectID{}, err + } + + v, err := ejvr.p.readValue(bsontype.ObjectID) + if err != nil { + return primitive.ObjectID{}, err + } + + oid, err := v.parseObjectID() + + ejvr.pop() + return oid, err +} + +func (ejvr *extJSONValueReader) ReadRegex() (pattern string, options string, err error) { + if err = ejvr.ensureElementValue(bsontype.Regex, 0, "ReadRegex"); err != nil { + return "", "", err + } + + v, err := ejvr.p.readValue(bsontype.Regex) + if err != nil { + return "", "", err + } + + pattern, options, err = v.parseRegex() + + ejvr.pop() + return pattern, options, err +} + +func (ejvr *extJSONValueReader) ReadString() (string, error) { + if err := ejvr.ensureElementValue(bsontype.String, 0, "ReadString"); err != nil { + return "", err + } + + v, err := ejvr.p.readValue(bsontype.String) + if err != nil { + return "", err + } + + if v.t != bsontype.String { + return "", fmt.Errorf("expected type string but got type %s", v.t) + } + + ejvr.pop() + return v.v.(string), nil +} + +func (ejvr *extJSONValueReader) ReadSymbol() (symbol string, err error) { + if err = ejvr.ensureElementValue(bsontype.Symbol, 0, "ReadSymbol"); err != nil { + return "", err + } + + v, err := ejvr.p.readValue(bsontype.Symbol) + if err != nil { + return "", err + } + + symbol, err = v.parseSymbol() + + ejvr.pop() + return symbol, err +} + +func (ejvr *extJSONValueReader) ReadTimestamp() (t uint32, i uint32, err error) { + if err = ejvr.ensureElementValue(bsontype.Timestamp, 0, "ReadTimestamp"); err != nil { + return 0, 0, err + } + + v, err := ejvr.p.readValue(bsontype.Timestamp) + if err != nil { + return 0, 0, err + } + + t, i, err = v.parseTimestamp() + + ejvr.pop() + return t, i, err +} + +func (ejvr *extJSONValueReader) ReadUndefined() error { + if err := ejvr.ensureElementValue(bsontype.Undefined, 0, "ReadUndefined"); err != nil { + return err + } + + v, err := ejvr.p.readValue(bsontype.Undefined) + if err != nil { + return err + } + + err = v.parseUndefined() + + ejvr.pop() + return err +} + +func (ejvr *extJSONValueReader) ReadElement() (string, ValueReader, error) { + switch ejvr.stack[ejvr.frame].mode { + case mTopLevel, mDocument, mCodeWithScope: + default: + return "", nil, ejvr.invalidTransitionErr(mElement, "ReadElement", []mode{mTopLevel, mDocument, mCodeWithScope}) + } + + name, t, err := ejvr.p.readKey() + + if err != nil { + if errors.Is(err, ErrEOD) { + if ejvr.stack[ejvr.frame].mode == mCodeWithScope { + _, err := ejvr.p.peekType() + if err != nil { + return "", nil, err + } + } + + ejvr.pop() + } + + return "", nil, err + } + + ejvr.push(mElement, t) + return name, ejvr, nil +} + +func (ejvr *extJSONValueReader) ReadValue() (ValueReader, error) { + switch ejvr.stack[ejvr.frame].mode { + case mArray: + default: + return nil, ejvr.invalidTransitionErr(mValue, "ReadValue", []mode{mArray}) + } + + t, err := ejvr.p.peekType() + if err != nil { + if errors.Is(err, ErrEOA) { + ejvr.pop() + } + + return nil, err + } + + ejvr.push(mValue, t) + return ejvr, nil +} diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_tables.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_tables.go new file mode 100644 index 0000000000..ba39c9601f --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_tables.go @@ -0,0 +1,223 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 +// +// Based on github.com/golang/go by The Go Authors +// See THIRD-PARTY-NOTICES for original license terms. + +package bsonrw + +import "unicode/utf8" + +// safeSet holds the value true if the ASCII character with the given array +// position can be represented inside a JSON string without any further +// escaping. +// +// All values are true except for the ASCII control characters (0-31), the +// double quote ("), and the backslash character ("\"). +var safeSet = [utf8.RuneSelf]bool{ + ' ': true, + '!': true, + '"': false, + '#': true, + '$': true, + '%': true, + '&': true, + '\'': true, + '(': true, + ')': true, + '*': true, + '+': true, + ',': true, + '-': true, + '.': true, + '/': true, + '0': true, + '1': true, + '2': true, + '3': true, + '4': true, + '5': true, + '6': true, + '7': true, + '8': true, + '9': true, + ':': true, + ';': true, + '<': true, + '=': true, + '>': true, + '?': true, + '@': true, + 'A': true, + 'B': true, + 'C': true, + 'D': true, + 'E': true, + 'F': true, + 'G': true, + 'H': true, + 'I': true, + 'J': true, + 'K': true, + 'L': true, + 'M': true, + 'N': true, + 'O': true, + 'P': true, + 'Q': true, + 'R': true, + 'S': true, + 'T': true, + 'U': true, + 'V': true, + 'W': true, + 'X': true, + 'Y': true, + 'Z': true, + '[': true, + '\\': false, + ']': true, + '^': true, + '_': true, + '`': true, + 'a': true, + 'b': true, + 'c': true, + 'd': true, + 'e': true, + 'f': true, + 'g': true, + 'h': true, + 'i': true, + 'j': true, + 'k': true, + 'l': true, + 'm': true, + 'n': true, + 'o': true, + 'p': true, + 'q': true, + 'r': true, + 's': true, + 't': true, + 'u': true, + 'v': true, + 'w': true, + 'x': true, + 'y': true, + 'z': true, + '{': true, + '|': true, + '}': true, + '~': true, + '\u007f': true, +} + +// htmlSafeSet holds the value true if the ASCII character with the given +// array position can be safely represented inside a JSON string, embedded +// inside of HTML