From 32340e26cac75d01423a78a9b3ddcffb5dc531d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 16:52:04 +0000 Subject: [PATCH] chore(deps): bump github.com/quic-go/quic-go in /plugins/traefik Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.50.1 to 0.57.0. - [Release notes](https://github.com/quic-go/quic-go/releases) - [Commits](https://github.com/quic-go/quic-go/compare/v0.50.1...v0.57.0) --- updated-dependencies: - dependency-name: github.com/quic-go/quic-go dependency-version: 0.57.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- plugins/traefik/go.mod | 27 +- plugins/traefik/go.sum | 205 +- .../github.com/darkweak/souin/api/main.go | 50 - .../souin/api/prometheus/prometheus.go | 5 - .../github.com/darkweak/souin/api/souin.go | 233 - .../github.com/darkweak/souin/api/types.go | 12 - .../souin/configurationtypes/types.go | 24 + .../darkweak/souin/context/cache.go | 1 + .../darkweak/souin/context/graphql.go | 1 + .../github.com/darkweak/souin/context/key.go | 67 +- .../darkweak/souin/context/method.go | 1 + .../github.com/darkweak/souin/context/mode.go | 1 + .../github.com/darkweak/souin/context/now.go | 12 +- .../darkweak/souin/context/timeout.go | 2 + .../darkweak/souin/pkg/api/debug/debug.go | 87 + .../github.com/darkweak/souin/pkg/api/main.go | 9 +- .../souin/pkg/api/prometheus/prometheus.go | 106 +- .../darkweak/souin/pkg/api/souin.go | 133 +- .../souin/pkg/middleware/configuration.go | 12 + .../souin/pkg/middleware/middleware.go | 423 +- .../darkweak/souin/pkg/middleware/writer.go | 130 +- .../darkweak/souin/pkg/rfc/revalidation.go | 184 +- .../github.com/darkweak/souin/pkg/rfc/vary.go | 8 +- .../souin/pkg/storage/abstractProvider.go | 57 - .../souin/pkg/storage/cacheProvider.go | 178 - .../souin/pkg/storage/defaultProvider.go | 213 + .../darkweak/souin/pkg/storage/types/types.go | 23 +- .../souin/pkg/surrogate/providers/common.go | 119 +- .../github.com/francoispqt/gojay/.gitignore | 5 - .../github.com/francoispqt/gojay/.travis.yml | 15 - .../github.com/francoispqt/gojay/Gopkg.lock | 163 - .../github.com/francoispqt/gojay/Gopkg.toml | 23 - .../github.com/francoispqt/gojay/LICENSE | 21 - .../github.com/francoispqt/gojay/Makefile | 11 - .../github.com/francoispqt/gojay/README.md | 855 - .../github.com/francoispqt/gojay/decode.go | 386 - .../francoispqt/gojay/decode_array.go | 247 - .../francoispqt/gojay/decode_bool.go | 241 - .../francoispqt/gojay/decode_embedded_json.go | 85 - .../francoispqt/gojay/decode_interface.go | 130 - .../francoispqt/gojay/decode_number.go | 118 - .../francoispqt/gojay/decode_number_float.go | 516 - .../francoispqt/gojay/decode_number_int.go | 1338 - .../francoispqt/gojay/decode_number_uint.go | 715 - .../francoispqt/gojay/decode_object.go | 407 - .../francoispqt/gojay/decode_pool.go | 64 - .../francoispqt/gojay/decode_slice.go | 89 - .../francoispqt/gojay/decode_sqlnull.go | 157 - .../francoispqt/gojay/decode_stream.go | 115 - .../francoispqt/gojay/decode_stream_pool.go | 59 - .../francoispqt/gojay/decode_string.go | 260 - .../gojay/decode_string_unicode.go | 98 - .../francoispqt/gojay/decode_time.go | 53 - .../francoispqt/gojay/decode_unsafe.go | 120 - .../github.com/francoispqt/gojay/encode.go | 202 - .../francoispqt/gojay/encode_array.go | 212 - .../francoispqt/gojay/encode_bool.go | 164 - .../francoispqt/gojay/encode_builder.go | 65 - .../francoispqt/gojay/encode_embedded_json.go | 93 - .../francoispqt/gojay/encode_interface.go | 173 - .../francoispqt/gojay/encode_null.go | 39 - .../francoispqt/gojay/encode_number.go | 1 - .../francoispqt/gojay/encode_number_float.go | 368 - .../francoispqt/gojay/encode_number_int.go | 500 - .../francoispqt/gojay/encode_number_uint.go | 362 - .../francoispqt/gojay/encode_object.go | 400 - .../francoispqt/gojay/encode_pool.go | 50 - .../francoispqt/gojay/encode_slice.go | 113 - .../francoispqt/gojay/encode_sqlnull.go | 377 - .../francoispqt/gojay/encode_stream.go | 205 - .../francoispqt/gojay/encode_stream_pool.go | 38 - .../francoispqt/gojay/encode_string.go | 186 - .../francoispqt/gojay/encode_time.go | 68 - .../github.com/francoispqt/gojay/errors.go | 88 - .../github.com/francoispqt/gojay/gojay.go | 10 - .../github.com/francoispqt/gojay/gojay.png | Bin 44163 -> 0 bytes .../go-task/slim-sprig/.editorconfig | 14 - .../go-task/slim-sprig/.gitattributes | 1 - .../github.com/go-task/slim-sprig/.gitignore | 2 - .../go-task/slim-sprig/CHANGELOG.md | 364 - .../github.com/go-task/slim-sprig/LICENSE.txt | 19 - .../github.com/go-task/slim-sprig/README.md | 73 - .../go-task/slim-sprig/Taskfile.yml | 12 - .../github.com/go-task/slim-sprig/crypto.go | 24 - .../github.com/go-task/slim-sprig/date.go | 152 - .../github.com/go-task/slim-sprig/defaults.go | 163 - .../github.com/go-task/slim-sprig/dict.go | 118 - .../github.com/go-task/slim-sprig/doc.go | 19 - .../go-task/slim-sprig/functions.go | 317 - .../github.com/go-task/slim-sprig/list.go | 464 - .../github.com/go-task/slim-sprig/network.go | 12 - .../github.com/go-task/slim-sprig/numeric.go | 228 - .../github.com/go-task/slim-sprig/reflect.go | 28 - .../github.com/go-task/slim-sprig/regex.go | 83 - .../github.com/go-task/slim-sprig/strings.go | 189 - .../github.com/go-task/slim-sprig/url.go | 66 - .../vendor/github.com/google/pprof/AUTHORS | 7 - .../github.com/google/pprof/CONTRIBUTORS | 16 - .../vendor/github.com/google/pprof/LICENSE | 202 - .../github.com/google/pprof/profile/encode.go | 588 - .../github.com/google/pprof/profile/filter.go | 274 - .../github.com/google/pprof/profile/index.go | 64 - .../pprof/profile/legacy_java_profile.go | 315 - .../google/pprof/profile/legacy_profile.go | 1228 - .../github.com/google/pprof/profile/merge.go | 667 - .../google/pprof/profile/profile.go | 856 - .../github.com/google/pprof/profile/proto.go | 367 - .../github.com/google/pprof/profile/prune.go | 194 - .../vendor/github.com/onsi/ginkgo/v2/LICENSE | 20 - .../onsi/ginkgo/v2/config/deprecated.go | 69 - .../ginkgo/v2/formatter/colorable_others.go | 41 - .../ginkgo/v2/formatter/colorable_windows.go | 809 - .../onsi/ginkgo/v2/formatter/formatter.go | 230 - .../ginkgo/v2/ginkgo/build/build_command.go | 63 - .../onsi/ginkgo/v2/ginkgo/command/abort.go | 61 - .../onsi/ginkgo/v2/ginkgo/command/command.go | 50 - .../onsi/ginkgo/v2/ginkgo/command/program.go | 182 - .../ginkgo/generators/boostrap_templates.go | 48 - .../v2/ginkgo/generators/bootstrap_command.go | 133 - .../v2/ginkgo/generators/generate_command.go | 264 - .../ginkgo/generators/generate_templates.go | 43 - .../v2/ginkgo/generators/generators_common.go | 76 - .../onsi/ginkgo/v2/ginkgo/internal/compile.go | 161 - .../ginkgo/internal/profiles_and_reports.go | 237 - .../onsi/ginkgo/v2/ginkgo/internal/run.go | 355 - .../ginkgo/v2/ginkgo/internal/test_suite.go | 284 - .../onsi/ginkgo/v2/ginkgo/internal/utils.go | 86 - .../v2/ginkgo/internal/verify_version.go | 54 - .../ginkgo/v2/ginkgo/labels/labels_command.go | 123 - .../github.com/onsi/ginkgo/v2/ginkgo/main.go | 58 - .../onsi/ginkgo/v2/ginkgo/outline/ginkgo.go | 301 - .../onsi/ginkgo/v2/ginkgo/outline/import.go | 58 - .../onsi/ginkgo/v2/ginkgo/outline/outline.go | 110 - .../v2/ginkgo/outline/outline_command.go | 98 - .../onsi/ginkgo/v2/ginkgo/run/run_command.go | 232 - .../v2/ginkgo/unfocus/unfocus_command.go | 186 - .../onsi/ginkgo/v2/ginkgo/watch/delta.go | 22 - .../ginkgo/v2/ginkgo/watch/delta_tracker.go | 75 - .../ginkgo/v2/ginkgo/watch/dependencies.go | 92 - .../ginkgo/v2/ginkgo/watch/package_hash.go | 108 - .../ginkgo/v2/ginkgo/watch/package_hashes.go | 85 - .../onsi/ginkgo/v2/ginkgo/watch/suite.go | 87 - .../ginkgo/v2/ginkgo/watch/watch_command.go | 192 - .../interrupt_handler/interrupt_handler.go | 177 - .../sigquit_swallower_unix.go | 15 - .../sigquit_swallower_windows.go | 8 - .../parallel_support/client_server.go | 72 - .../internal/parallel_support/http_client.go | 169 - .../internal/parallel_support/http_server.go | 242 - .../internal/parallel_support/rpc_client.go | 136 - .../internal/parallel_support/rpc_server.go | 75 - .../parallel_support/server_handler.go | 234 - .../ginkgo/v2/reporters/default_reporter.go | 759 - .../v2/reporters/deprecated_reporter.go | 149 - .../onsi/ginkgo/v2/reporters/json_report.go | 69 - .../onsi/ginkgo/v2/reporters/junit_report.go | 388 - .../onsi/ginkgo/v2/reporters/reporter.go | 29 - .../ginkgo/v2/reporters/teamcity_report.go | 105 - .../onsi/ginkgo/v2/types/code_location.go | 159 - .../github.com/onsi/ginkgo/v2/types/config.go | 758 - .../onsi/ginkgo/v2/types/deprecated_types.go | 141 - .../ginkgo/v2/types/deprecation_support.go | 177 - .../onsi/ginkgo/v2/types/enum_support.go | 43 - .../github.com/onsi/ginkgo/v2/types/errors.go | 639 - .../onsi/ginkgo/v2/types/file_filter.go | 106 - .../github.com/onsi/ginkgo/v2/types/flags.go | 489 - .../onsi/ginkgo/v2/types/label_filter.go | 358 - .../onsi/ginkgo/v2/types/report_entry.go | 190 - .../github.com/onsi/ginkgo/v2/types/types.go | 914 - .../onsi/ginkgo/v2/types/version.go | 3 - .../github.com/quic-go/qpack/.gitmodules | 4 +- .../github.com/quic-go/qpack/.golangci.yml | 19 +- .../vendor/github.com/quic-go/qpack/README.md | 6 +- .../github.com/quic-go/qpack/decoder.go | 260 +- .../github.com/quic-go/qpack/static_table.go | 2 +- .../vendor/github.com/quic-go/qpack/varint.go | 11 +- .../github.com/quic-go/quic-go/.golangci.yml | 132 +- .../github.com/quic-go/quic-go/Changelog.md | 109 - .../github.com/quic-go/quic-go/README.md | 4 +- .../github.com/quic-go/quic-go/client.go | 27 +- .../github.com/quic-go/quic-go/codecov.yml | 5 +- .../github.com/quic-go/quic-go/config.go | 39 +- .../quic-go/quic-go/conn_id_generator.go | 134 +- .../quic-go/quic-go/conn_id_manager.go | 90 +- .../github.com/quic-go/quic-go/connection.go | 2441 +- .../quic-go/quic-go/connection_logging.go | 276 +- .../quic-go/quic-go/connection_timer.go | 51 - .../quic-go/quic-go/crypto_stream.go | 182 +- .../quic-go/quic-go/crypto_stream_manager.go | 20 +- .../github.com/quic-go/quic-go/errors.go | 82 +- .../github.com/quic-go/quic-go/framer.go | 18 +- .../github.com/quic-go/quic-go/http3/body.go | 12 +- .../quic-go/quic-go/http3/capsule.go | 17 +- .../quic-go/quic-go/http3/client.go | 197 +- .../github.com/quic-go/quic-go/http3/conn.go | 411 +- .../quic-go/quic-go/http3/datagram.go | 98 - .../quic-go/quic-go/http3/error_codes.go | 39 +- .../quic-go/quic-go/http3/frames.go | 189 +- .../quic-go/quic-go/http3/headers.go | 82 +- .../quic-go/quic-go/http3/http_stream.go | 294 - .../quic-go/quic-go/http3/mockgen.go | 7 +- .../github.com/quic-go/quic-go/http3/qlog.go | 56 + .../quic-go/quic-go/http3/qlog/event.go | 138 + .../quic-go/quic-go/http3/qlog/frame.go | 220 + .../quic-go/quic-go/http3/qlog/qlog_dir.go | 15 + .../quic-go/quic-go/http3/request_writer.go | 43 +- .../quic-go/quic-go/http3/response_writer.go | 53 +- .../quic-go/quic-go/http3/server.go | 384 +- .../quic-go/http3/state_tracking_stream.go | 93 +- .../quic-go/quic-go/http3/stream.go | 403 + .../github.com/quic-go/quic-go/http3/trace.go | 4 +- .../quic-go/quic-go/http3/transport.go | 127 +- .../github.com/quic-go/quic-go/interface.go | 237 +- .../internal/ackhandler/ack_eliciting.go | 13 + .../quic-go/internal/ackhandler/ackhandler.go | 7 +- .../quic-go/internal/ackhandler/ecn.go | 98 +- .../quic-go/internal/ackhandler/interfaces.go | 31 +- .../ackhandler/lost_packet_tracker.go | 73 + .../quic-go/internal/ackhandler/mockgen.go | 4 +- .../quic-go/internal/ackhandler/packet.go | 18 +- .../ackhandler/received_packet_handler.go | 12 +- .../ackhandler/received_packet_history.go | 44 +- .../ackhandler/received_packet_tracker.go | 47 +- .../ackhandler/sent_packet_handler.go | 409 +- .../ackhandler/sent_packet_history.go | 125 +- .../quic-go/internal/congestion/bandwidth.go | 3 - .../quic-go/internal/congestion/clock.go | 10 +- .../quic-go/internal/congestion/cubic.go | 11 +- .../internal/congestion/cubic_sender.go | 68 +- .../quic-go/internal/congestion/interface.go | 11 +- .../quic-go/internal/congestion/pacer.go | 44 +- .../flowcontrol/base_flow_controller.go | 9 +- .../flowcontrol/connection_flow_controller.go | 14 +- .../quic-go/internal/flowcontrol/interface.go | 11 +- .../flowcontrol/stream_flow_controller.go | 10 +- .../internal/handshake/crypto_setup.go | 149 +- .../quic-go/internal/handshake/interface.go | 4 +- .../internal/handshake/session_ticket.go | 28 +- .../internal/handshake/token_generator.go | 8 +- .../internal/handshake/updatable_aead.go | 90 +- .../quic-go/quic-go/internal/monotime/time.go | 90 + .../internal/protocol/connection_id.go | 6 +- .../quic-go/internal/protocol/params.go | 4 - .../quic-go/internal/protocol/protocol.go | 7 + .../quic-go/internal/protocol/stream.go | 26 + .../quic-go/internal/protocol/version.go | 35 +- .../internal/utils/buffered_write_closer.go | 2 +- .../quic-go/internal/utils/connstats.go | 14 + .../quic-go/quic-go/internal/utils/log.go | 14 +- .../quic-go/internal/utils/rtt_stats.go | 100 +- .../quic-go/quic-go/internal/utils/timer.go | 57 - .../quic-go/internal/wire/ack_frame.go | 8 +- .../internal/wire/ack_frequency_frame.go | 65 + .../internal/wire/connection_close_frame.go | 8 +- .../quic-go/internal/wire/crypto_frame.go | 2 +- .../internal/wire/data_blocked_frame.go | 2 +- .../quic-go/internal/wire/datagram_frame.go | 4 +- .../quic-go/internal/wire/extended_header.go | 2 - .../quic-go/quic-go/internal/wire/frame.go | 12 + .../quic-go/internal/wire/frame_parser.go | 251 +- .../quic-go/internal/wire/frame_type.go | 81 + .../internal/wire/handshake_done_frame.go | 2 +- .../internal/wire/immediate_ack_frame.go | 18 + .../quic-go/internal/wire/max_data_frame.go | 2 +- .../internal/wire/max_stream_data_frame.go | 2 +- .../internal/wire/max_streams_frame.go | 11 +- .../internal/wire/new_connection_id_frame.go | 4 +- .../quic-go/internal/wire/new_token_frame.go | 2 +- .../internal/wire/path_challenge_frame.go | 2 +- .../internal/wire/path_response_frame.go | 2 +- .../quic-go/internal/wire/ping_frame.go | 2 +- .../quic-go/quic-go/internal/wire/pool.go | 2 +- .../internal/wire/reset_stream_frame.go | 58 +- .../wire/retire_connection_id_frame.go | 2 +- .../internal/wire/stop_sending_frame.go | 2 +- .../quic-go/internal/wire/stream_frame.go | 2 +- .../internal/wire/streams_blocked_frame.go | 11 +- .../internal/wire/transport_parameters.go | 91 +- .../internal/wire/version_negotiation.go | 4 +- .../quic-go/logging/connection_tracer.go | 44 - .../logging/connection_tracer_multiplexer.go | 236 - .../quic-go/quic-go/logging/frame.go | 66 - .../quic-go/logging/generate_multiplexer.go | 161 - .../quic-go/quic-go/logging/interface.go | 111 - .../quic-go/quic-go/logging/multiplexer.tmpl | 21 - .../quic-go/quic-go/logging/packet_header.go | 24 - .../quic-go/quic-go/logging/tracer.go | 14 - .../quic-go/logging/tracer_multiplexer.go | 51 - .../quic-go/quic-go/logging/types.go | 130 - .../github.com/quic-go/quic-go/mockgen.go | 51 +- .../quic-go/quic-go/mtu_discoverer.go | 37 +- .../github.com/quic-go/quic-go/oss-fuzz.sh | 6 +- .../quic-go/quic-go/packet_handler_map.go | 228 - .../quic-go/quic-go/packet_packer.go | 222 +- .../quic-go/quic-go/packet_unpacker.go | 9 +- .../quic-go/quic-go/path_manager.go | 129 +- .../quic-go/quic-go/path_manager_outgoing.go | 314 + .../quic-go/quic-go/qlog/connection_tracer.go | 461 - .../github.com/quic-go/quic-go/qlog/event.go | 1089 +- .../github.com/quic-go/quic-go/qlog/frame.go | 554 +- .../quic-go/quic-go/qlog/packet_header.go | 207 +- .../quic-go/quic-go/qlog/qlog_dir.go | 40 +- .../github.com/quic-go/quic-go/qlog/trace.go | 101 - .../github.com/quic-go/quic-go/qlog/tracer.go | 60 - .../github.com/quic-go/quic-go/qlog/types.go | 445 +- .../github.com/quic-go/quic-go/qlog/writer.go | 98 - .../quic-go/qlogwriter/jsontext/encoder.go | 324 + .../quic-go/quic-go/qlogwriter/trace.go | 124 + .../quic-go/quic-go/qlogwriter/writer.go | 216 + .../quic-go/quic-go/quicvarint/io.go | 9 +- .../quic-go/quic-go/quicvarint/varint.go | 68 +- .../quic-go/quic-go/receive_stream.go | 120 +- .../quic-go/quic-go/retransmission_queue.go | 158 +- .../github.com/quic-go/quic-go/send_stream.go | 426 +- .../github.com/quic-go/quic-go/server.go | 410 +- .../vendor/github.com/quic-go/quic-go/sni.go | 136 + .../github.com/quic-go/quic-go/stream.go | 148 +- .../github.com/quic-go/quic-go/streams_map.go | 274 +- .../quic-go/quic-go/streams_map_incoming.go | 106 +- .../quic-go/quic-go/streams_map_outgoing.go | 138 +- .../github.com/quic-go/quic-go/sys_conn.go | 34 +- .../quic-go/quic-go/sys_conn_helper_darwin.go | 2 +- .../quic-go/quic-go/sys_conn_helper_linux.go | 2 +- .../quic-go/quic-go/sys_conn_oob.go | 19 +- .../github.com/quic-go/quic-go/tools.go | 8 - .../github.com/quic-go/quic-go/transport.go | 287 +- .../traefik/vendor/go.uber.org/mock/AUTHORS | 12 - .../traefik/vendor/go.uber.org/mock/LICENSE | 202 - .../go.uber.org/mock/mockgen/deprecated.go | 41 - .../go.uber.org/mock/mockgen/generic.go | 103 - .../vendor/go.uber.org/mock/mockgen/gob.go | 21 - .../go.uber.org/mock/mockgen/mockgen.go | 906 - .../go.uber.org/mock/mockgen/model/model.go | 533 - .../go.uber.org/mock/mockgen/package_mode.go | 358 - .../vendor/go.uber.org/mock/mockgen/parse.go | 805 - .../go.uber.org/mock/mockgen/version.go | 31 - .../golang.org/x/crypto/bcrypt/bcrypt.go | 2 +- .../golang.org/x/crypto/cryptobyte/asn1.go | 2 +- .../x/crypto/internal/poly1305/mac_noasm.go | 2 +- .../poly1305/{sum_amd64.go => sum_asm.go} | 2 +- .../x/crypto/internal/poly1305/sum_loong64.s | 123 + .../x/crypto/internal/poly1305/sum_ppc64x.go | 47 - .../golang.org/x/crypto/ssh/agent/client.go | 22 +- .../golang.org/x/crypto/ssh/agent/server.go | 4 +- .../vendor/golang.org/x/crypto/ssh/certs.go | 67 +- .../vendor/golang.org/x/crypto/ssh/cipher.go | 40 +- .../vendor/golang.org/x/crypto/ssh/client.go | 1 + .../golang.org/x/crypto/ssh/client_auth.go | 2 +- .../vendor/golang.org/x/crypto/ssh/common.go | 412 +- .../golang.org/x/crypto/ssh/connection.go | 12 + .../vendor/golang.org/x/crypto/ssh/doc.go | 1 + .../golang.org/x/crypto/ssh/handshake.go | 27 +- .../vendor/golang.org/x/crypto/ssh/kex.go | 107 +- .../vendor/golang.org/x/crypto/ssh/keys.go | 27 +- .../vendor/golang.org/x/crypto/ssh/mac.go | 12 +- .../golang.org/x/crypto/ssh/messages.go | 6 +- .../vendor/golang.org/x/crypto/ssh/mlkem.go | 183 + .../vendor/golang.org/x/crypto/ssh/server.go | 12 +- .../golang.org/x/crypto/ssh/transport.go | 15 +- .../vendor/golang.org/x/exp/rand/exp.go | 221 - .../vendor/golang.org/x/exp/rand/normal.go | 156 - .../vendor/golang.org/x/exp/rand/rand.go | 372 - .../vendor/golang.org/x/exp/rand/rng.go | 91 - .../vendor/golang.org/x/exp/rand/zipf.go | 77 - .../x/mod/internal/lazyregexp/lazyre.go | 78 - .../vendor/golang.org/x/mod/modfile/print.go | 184 - .../vendor/golang.org/x/mod/modfile/read.go | 964 - .../vendor/golang.org/x/mod/modfile/rule.go | 1836 - .../vendor/golang.org/x/mod/modfile/work.go | 335 - .../vendor/golang.org/x/mod/module/module.go | 841 - .../vendor/golang.org/x/mod/module/pseudo.go | 250 - .../vendor/golang.org/x/mod/semver/semver.go | 30 +- .../vendor/golang.org/x/net/http2/frame.go | 16 +- .../vendor/golang.org/x/net/http2/http2.go | 2 - .../vendor/golang.org/x/net/trace/events.go | 2 +- .../golang.org/x/sync/errgroup/errgroup.go | 121 +- .../vendor/golang.org/x/sys/cpu/cpu.go | 23 + .../golang.org/x/sys/cpu/cpu_linux_loong64.go | 22 + .../golang.org/x/sys/cpu/cpu_linux_noinit.go | 2 +- .../golang.org/x/sys/cpu/cpu_linux_riscv64.go | 23 + .../golang.org/x/sys/cpu/cpu_loong64.go | 38 + .../vendor/golang.org/x/sys/cpu/cpu_loong64.s | 13 + .../golang.org/x/sys/cpu/cpu_riscv64.go | 12 + .../vendor/golang.org/x/sys/cpu/parse.go | 4 +- .../vendor/golang.org/x/sys/unix/mkerrors.sh | 3 + .../golang.org/x/sys/unix/syscall_darwin.go | 93 + .../golang.org/x/sys/unix/syscall_linux.go | 42 +- .../golang.org/x/sys/unix/zerrors_linux.go | 63 +- .../x/sys/unix/zerrors_linux_386.go | 3 + .../x/sys/unix/zerrors_linux_amd64.go | 3 + .../x/sys/unix/zerrors_linux_arm.go | 3 + .../x/sys/unix/zerrors_linux_arm64.go | 3 + .../x/sys/unix/zerrors_linux_loong64.go | 3 + .../x/sys/unix/zerrors_linux_mips.go | 3 + .../x/sys/unix/zerrors_linux_mips64.go | 3 + .../x/sys/unix/zerrors_linux_mips64le.go | 3 + .../x/sys/unix/zerrors_linux_mipsle.go | 3 + .../x/sys/unix/zerrors_linux_ppc.go | 3 + .../x/sys/unix/zerrors_linux_ppc64.go | 3 + .../x/sys/unix/zerrors_linux_ppc64le.go | 3 + .../x/sys/unix/zerrors_linux_riscv64.go | 3 + .../x/sys/unix/zerrors_linux_s390x.go | 3 + .../x/sys/unix/zerrors_linux_sparc64.go | 3 + .../x/sys/unix/zsyscall_darwin_amd64.go | 84 + .../x/sys/unix/zsyscall_darwin_amd64.s | 20 + .../x/sys/unix/zsyscall_darwin_arm64.go | 84 + .../x/sys/unix/zsyscall_darwin_arm64.s | 20 + .../x/sys/unix/zsysnum_linux_386.go | 1 + .../x/sys/unix/zsysnum_linux_amd64.go | 1 + .../x/sys/unix/zsysnum_linux_arm.go | 1 + .../x/sys/unix/zsysnum_linux_arm64.go | 1 + .../x/sys/unix/zsysnum_linux_loong64.go | 1 + .../x/sys/unix/zsysnum_linux_mips.go | 1 + .../x/sys/unix/zsysnum_linux_mips64.go | 1 + .../x/sys/unix/zsysnum_linux_mips64le.go | 1 + .../x/sys/unix/zsysnum_linux_mipsle.go | 1 + .../x/sys/unix/zsysnum_linux_ppc.go | 1 + .../x/sys/unix/zsysnum_linux_ppc64.go | 1 + .../x/sys/unix/zsysnum_linux_ppc64le.go | 1 + .../x/sys/unix/zsysnum_linux_riscv64.go | 1 + .../x/sys/unix/zsysnum_linux_s390x.go | 1 + .../x/sys/unix/zsysnum_linux_sparc64.go | 1 + .../golang.org/x/sys/unix/ztypes_linux.go | 139 +- .../golang.org/x/sys/unix/ztypes_linux_386.go | 18 +- .../x/sys/unix/ztypes_linux_amd64.go | 16 + .../golang.org/x/sys/unix/ztypes_linux_arm.go | 20 +- .../x/sys/unix/ztypes_linux_arm64.go | 16 + .../x/sys/unix/ztypes_linux_loong64.go | 16 + .../x/sys/unix/ztypes_linux_mips.go | 18 +- .../x/sys/unix/ztypes_linux_mips64.go | 16 + .../x/sys/unix/ztypes_linux_mips64le.go | 16 + .../x/sys/unix/ztypes_linux_mipsle.go | 18 +- .../golang.org/x/sys/unix/ztypes_linux_ppc.go | 20 +- .../x/sys/unix/ztypes_linux_ppc64.go | 16 + .../x/sys/unix/ztypes_linux_ppc64le.go | 16 + .../x/sys/unix/ztypes_linux_riscv64.go | 16 + .../x/sys/unix/ztypes_linux_s390x.go | 16 + .../x/sys/unix/ztypes_linux_sparc64.go | 16 + .../x/sys/windows/security_windows.go | 49 +- .../golang.org/x/sys/windows/svc/service.go | 20 +- .../x/sys/windows/syscall_windows.go | 6 +- .../golang.org/x/sys/windows/types_windows.go | 239 + .../x/sys/windows/zsyscall_windows.go | 9 + .../vendor/golang.org/x/term/term_windows.go | 4 +- .../vendor/golang.org/x/term/terminal.go | 86 +- .../golang.org/x/time/rate/sometimes.go | 4 +- .../x/tools/go/ast/astutil/enclosing.go | 654 - .../x/tools/go/ast/astutil/imports.go | 490 - .../x/tools/go/ast/astutil/rewrite.go | 486 - .../golang.org/x/tools/go/ast/astutil/util.go | 13 - .../x/tools/go/ast/inspector/inspector.go | 284 - .../x/tools/go/ast/inspector/iter.go | 85 - .../x/tools/go/ast/inspector/typeof.go | 230 - .../x/tools/go/ast/inspector/walk.go | 341 - .../x/tools/go/gcexportdata/gcexportdata.go | 5 +- .../golang.org/x/tools/go/packages/doc.go | 2 + .../x/tools/go/packages/external.go | 2 +- .../golang.org/x/tools/go/packages/golist.go | 20 +- .../x/tools/go/packages/golist_overlay.go | 2 +- .../x/tools/go/packages/packages.go | 14 - .../x/tools/go/types/objectpath/objectpath.go | 2 +- .../x/tools/go/types/typeutil/callee.go | 83 +- .../golang.org/x/tools/imports/forward.go | 77 - .../x/tools/internal/astutil/edge/edge.go | 295 - .../x/tools/internal/event/label/label.go | 7 +- .../x/tools/internal/gcimporter/iexport.go | 34 +- .../x/tools/internal/gcimporter/iimport.go | 3 +- .../x/tools/internal/gocommand/invoke.go | 2 +- .../x/tools/internal/gopathwalk/walk.go | 337 - .../x/tools/internal/imports/fix.go | 1900 - .../x/tools/internal/imports/imports.go | 359 - .../x/tools/internal/imports/mod.go | 840 - .../x/tools/internal/imports/mod_cache.go | 331 - .../x/tools/internal/imports/sortimports.go | 297 - .../x/tools/internal/imports/source.go | 63 - .../x/tools/internal/imports/source_env.go | 129 - .../tools/internal/imports/source_modindex.go | 103 - .../x/tools/internal/modindex/directories.go | 135 - .../x/tools/internal/modindex/index.go | 266 - .../x/tools/internal/modindex/lookup.go | 178 - .../x/tools/internal/modindex/modindex.go | 164 - .../x/tools/internal/modindex/symbols.go | 218 - .../x/tools/internal/modindex/types.go | 25 - .../internal/packagesinternal/packages.go | 9 +- .../x/tools/internal/pkgbits/decoder.go | 2 +- .../x/tools/internal/stdlib/deps.go | 550 +- .../x/tools/internal/stdlib/manifest.go | 34626 ++++++++-------- .../x/tools/internal/stdlib/stdlib.go | 8 + .../x/tools/internal/typeparams/free.go | 2 +- .../x/tools/internal/typeparams/termlist.go | 12 +- .../x/tools/internal/typeparams/typeterm.go | 3 + .../internal/typesinternal/classify_call.go | 137 + .../x/tools/internal/typesinternal/types.go | 76 +- plugins/traefik/vendor/modules.txt | 73 +- 494 files changed, 31416 insertions(+), 72150 deletions(-) delete mode 100644 plugins/traefik/vendor/github.com/darkweak/souin/api/main.go delete mode 100644 plugins/traefik/vendor/github.com/darkweak/souin/api/prometheus/prometheus.go delete mode 100644 plugins/traefik/vendor/github.com/darkweak/souin/api/souin.go delete mode 100644 plugins/traefik/vendor/github.com/darkweak/souin/api/types.go create mode 100644 plugins/traefik/vendor/github.com/darkweak/souin/pkg/api/debug/debug.go delete mode 100644 plugins/traefik/vendor/github.com/darkweak/souin/pkg/storage/abstractProvider.go delete mode 100644 plugins/traefik/vendor/github.com/darkweak/souin/pkg/storage/cacheProvider.go create mode 100644 plugins/traefik/vendor/github.com/darkweak/souin/pkg/storage/defaultProvider.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/.gitignore delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/.travis.yml delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/Gopkg.lock delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/Gopkg.toml delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/LICENSE delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/Makefile delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/README.md delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_array.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_bool.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_embedded_json.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_interface.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_number.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_number_float.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_number_int.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_number_uint.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_object.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_pool.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_slice.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_sqlnull.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_stream.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_stream_pool.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_string.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_string_unicode.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_time.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/decode_unsafe.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_array.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_bool.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_builder.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_embedded_json.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_interface.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_null.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_number.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_number_float.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_number_int.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_number_uint.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_object.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_pool.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_slice.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_sqlnull.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_stream.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_stream_pool.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_string.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/encode_time.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/errors.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/gojay.go delete mode 100644 plugins/traefik/vendor/github.com/francoispqt/gojay/gojay.png delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/.editorconfig delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/.gitattributes delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/.gitignore delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/CHANGELOG.md delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/LICENSE.txt delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/README.md delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/Taskfile.yml delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/crypto.go delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/date.go delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/defaults.go delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/dict.go delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/doc.go delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/functions.go delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/list.go delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/network.go delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/numeric.go delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/reflect.go delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/regex.go delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/strings.go delete mode 100644 plugins/traefik/vendor/github.com/go-task/slim-sprig/url.go delete mode 100644 plugins/traefik/vendor/github.com/google/pprof/AUTHORS delete mode 100644 plugins/traefik/vendor/github.com/google/pprof/CONTRIBUTORS delete mode 100644 plugins/traefik/vendor/github.com/google/pprof/LICENSE delete mode 100644 plugins/traefik/vendor/github.com/google/pprof/profile/encode.go delete mode 100644 plugins/traefik/vendor/github.com/google/pprof/profile/filter.go delete mode 100644 plugins/traefik/vendor/github.com/google/pprof/profile/index.go delete mode 100644 plugins/traefik/vendor/github.com/google/pprof/profile/legacy_java_profile.go delete mode 100644 plugins/traefik/vendor/github.com/google/pprof/profile/legacy_profile.go delete mode 100644 plugins/traefik/vendor/github.com/google/pprof/profile/merge.go delete mode 100644 plugins/traefik/vendor/github.com/google/pprof/profile/profile.go delete mode 100644 plugins/traefik/vendor/github.com/google/pprof/profile/proto.go delete mode 100644 plugins/traefik/vendor/github.com/google/pprof/profile/prune.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/LICENSE delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/config/deprecated.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/formatter/colorable_others.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/formatter/colorable_windows.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/build/build_command.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/abort.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/command.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/program.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/boostrap_templates.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_templates.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/compile.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/profiles_and_reports.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/test_suite.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/utils.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/verify_version.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/labels/labels_command.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/main.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/import.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline_command.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/unfocus/unfocus_command.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta_tracker.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/dependencies.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hashes.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/suite.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/interrupt_handler.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_unix.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_windows.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/client_server.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_client.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_server.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_client.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_server.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/server_handler.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/deprecated_reporter.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/json_report.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/reporter.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/teamcity_report.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/code_location.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/config.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/deprecated_types.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/deprecation_support.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/enum_support.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/errors.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/file_filter.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/flags.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/label_filter.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/report_entry.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/types.go delete mode 100644 plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/version.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/Changelog.md delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/connection_timer.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/http3/datagram.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/http3/http_stream.go create mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/http3/qlog.go create mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/http3/qlog/event.go create mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/http3/qlog/frame.go create mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/http3/qlog/qlog_dir.go create mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/http3/stream.go create mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/lost_packet_tracker.go create mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/internal/monotime/time.go create mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/connstats.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/timer.go create mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/ack_frequency_frame.go create mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/frame_type.go create mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/immediate_ack_frame.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/logging/connection_tracer.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/logging/connection_tracer_multiplexer.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/logging/frame.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/logging/generate_multiplexer.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/logging/interface.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/logging/multiplexer.tmpl delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/logging/packet_header.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/logging/tracer.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/logging/tracer_multiplexer.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/logging/types.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/packet_handler_map.go create mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/path_manager_outgoing.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/connection_tracer.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/trace.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/tracer.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/writer.go create mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/qlogwriter/jsontext/encoder.go create mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/qlogwriter/trace.go create mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/qlogwriter/writer.go create mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/sni.go delete mode 100644 plugins/traefik/vendor/github.com/quic-go/quic-go/tools.go delete mode 100644 plugins/traefik/vendor/go.uber.org/mock/AUTHORS delete mode 100644 plugins/traefik/vendor/go.uber.org/mock/LICENSE delete mode 100644 plugins/traefik/vendor/go.uber.org/mock/mockgen/deprecated.go delete mode 100644 plugins/traefik/vendor/go.uber.org/mock/mockgen/generic.go delete mode 100644 plugins/traefik/vendor/go.uber.org/mock/mockgen/gob.go delete mode 100644 plugins/traefik/vendor/go.uber.org/mock/mockgen/mockgen.go delete mode 100644 plugins/traefik/vendor/go.uber.org/mock/mockgen/model/model.go delete mode 100644 plugins/traefik/vendor/go.uber.org/mock/mockgen/package_mode.go delete mode 100644 plugins/traefik/vendor/go.uber.org/mock/mockgen/parse.go delete mode 100644 plugins/traefik/vendor/go.uber.org/mock/mockgen/version.go rename plugins/traefik/vendor/golang.org/x/crypto/internal/poly1305/{sum_amd64.go => sum_asm.go} (94%) create mode 100644 plugins/traefik/vendor/golang.org/x/crypto/internal/poly1305/sum_loong64.s delete mode 100644 plugins/traefik/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64x.go create mode 100644 plugins/traefik/vendor/golang.org/x/crypto/ssh/mlkem.go delete mode 100644 plugins/traefik/vendor/golang.org/x/exp/rand/exp.go delete mode 100644 plugins/traefik/vendor/golang.org/x/exp/rand/normal.go delete mode 100644 plugins/traefik/vendor/golang.org/x/exp/rand/rand.go delete mode 100644 plugins/traefik/vendor/golang.org/x/exp/rand/rng.go delete mode 100644 plugins/traefik/vendor/golang.org/x/exp/rand/zipf.go delete mode 100644 plugins/traefik/vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go delete mode 100644 plugins/traefik/vendor/golang.org/x/mod/modfile/print.go delete mode 100644 plugins/traefik/vendor/golang.org/x/mod/modfile/read.go delete mode 100644 plugins/traefik/vendor/golang.org/x/mod/modfile/rule.go delete mode 100644 plugins/traefik/vendor/golang.org/x/mod/modfile/work.go delete mode 100644 plugins/traefik/vendor/golang.org/x/mod/module/module.go delete mode 100644 plugins/traefik/vendor/golang.org/x/mod/module/pseudo.go create mode 100644 plugins/traefik/vendor/golang.org/x/sys/cpu/cpu_linux_loong64.go create mode 100644 plugins/traefik/vendor/golang.org/x/sys/cpu/cpu_loong64.s delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/go/ast/astutil/enclosing.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/go/ast/astutil/imports.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/go/ast/astutil/rewrite.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/go/ast/astutil/util.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/go/ast/inspector/inspector.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/go/ast/inspector/iter.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/go/ast/inspector/typeof.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/go/ast/inspector/walk.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/imports/forward.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/astutil/edge/edge.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/gopathwalk/walk.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/imports/fix.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/imports/imports.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/imports/mod.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/imports/mod_cache.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/imports/sortimports.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/imports/source.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/imports/source_env.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/imports/source_modindex.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/modindex/directories.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/modindex/index.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/modindex/lookup.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/modindex/modindex.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/modindex/symbols.go delete mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/modindex/types.go create mode 100644 plugins/traefik/vendor/golang.org/x/tools/internal/typesinternal/classify_call.go diff --git a/plugins/traefik/go.mod b/plugins/traefik/go.mod index d94f84b1c..42e526872 100644 --- a/plugins/traefik/go.mod +++ b/plugins/traefik/go.mod @@ -31,15 +31,12 @@ require ( github.com/dgraph-io/badger v1.6.2 // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect - github.com/francoispqt/gojay v1.2.13 // indirect github.com/go-jose/go-jose/v3 v3.0.4 // indirect github.com/go-kit/kit v0.13.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/cel-go v0.24.1 // indirect - github.com/google/pprof v0.0.0-20231212022811-ec68065c825e // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect @@ -61,11 +58,10 @@ require ( github.com/mitchellh/go-ps v1.0.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/onsi/ginkgo/v2 v2.15.0 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect - github.com/quic-go/qpack v0.5.1 // indirect - github.com/quic-go/quic-go v0.50.1 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.57.0 // indirect github.com/rs/xid v1.5.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect @@ -87,14 +83,13 @@ require ( go.step.sm/crypto v0.45.0 // indirect go.step.sm/linkedca v0.20.1 // indirect go.uber.org/automaxprocs v1.6.0 // indirect - go.uber.org/mock v0.5.0 // indirect go.uber.org/zap v1.27.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect - golang.org/x/crypto v0.36.0 // indirect + golang.org/x/crypto v0.41.0 // indirect golang.org/x/crypto/x509roots/fallback v0.0.0-20250305170421-49bf5b80c810 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect - golang.org/x/term v0.30.0 // indirect - golang.org/x/time v0.11.0 // indirect + golang.org/x/term v0.34.0 // indirect + golang.org/x/time v0.12.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect howett.net/plist v1.0.0 // indirect @@ -118,12 +113,12 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/spf13/cast v1.7.0 go.uber.org/multierr v1.11.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.38.0 // indirect - golang.org/x/sync v0.14.0 - golang.org/x/sys v0.31.0 // indirect - golang.org/x/text v0.23.0 // indirect - golang.org/x/tools v0.31.0 // indirect + golang.org/x/mod v0.27.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sync v0.16.0 + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/tools v0.36.0 // indirect google.golang.org/grpc v1.67.1 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/yaml.v3 v3.0.1 diff --git a/plugins/traefik/go.sum b/plugins/traefik/go.sum index 54de73804..505b1e057 100644 --- a/plugins/traefik/go.sum +++ b/plugins/traefik/go.sum @@ -1,9 +1,5 @@ cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= cloud.google.com/go/auth v0.4.1 h1:Z7YNIhlWRtrnKlZke7z3GMqzvuYzdc2z98F9D1NV5Hg= cloud.google.com/go/auth v0.4.1/go.mod h1:QVBuVEKpCn4Zp58hzRGvL0tjRGU0YqdRTdCHM1IHnro= @@ -19,13 +15,8 @@ cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuA cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= -dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= -dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -45,7 +36,6 @@ github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/akyoto/cache v1.0.6 h1:5XGVVYoi2i+DZLLPuVIXtsNIJ/qaAM16XT0LaBaXd2k= github.com/akyoto/cache v1.0.6/go.mod h1:WfxTRqKhfgAG71Xh6E3WLpjhBtZI37O53G4h5s+3iM4= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -79,11 +69,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.28.7 h1:et3Ta53gotFR4ERLXXHIHl/Uuk1q github.com/aws/aws-sdk-go-v2/service/sts v1.28.7/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw= github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/caddyserver/caddy/v2 v2.10.0 h1:fonubSaQKF1YANl8TXqGcn4IbIRUDdfAkpcsfI/vX5U= github.com/caddyserver/caddy/v2 v2.10.0/go.mod h1:q+dgBS3xtIJJGYI2H5Nyh9+4BvhQQ9yCGmECv4Ubdjo= github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU= @@ -103,7 +90,6 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= @@ -111,7 +97,6 @@ github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMe github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= @@ -142,15 +127,9 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= -github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY= github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -171,47 +150,31 @@ github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrt github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/cel-go v0.24.1 h1:jsBCtxG8mM5wiUJDSGUqU0K7Mtr3w7Eyv00rw4DiZxI= github.com/google/cel-go v0.24.1/go.mod h1:Hdf9TqOaTNSFQA1ybQaRqATVoK7m/zcf7IMhGXP5zI8= github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 h1:heyoXNxkRT155x4jTAiSv5BVSVkueifPUm+Q8LUXMRo= github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745/go.mod h1:zN0wUQgV9LjwLZeFHnrAbQi8hzMVvEWePyk+MhPOk7k= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk= github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= github.com/google/go-tpm-tools v0.4.4 h1:oiQfAIkc6xTy9Fl5NKTeTJkBTlXdHsxAofmQyxBKY98= github.com/google/go-tpm-tools v0.4.4/go.mod h1:T8jXkp2s+eltnCDIsXR84/MTcVU9Ja7bh3Mit0pa4AY= github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus= github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20231212022811-ec68065c825e h1:bwOy7hAFd0C91URzMIEBfr6BAz29yk7Qj0cy6S7DJlU= -github.com/google/pprof v0.0.0-20231212022811-ec68065c825e/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= @@ -219,14 +182,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.12.4 h1:9gWcmF85Wvq4ryPFvGFaOgPIs1AQX0d0bcbGw4Z96qg= github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= @@ -280,10 +237,7 @@ github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= @@ -297,7 +251,6 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -311,9 +264,7 @@ github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libdns/libdns v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ= github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= -github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= @@ -326,12 +277,10 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc= github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= -github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -342,17 +291,8 @@ github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= -github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= -github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= -github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= -github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -369,22 +309,18 @@ github.com/pquerna/cachecontrol v0.2.0 h1:vBXSNuE5MYP9IJ5kjsdo8uq+w41jSPgvba2DEn github.com/pquerna/cachecontrol v0.2.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.50.1 h1:unsgjFIUqW8a2oopkY7YNONpV1gYND6Nt9hnt1PN94Q= -github.com/quic-go/quic-go v0.50.1/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.57.0 h1:AsSSrrMs4qI/hLrKlTH/TGQeTMY0ib1pAOX7vA3AdqE= +github.com/quic-go/quic-go v0.57.0/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= @@ -399,35 +335,12 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E= github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= -github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= -github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= -github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= -github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= -github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= -github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= -github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= -github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= -github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= -github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= -github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= -github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= -github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= -github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -449,8 +362,6 @@ github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d h1:06LUHn4Ia2X6syjI github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d/go.mod h1:4d0ub42ut1mMtvGyMensjuHYEUpRrASvkzLEJvoRQcU= github.com/smallstep/truststore v0.13.0 h1:90if9htAOblavbMeWlqNLnO9bsjjgVv2hQeQJCi/py4= github.com/smallstep/truststore v0.13.0/go.mod h1:3tmMp2aLKZ/OA/jnFUB0cYPcho402UG2knuJoPh4j7A= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -482,16 +393,13 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tailscale/tscert v0.0.0-20240608151842-d3f834017e53 h1:uxMgm0C+EjytfAqyfBG55ZONKQ7mvd7x4YYCWsf8QHQ= github.com/tailscale/tscert v0.0.0-20240608151842-d3f834017e53/go.mod h1:kNGUQ3VESx3VZwRwA9MSCUegIl6+saPL8Noq82ozCaU= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= -github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= -github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= @@ -503,7 +411,6 @@ github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= @@ -530,8 +437,8 @@ go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -545,12 +452,8 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= -golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -561,32 +464,20 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/crypto/x509roots/fallback v0.0.0-20250305170421-49bf5b80c810 h1:V5+zy0jmgNYmK1uW/sPpBw8ioFvalrhaUrYWmu1Fpe4= golang.org/x/crypto/x509roots/fallback v0.0.0-20250305170421-49bf5b80c810/go.mod h1:lxN5T34bK4Z/i6cMaU7frUU57VkDXFD4Kamfl/cp9oU= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -594,33 +485,20 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -639,18 +517,17 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -659,17 +536,11 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= -golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -680,37 +551,21 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= -golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/api v0.180.0 h1:M2D87Yo0rGBPWpo1orwfCLehUUL6E7/TYe5gvMQWDh4= google.golang.org/api v0.180.0/go.mod h1:51AiyoEg1MJPSZ9zvklA8VnRILPXxn1iVen9v25XHAE= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda h1:wu/KJm9KJwpfHWhkkZGohVC6KRrc1oJNr4jwtQMOQXw= google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda/go.mod h1:g2LLCvCeCSir/JJSWosk19BR4NVxGqHUC6rxIRsd7Aw= google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= @@ -722,20 +577,12 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= -sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/api/main.go b/plugins/traefik/vendor/github.com/darkweak/souin/api/main.go deleted file mode 100644 index 03230b136..000000000 --- a/plugins/traefik/vendor/github.com/darkweak/souin/api/main.go +++ /dev/null @@ -1,50 +0,0 @@ -package api - -import ( - "net/http" - - "github.com/darkweak/souin/configurationtypes" - "github.com/darkweak/souin/pkg/storage/types" - "github.com/darkweak/souin/pkg/surrogate/providers" -) - -// MapHandler is a map to store the available http Handlers -type MapHandler struct { - Handlers *map[string]http.HandlerFunc -} - -// GenerateHandlerMap generate the MapHandler -func GenerateHandlerMap( - configuration configurationtypes.AbstractConfigurationInterface, - storers []types.Storer, - surrogateStorage providers.SurrogateInterface, -) *MapHandler { - hm := make(map[string]http.HandlerFunc) - shouldEnable := false - - souinAPI := configuration.GetAPI() - basePathAPIS := souinAPI.BasePath - if basePathAPIS == "" { - basePathAPIS = "/souin-api" - } - - for _, endpoint := range Initialize(configuration, storers, surrogateStorage) { - if endpoint.IsEnabled() { - shouldEnable = true - if e, ok := endpoint.(*SouinAPI); ok { - hm[basePathAPIS+endpoint.GetBasePath()] = e.HandleRequest - } - } - } - - if shouldEnable { - return &MapHandler{Handlers: &hm} - } - - return nil -} - -// Initialize contains all apis that should be enabled -func Initialize(c configurationtypes.AbstractConfigurationInterface, storers []types.Storer, surrogateStorage providers.SurrogateInterface) []EndpointInterface { - return []EndpointInterface{initializeSouin(c, storers, surrogateStorage)} -} diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/api/prometheus/prometheus.go b/plugins/traefik/vendor/github.com/darkweak/souin/api/prometheus/prometheus.go deleted file mode 100644 index 2712571d6..000000000 --- a/plugins/traefik/vendor/github.com/darkweak/souin/api/prometheus/prometheus.go +++ /dev/null @@ -1,5 +0,0 @@ -package prometheus - -const RequestCounter = "" - -func Increment(string) {} diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/api/souin.go b/plugins/traefik/vendor/github.com/darkweak/souin/api/souin.go deleted file mode 100644 index 8f5305cb9..000000000 --- a/plugins/traefik/vendor/github.com/darkweak/souin/api/souin.go +++ /dev/null @@ -1,233 +0,0 @@ -package api - -import ( - "encoding/json" - "fmt" - "net/http" - "regexp" - "strings" - - "github.com/darkweak/souin/configurationtypes" - "github.com/darkweak/souin/pkg/storage/types" - "github.com/darkweak/souin/pkg/surrogate/providers" -) - -// SouinAPI object contains informations related to the endpoints -type SouinAPI struct { - basePath string - enabled bool - storers []types.Storer - surrogateStorage providers.SurrogateInterface - allowedMethods []string -} - -type invalidationType string - -const ( - uriInvalidationType invalidationType = "uri" - uriPrefixInvalidationType invalidationType = "uri-prefix" - originInvalidationType invalidationType = "origin" - groupInvalidationType invalidationType = "group" -) - -type invalidation struct { - Type invalidationType `json:"type"` - Selectors []string `json:"selectors"` - Groups []string `json:"groups"` - Purge bool `json:"purge"` -} - -func initializeSouin( - configuration configurationtypes.AbstractConfigurationInterface, - storers []types.Storer, - surrogateStorage providers.SurrogateInterface, -) *SouinAPI { - basePath := configuration.GetAPI().Souin.BasePath - if basePath == "" { - basePath = "/souin" - } - - allowedMethods := configuration.GetDefaultCache().GetAllowedHTTPVerbs() - if len(allowedMethods) == 0 { - allowedMethods = []string{http.MethodGet, http.MethodHead} - } - - return &SouinAPI{ - basePath, - configuration.GetAPI().Souin.Enable, - storers, - surrogateStorage, - allowedMethods, - } -} - -// BulkDelete allow user to delete multiple items with regexp -func (s *SouinAPI) BulkDelete(key string) { - for _, current := range s.storers { - current.DeleteMany(key) - } -} - -// Delete will delete a record into the provider cache system and will update the Souin API if enabled -func (s *SouinAPI) Delete(key string) { - for _, current := range s.storers { - current.Delete(key) - } -} - -// GetAll will retrieve all stored keys in the provider -func (s *SouinAPI) GetAll() []string { - keys := []string{} - for _, current := range s.storers { - keys = append(keys, current.ListKeys()...) - } - - return keys -} - -// GetBasePath will return the basepath for this resource -func (s *SouinAPI) GetBasePath() string { - return s.basePath -} - -// IsEnabled will return enabled status -func (s *SouinAPI) IsEnabled() bool { - return s.enabled -} - -func (s *SouinAPI) listKeys(search string) []string { - res := []string{} - re, err := regexp.Compile(search) - if err != nil { - return res - } - for _, key := range s.GetAll() { - if re.MatchString(key) { - res = append(res, key) - } - } - - return res -} - -// HandleRequest will handle the request -func (s *SouinAPI) HandleRequest(w http.ResponseWriter, r *http.Request) { - res := []byte{} - compile := regexp.MustCompile(s.GetBasePath()+"/.+").FindString(r.RequestURI) != "" - switch r.Method { - case http.MethodGet: - if regexp.MustCompile(s.GetBasePath()+"/surrogate_keys").FindString(r.RequestURI) != "" { - res, _ = json.Marshal(s.surrogateStorage.List()) - } else if compile { - search := regexp.MustCompile(s.GetBasePath()+"/(.+)").FindAllStringSubmatch(r.RequestURI, -1)[0][1] - res, _ = json.Marshal(s.listKeys(search)) - if len(res) == 2 { - w.WriteHeader(http.StatusNotFound) - } - } else { - res, _ = json.Marshal(s.GetAll()) - } - w.Header().Set("Content-Type", "application/json") - case http.MethodPost: - var invalidator invalidation - err := json.NewDecoder(r.Body).Decode(&invalidator) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - return - } - - keysToInvalidate := []string{} - switch invalidator.Type { - case groupInvalidationType: - keysToInvalidate, _ = s.surrogateStorage.Purge(http.Header{"Surrogate-Key": invalidator.Groups}) - case uriPrefixInvalidationType, uriInvalidationType: - bodyKeys := []string{} - listedKeys := s.GetAll() - for _, k := range invalidator.Selectors { - if !strings.Contains(k, "//") { - rq, err := http.NewRequest(http.MethodGet, "//"+k, nil) - if err != nil { - continue - } - - bodyKeys = append(bodyKeys, rq.Host+"-"+rq.URL.Path) - } - } - - for _, allKey := range listedKeys { - for _, bk := range bodyKeys { - if invalidator.Type == uriInvalidationType { - if strings.Contains(allKey, bk) && strings.Contains(allKey, bk+"-") && strings.HasSuffix(allKey, bk) { - keysToInvalidate = append(keysToInvalidate, allKey) - break - } - } else { - if strings.Contains(allKey, bk) && - (strings.Contains(allKey, bk+"-") || strings.Contains(allKey, bk+"?") || strings.Contains(allKey, bk+"/") || strings.HasSuffix(allKey, bk)) { - keysToInvalidate = append(keysToInvalidate, allKey) - break - } - } - } - } - case originInvalidationType: - bodyKeys := []string{} - listedKeys := s.GetAll() - for _, k := range invalidator.Selectors { - if !strings.Contains(k, "//") { - rq, err := http.NewRequest(http.MethodGet, "//"+k, nil) - if err != nil { - continue - } - - bodyKeys = append(bodyKeys, rq.Host) - } - } - - for _, allKey := range listedKeys { - for _, bk := range bodyKeys { - if strings.Contains(allKey, bk) { - keysToInvalidate = append(keysToInvalidate, allKey) - break - } - } - } - } - - for _, k := range keysToInvalidate { - for _, current := range s.storers { - current.Delete(k) - } - } - w.WriteHeader(http.StatusOK) - case "PURGE": - if compile { - keysRg := regexp.MustCompile(s.GetBasePath() + "/(.+)") - flushRg := regexp.MustCompile(s.GetBasePath() + "/flush$") - - if flushRg.FindString(r.RequestURI) != "" { - for _, current := range s.storers { - current.DeleteMany(".+") - } - e := s.surrogateStorage.Destruct() - if e != nil { - fmt.Printf("Error while purging the surrogate keys: %+v.", e) - } - fmt.Println("Successfully clear the cache and the surrogate keys storage.") - } else { - submatch := keysRg.FindAllStringSubmatch(r.RequestURI, -1)[0][1] - s.BulkDelete(submatch) - } - } else { - ck, _ := s.surrogateStorage.Purge(r.Header) - for _, k := range ck { - for _, current := range s.storers { - current.Delete(k) - } - } - } - w.WriteHeader(http.StatusNoContent) - default: - } - _, _ = w.Write(res) -} diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/api/types.go b/plugins/traefik/vendor/github.com/darkweak/souin/api/types.go deleted file mode 100644 index a1c5c53e9..000000000 --- a/plugins/traefik/vendor/github.com/darkweak/souin/api/types.go +++ /dev/null @@ -1,12 +0,0 @@ -package api - -import ( - "net/http" -) - -// EndpointInterface is the contract to be able to enable your custom endpoints -type EndpointInterface interface { - GetBasePath() string - IsEnabled() bool - HandleRequest(http.ResponseWriter, *http.Request) -} diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/configurationtypes/types.go b/plugins/traefik/vendor/github.com/darkweak/souin/configurationtypes/types.go index e03ed7f93..f9434b57d 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/configurationtypes/types.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/configurationtypes/types.go @@ -9,6 +9,7 @@ import ( "strings" "time" + "github.com/darkweak/storages/core" "gopkg.in/yaml.v3" ) @@ -198,6 +199,26 @@ type CacheProvider struct { Configuration interface{} `json:"configuration" yaml:"configuration"` } +func (c *CacheProvider) MarshalJSON() ([]byte, error) { + if !c.Found && c.URL == "" && c.Path == "" && c.Configuration == nil && c.Uuid == "" { + return []byte("null"), nil + } + + return json.Marshal(struct { + Uuid string + Found bool `json:"found"` + URL string `json:"url"` + Path string `json:"path"` + Configuration interface{} `json:"configuration"` + }{ + Uuid: c.Uuid, + Found: c.Found, + URL: c.URL, + Path: c.Path, + Configuration: c.Configuration, + }) +} + // Timeout configuration to handle the cache provider and the // reverse-proxy timeout. type Timeout struct { @@ -456,7 +477,10 @@ type AbstractConfigurationInterface interface { GetDefaultCache() DefaultCacheInterface GetAPI() API GetLogLevel() string + GetLogger() core.Logger + SetLogger(core.Logger) GetYkeys() map[string]SurrogateKeys GetSurrogateKeys() map[string]SurrogateKeys + IsSurrogateDisabled() bool GetCacheKeys() CacheKeys } diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/context/cache.go b/plugins/traefik/vendor/github.com/darkweak/souin/context/cache.go index c4051f0c4..0e24066b4 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/context/cache.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/context/cache.go @@ -28,6 +28,7 @@ func (cc *cacheContext) SetupContext(c configurationtypes.AbstractConfigurationI if c.GetDefaultCache().GetCacheName() != "" { cc.cacheName = c.GetDefaultCache().GetCacheName() } + c.GetLogger().Debugf("Set %s as Cache-Status name", cc.cacheName) } func (cc *cacheContext) SetContext(req *http.Request) *http.Request { diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/context/graphql.go b/plugins/traefik/vendor/github.com/darkweak/souin/context/graphql.go index 46def862e..d37922136 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/context/graphql.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/context/graphql.go @@ -50,6 +50,7 @@ func (g *graphQLContext) SetContextWithBaseRequest(req *http.Request, baseRq *ht func (g *graphQLContext) SetupContext(c configurationtypes.AbstractConfigurationInterface) { if len(c.GetDefaultCache().GetAllowedHTTPVerbs()) != 0 { g.custom = true + c.GetLogger().Debug("Enable GraphQL logic due to your custom HTTP verbs setup.") } } diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/context/key.go b/plugins/traefik/vendor/github.com/darkweak/souin/context/key.go index d4edc3a85..ab0e99dbe 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/context/key.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/context/key.go @@ -5,7 +5,10 @@ import ( "net/http" "regexp" + "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/modules/caddyhttp" "github.com/darkweak/souin/configurationtypes" + "github.com/darkweak/storages/core" ) const ( @@ -20,8 +23,8 @@ type keyContext struct { disable_host bool disable_method bool disable_query bool - disable_scheme bool disable_vary bool + disable_scheme bool displayable bool hash bool headers []string @@ -50,24 +53,34 @@ func (g *keyContext) SetupContext(c configurationtypes.AbstractConfigurationInte g.overrides = make([]map[*regexp.Regexp]keyContext, 0) - // for _, cacheKey := range c.GetCacheKeys() { - // for r, v := range cacheKey { - // g.overrides = append(g.overrides, map[*regexp.Regexp]keyContext{r.Regexp: { - // disable_body: v.DisableBody, - // disable_host: v.DisableHost, - // disable_method: v.DisableMethod, - // disable_query: v.DisableQuery, - // disable_scheme: v.DisableScheme, - // hash: v.Hash, - // displayable: !v.Hide, - // template: v.Template, - // headers: v.Headers, - // }}) - // } - // } - - g.initializer = func(r *http.Request) *http.Request { - return r + for _, cacheKey := range c.GetCacheKeys() { + for r, v := range cacheKey { + g.overrides = append(g.overrides, map[*regexp.Regexp]keyContext{r.Regexp: { + disable_body: v.DisableBody, + disable_host: v.DisableHost, + disable_method: v.DisableMethod, + disable_query: v.DisableQuery, + disable_scheme: v.DisableScheme, + disable_vary: v.DisableVary, + hash: v.Hash, + displayable: !v.Hide, + template: v.Template, + headers: v.Headers, + }}) + } + } + + switch c.GetPluginName() { + case "caddy": + g.initializer = func(r *http.Request) *http.Request { + return r + } + default: + g.initializer = func(r *http.Request) *http.Request { + repl := caddy.NewReplacer() + + return caddyhttp.PrepareRequest(r, repl, nil, nil) + } } } @@ -107,6 +120,9 @@ func parseKeyInformations(req *http.Request, kCtx keyContext) (query, body, host } func (g *keyContext) computeKey(req *http.Request) (key string, headers []string, hash, displayable bool) { + if g.template != "" { + return req.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer).ReplaceAll(g.template, ""), g.headers, g.hash, g.displayable + } key = req.URL.Path query, body, host, scheme, method, headerValues, headers, displayable, hash := parseKeyInformations(req, *g) @@ -114,6 +130,9 @@ func (g *keyContext) computeKey(req *http.Request) (key string, headers []string for _, current := range g.overrides { for k, v := range current { if k.MatchString(req.RequestURI) { + if v.template != "" { + return req.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer).ReplaceAll(v.template, ""), v.headers, v.hash, v.displayable + } query, body, host, scheme, method, headerValues, headers, displayable, hash = parseKeyInformations(req, v) hasOverride = true break @@ -139,9 +158,13 @@ func (g *keyContext) SetContext(req *http.Request) *http.Request { context.WithValue( context.WithValue( context.WithValue( - req.Context(), - Key, - key, + context.WithValue( + req.Context(), + Key, + key, + ), + core.DISABLE_VARY_CTX, //nolint:staticcheck // we don't care about collision + g.disable_vary, ), Hashed, hash, diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/context/method.go b/plugins/traefik/vendor/github.com/darkweak/souin/context/method.go index ee772a3de..502e4f2cd 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/context/method.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/context/method.go @@ -26,6 +26,7 @@ func (m *methodContext) SetupContext(c configurationtypes.AbstractConfigurationI m.allowedVerbs = c.GetDefaultCache().GetAllowedHTTPVerbs() m.custom = true } + c.GetLogger().Debugf("Allow %d method(s). %v.", len(m.allowedVerbs), m.allowedVerbs) } func (m *methodContext) SetContext(req *http.Request) *http.Request { diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/context/mode.go b/plugins/traefik/vendor/github.com/darkweak/souin/context/mode.go index ec2d5221d..d436152b5 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/context/mode.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/context/mode.go @@ -22,6 +22,7 @@ func (mc *ModeContext) SetupContext(c configurationtypes.AbstractConfigurationIn mc.Bypass_request = mode == "bypass" || mode == "bypass_request" mc.Bypass_response = mode == "bypass" || mode == "bypass_response" mc.Strict = !mc.Bypass_request && !mc.Bypass_response + c.GetLogger().Debugf("The cache logic will run as %s: %+v", mode, mc) } func (mc *ModeContext) SetContext(req *http.Request) *http.Request { diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/context/now.go b/plugins/traefik/vendor/github.com/darkweak/souin/context/now.go index d0d4e0f3b..da52beb39 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/context/now.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/context/now.go @@ -19,8 +19,16 @@ func (*nowContext) SetContextWithBaseRequest(req *http.Request, _ *http.Request) func (cc *nowContext) SetupContext(_ configurationtypes.AbstractConfigurationInterface) {} func (cc *nowContext) SetContext(req *http.Request) *http.Request { - now := time.Now().UTC() - req.Header.Set("Date", now.Format(time.RFC1123)) + var now time.Time + var e error + + now, e = time.Parse(time.RFC1123, req.Header.Get("Date")) + + if e != nil { + now = time.Now().UTC() + req.Header.Set("Date", now.Format(time.RFC1123)) + } + return req.WithContext(context.WithValue(req.Context(), Now, now)) } diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/context/timeout.go b/plugins/traefik/vendor/github.com/darkweak/souin/context/timeout.go index 4da27d984..a38fb593e 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/context/timeout.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/context/timeout.go @@ -35,6 +35,8 @@ func (t *timeoutContext) SetupContext(c configurationtypes.AbstractConfiguration if c.GetDefaultCache().GetTimeout().Backend.Duration != 0 { t.timeoutBackend = c.GetDefaultCache().GetTimeout().Backend.Duration } + c.GetLogger().Infof("Set backend timeout to %v", t.timeoutBackend) + c.GetLogger().Infof("Set cache timeout to %v", t.timeoutCache) } func (t *timeoutContext) SetContext(req *http.Request) *http.Request { diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/api/debug/debug.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/api/debug/debug.go new file mode 100644 index 000000000..00856f5c5 --- /dev/null +++ b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/api/debug/debug.go @@ -0,0 +1,87 @@ +package debug + +import ( + "net/http" + "net/http/pprof" + "strings" + + "github.com/darkweak/souin/configurationtypes" +) + +// DebugAPI object contains informations related to the endpoints +type DebugAPI struct { + basePath string + enabled bool +} + +type DefaultHandler struct{} + +func (d *DefaultHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + pprof.Index(w, r) +} + +// InitializeDebug initialize the debug endpoints +func InitializeDebug(configuration configurationtypes.AbstractConfigurationInterface) *DebugAPI { + basePath := configuration.GetAPI().Debug.BasePath + enabled := configuration.GetAPI().Debug.Enable + if basePath == "" { + basePath = "/debug/" + } + + return &DebugAPI{ + basePath, + enabled, + } +} + +// GetBasePath will return the basepath for this resource +func (p *DebugAPI) GetBasePath() string { + return p.basePath +} + +// IsEnabled will return enabled status +func (p *DebugAPI) IsEnabled() bool { + return p.enabled +} + +// HandleRequest will handle the request +func (p *DebugAPI) HandleRequest(w http.ResponseWriter, r *http.Request) { + var executor http.Handler + executor = &DefaultHandler{} + + if strings.Contains(r.RequestURI, "allocs") { + executor = pprof.Handler("allocs") + } + if strings.Contains(r.RequestURI, "cmdline") { + executor = pprof.Handler("cmdline") + } + if strings.Contains(r.RequestURI, "profile") { + executor = pprof.Handler("profile") + } + if strings.Contains(r.RequestURI, "symbol") { + executor = pprof.Handler("symbol") + } + if strings.Contains(r.RequestURI, "trace") { + executor = pprof.Handler("trace") + } + if strings.Contains(r.RequestURI, "goroutine") { + executor = pprof.Handler("goroutine") + } + if strings.Contains(r.RequestURI, "heap") { + executor = pprof.Handler("heap") + } + if strings.Contains(r.RequestURI, "block") { + executor = pprof.Handler("block") + } + if strings.Contains(r.RequestURI, "heap") { + executor = pprof.Handler("heap") + } + if strings.Contains(r.RequestURI, "mutex") { + executor = pprof.Handler("mutex") + } + if strings.Contains(r.RequestURI, "threadcreate") { + executor = pprof.Handler("threadcreate") + } + + executor.ServeHTTP(w, r) +} diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/api/main.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/api/main.go index 03230b136..810f447ff 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/api/main.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/api/main.go @@ -4,6 +4,8 @@ import ( "net/http" "github.com/darkweak/souin/configurationtypes" + "github.com/darkweak/souin/pkg/api/debug" + "github.com/darkweak/souin/pkg/api/prometheus" "github.com/darkweak/souin/pkg/storage/types" "github.com/darkweak/souin/pkg/surrogate/providers" ) @@ -31,9 +33,7 @@ func GenerateHandlerMap( for _, endpoint := range Initialize(configuration, storers, surrogateStorage) { if endpoint.IsEnabled() { shouldEnable = true - if e, ok := endpoint.(*SouinAPI); ok { - hm[basePathAPIS+endpoint.GetBasePath()] = e.HandleRequest - } + hm[basePathAPIS+endpoint.GetBasePath()] = endpoint.HandleRequest } } @@ -46,5 +46,6 @@ func GenerateHandlerMap( // Initialize contains all apis that should be enabled func Initialize(c configurationtypes.AbstractConfigurationInterface, storers []types.Storer, surrogateStorage providers.SurrogateInterface) []EndpointInterface { - return []EndpointInterface{initializeSouin(c, storers, surrogateStorage)} + return []EndpointInterface{initializeSouin(c, storers, + surrogateStorage), debug.InitializeDebug(c), prometheus.InitializePrometheus(c)} } diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/api/prometheus/prometheus.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/api/prometheus/prometheus.go index 2712571d6..051d122f6 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/api/prometheus/prometheus.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/api/prometheus/prometheus.go @@ -1,5 +1,107 @@ package prometheus -const RequestCounter = "" +import ( + "net/http" -func Increment(string) {} + "github.com/darkweak/souin/configurationtypes" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +const ( + counter = "counter" + average = "average" + + RequestCounter = "souin_request_upstream_counter" + RequestRevalidationCounter = "souin_request_revalidation_counter" + NoCachedResponseCounter = "souin_no_cached_response_counter" + CachedResponseCounter = "souin_cached_response_counter" + AvgResponseTime = "souin_avg_response_time" +) + +// PrometheusAPI object contains informations related to the endpoints +type PrometheusAPI struct { + basePath string + enabled bool +} + +// InitializePrometheus initialize the prometheus endpoints +func InitializePrometheus(configuration configurationtypes.AbstractConfigurationInterface) *PrometheusAPI { + basePath := configuration.GetAPI().Prometheus.BasePath + enabled := configuration.GetAPI().Prometheus.Enable + if basePath == "" { + basePath = "/metrics" + } + + if registered == nil { + run() + } + return &PrometheusAPI{ + basePath, + enabled, + } +} + +// GetBasePath will return the basepath for this resource +func (p *PrometheusAPI) GetBasePath() string { + return p.basePath +} + +// IsEnabled will return enabled status +func (p *PrometheusAPI) IsEnabled() bool { + return p.enabled +} + +// HandleRequest will handle the request +func (p *PrometheusAPI) HandleRequest(w http.ResponseWriter, r *http.Request) { + promhttp.Handler().ServeHTTP(w, r) +} + +var registered map[string]interface{} + +// Increment will increment the counter. +func Increment(name string) { + if _, ok := registered[name]; ok { + registered[name].(prometheus.Counter).Inc() + } +} + +// Increment will add the referred value the counter. +func Add(name string, value float64) { + if c, ok := registered[name].(prometheus.Counter); ok { + c.Add(value) + } + if g, ok := registered[name].(prometheus.Histogram); ok { + g.Observe(value) + } +} + +func push(promType, name, help string) { + switch promType { + case counter: + registered[name] = promauto.NewCounter(prometheus.CounterOpts{ + Name: name, + Help: help, + }) + + return + case average: + avg := prometheus.NewHistogram(prometheus.HistogramOpts{ + Name: name, + Help: help, + }) + prometheus.MustRegister(avg) + registered[name] = avg + } +} + +// Run populate and prepare the map with the default values. +func run() { + registered = make(map[string]interface{}) + push(counter, RequestCounter, "Total upstream request counter") + push(counter, RequestRevalidationCounter, "Total revalidation request revalidation counter") + push(counter, NoCachedResponseCounter, "No cached response counter") + push(counter, CachedResponseCounter, "Cached response counter") + push(average, AvgResponseTime, "Average response time") +} diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/api/souin.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/api/souin.go index 8f5305cb9..131723edb 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/api/souin.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/api/souin.go @@ -6,10 +6,14 @@ import ( "net/http" "regexp" "strings" + "time" "github.com/darkweak/souin/configurationtypes" "github.com/darkweak/souin/pkg/storage/types" "github.com/darkweak/souin/pkg/surrogate/providers" + "github.com/darkweak/storages/core" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" ) // SouinAPI object contains informations related to the endpoints @@ -62,16 +66,54 @@ func initializeSouin( } // BulkDelete allow user to delete multiple items with regexp -func (s *SouinAPI) BulkDelete(key string) { +func (s *SouinAPI) BulkDelete(key string, purge bool) { + key, _ = strings.CutPrefix(key, core.MappingKeyPrefix) for _, current := range s.storers { - current.DeleteMany(key) + if b := current.Get(core.MappingKeyPrefix + key); len(b) > 0 { + var mapping core.StorageMapper + + if e := proto.Unmarshal(b, &mapping); e == nil { + for k := range mapping.GetMapping() { + current.Delete(k) + } + } + + if !purge { + newFreshTime := time.Now() + for k, v := range mapping.Mapping { + v.FreshTime = timestamppb.New(newFreshTime) + mapping.Mapping[k] = v + } + + v, e := proto.Marshal(&mapping) + if e != nil { + fmt.Println("Impossible to re-encode the mapping", core.MappingKeyPrefix+key) + current.Delete(core.MappingKeyPrefix + key) + } + _ = current.Set(core.MappingKeyPrefix+key, v, storageToInfiniteTTLMap[current.Name()]) + } + } + + if purge { + current.Delete(core.MappingKeyPrefix + key) + } + + current.Delete(key) } + + s.Delete(key) } // Delete will delete a record into the provider cache system and will update the Souin API if enabled +// The key can be a regexp to delete multiple items func (s *SouinAPI) Delete(key string) { + _, err := regexp.Compile(key) for _, current := range s.storers { - current.Delete(key) + if err != nil { + current.DeleteMany(key) + } else { + current.Delete(key) + } } } @@ -110,6 +152,64 @@ func (s *SouinAPI) listKeys(search string) []string { return res } +var storageToInfiniteTTLMap = map[string]time.Duration{ + "BADGER": types.OneYearDuration, + "ETCD": types.OneYearDuration, + "GO-REDIS": 0, + "NUTS": 0, + "OLRIC": types.OneYearDuration, + "OTTER": types.OneYearDuration, + "REDIS": -1, + "SIMPLEFS": 0, + types.DefaultStorageName: types.OneYearDuration, +} + +func EvictMapping(current types.Storer) { + values := current.MapKeys(core.MappingKeyPrefix) + now := time.Now() + infiniteStoreDuration := storageToInfiniteTTLMap[current.Name()] + + for k, v := range values { + mapping := &core.StorageMapper{} + + e := proto.Unmarshal([]byte(v), mapping) + if e != nil { + current.Delete(core.MappingKeyPrefix + k) + continue + } + + updated := false + for key, val := range mapping.GetMapping() { + if now.Sub(val.FreshTime.AsTime()) > 0 && now.Sub(val.StaleTime.AsTime()) > 0 { + delete(mapping.GetMapping(), key) + updated = true + } + } + + if updated { + v, e := proto.Marshal(mapping) + if e != nil { + fmt.Println("Impossible to re-encode the mapping", core.MappingKeyPrefix+k) + current.Delete(core.MappingKeyPrefix + k) + } + _ = current.Set(core.MappingKeyPrefix+k, v, infiniteStoreDuration) + } + + if len(mapping.GetMapping()) == 0 { + current.Delete(core.MappingKeyPrefix + k) + } + } + time.Sleep(time.Minute) +} + +func (s *SouinAPI) purgeMapping() { + for _, current := range s.storers { + EvictMapping(current) + } + + fmt.Println("Successfully clear the mappings.") +} + // HandleRequest will handle the request func (s *SouinAPI) HandleRequest(w http.ResponseWriter, r *http.Request) { res := []byte{} @@ -130,6 +230,9 @@ func (s *SouinAPI) HandleRequest(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") case http.MethodPost: var invalidator invalidation + defer func() { + _ = r.Body.Close() + }() err := json.NewDecoder(r.Body).Decode(&invalidator) if err != nil { w.WriteHeader(http.StatusBadRequest) @@ -139,7 +242,9 @@ func (s *SouinAPI) HandleRequest(w http.ResponseWriter, r *http.Request) { keysToInvalidate := []string{} switch invalidator.Type { case groupInvalidationType: - keysToInvalidate, _ = s.surrogateStorage.Purge(http.Header{"Surrogate-Key": invalidator.Groups}) + var surrogateKeys []string + keysToInvalidate, surrogateKeys = s.surrogateStorage.Purge(http.Header{"Surrogate-Key": invalidator.Groups}) + keysToInvalidate = append(keysToInvalidate, surrogateKeys...) case uriPrefixInvalidationType, uriInvalidationType: bodyKeys := []string{} listedKeys := s.GetAll() @@ -195,15 +300,14 @@ func (s *SouinAPI) HandleRequest(w http.ResponseWriter, r *http.Request) { } for _, k := range keysToInvalidate { - for _, current := range s.storers { - current.Delete(k) - } + s.BulkDelete(k, invalidator.Purge) } w.WriteHeader(http.StatusOK) case "PURGE": if compile { keysRg := regexp.MustCompile(s.GetBasePath() + "/(.+)") flushRg := regexp.MustCompile(s.GetBasePath() + "/flush$") + mappingRg := regexp.MustCompile(s.GetBasePath() + "/mapping$") if flushRg.FindString(r.RequestURI) != "" { for _, current := range s.storers { @@ -214,16 +318,21 @@ func (s *SouinAPI) HandleRequest(w http.ResponseWriter, r *http.Request) { fmt.Printf("Error while purging the surrogate keys: %+v.", e) } fmt.Println("Successfully clear the cache and the surrogate keys storage.") + } else if mappingRg.FindString(r.RequestURI) != "" { + s.purgeMapping() } else { submatch := keysRg.FindAllStringSubmatch(r.RequestURI, -1)[0][1] - s.BulkDelete(submatch) + for _, current := range s.storers { + current.DeleteMany(submatch) + } } } else { - ck, _ := s.surrogateStorage.Purge(r.Header) + ck, surrogateKeys := s.surrogateStorage.Purge(r.Header) for _, k := range ck { - for _, current := range s.storers { - current.Delete(k) - } + s.BulkDelete(k, true) + } + for _, k := range surrogateKeys { + s.BulkDelete("SURROGATE_"+k, true) } } w.WriteHeader(http.StatusNoContent) diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/middleware/configuration.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/middleware/configuration.go index 238dfcdb8..34bdd0c56 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/middleware/configuration.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/middleware/configuration.go @@ -2,6 +2,7 @@ package middleware import ( "github.com/darkweak/souin/configurationtypes" + "github.com/darkweak/storages/core" ) // BaseConfiguration holder @@ -11,6 +12,7 @@ type BaseConfiguration struct { CacheKeys configurationtypes.CacheKeys `json:"cache_keys" yaml:"cache_keys"` URLs map[string]configurationtypes.URL `json:"urls" yaml:"urls"` LogLevel string `json:"log_level" yaml:"log_level"` + Logger core.Logger PluginName string Ykeys map[string]configurationtypes.SurrogateKeys `json:"ykeys" yaml:"ykeys"` SurrogateKeys map[string]configurationtypes.SurrogateKeys `json:"surrogate_keys" yaml:"surrogate_keys"` @@ -42,6 +44,16 @@ func (c *BaseConfiguration) GetLogLevel() string { return c.LogLevel } +// GetLogger get the logger +func (c *BaseConfiguration) GetLogger() core.Logger { + return c.Logger +} + +// SetLogger set the logger +func (c *BaseConfiguration) SetLogger(l core.Logger) { + c.Logger = l +} + // GetYkeys get the ykeys list func (c *BaseConfiguration) GetYkeys() map[string]configurationtypes.SurrogateKeys { return c.SurrogateKeys diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/middleware/middleware.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/middleware/middleware.go index e81c15dfc..9b4848039 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/middleware/middleware.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/middleware/middleware.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "maps" "net/http" "net/http/httputil" "regexp" @@ -13,29 +14,123 @@ import ( "sync" "time" + "github.com/cespare/xxhash/v2" "github.com/darkweak/souin/configurationtypes" "github.com/darkweak/souin/context" "github.com/darkweak/souin/helpers" "github.com/darkweak/souin/pkg/api" + "github.com/darkweak/souin/pkg/api/prometheus" "github.com/darkweak/souin/pkg/rfc" "github.com/darkweak/souin/pkg/storage" "github.com/darkweak/souin/pkg/storage/types" "github.com/darkweak/souin/pkg/surrogate" "github.com/darkweak/souin/pkg/surrogate/providers" + "github.com/darkweak/storages/core" "github.com/google/uuid" "github.com/pquerna/cachecontrol/cacheobject" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" "golang.org/x/sync/singleflight" ) +func reorderStorers(storers []types.Storer, expectedStorers []string) []types.Storer { + if len(expectedStorers) == 0 { + return storers + } + + newStorers := make([]types.Storer, 0) + for _, expectedStorer := range expectedStorers { + for _, storer := range storers { + if storer.Name() == strings.ToUpper(expectedStorer) { + newStorers = append(newStorers, storer) + } + } + } + + return newStorers +} + +func registerMappingKeysEviction(logger core.Logger, storers []types.Storer) { + for _, storer := range storers { + logger.Debugf("registering mapping eviction for storer %s", storer.Name()) + go func(current types.Storer) { + for { + logger.Debugf("run mapping eviction for storer %s", current.Name()) + + api.EvictMapping(current) + } + }(storer) + } +} + func NewHTTPCacheHandler(c configurationtypes.AbstractConfigurationInterface) *SouinBaseHandler { - storers, err := storage.NewStorages(c) - if err != nil { - panic(err) + if c.GetLogger() == nil { + var logLevel zapcore.Level + if c.GetLogLevel() == "" { + logLevel = zapcore.FatalLevel + } else if err := logLevel.UnmarshalText([]byte(c.GetLogLevel())); err != nil { + logLevel = zapcore.FatalLevel + } + cfg := zap.Config{ + Encoding: "json", + Level: zap.NewAtomicLevelAt(logLevel), + OutputPaths: []string{"stderr"}, + ErrorOutputPaths: []string{"stderr"}, + EncoderConfig: zapcore.EncoderConfig{ + MessageKey: "message", + + LevelKey: "level", + EncodeLevel: zapcore.CapitalLevelEncoder, + + TimeKey: "time", + EncodeTime: zapcore.ISO8601TimeEncoder, + + CallerKey: "caller", + EncodeCaller: zapcore.ShortCallerEncoder, + }, + } + logger, _ := cfg.Build() + c.SetLogger(logger.Sugar()) + } + + storedStorers := core.GetRegisteredStorers() + storers := []types.Storer{} + if len(storedStorers) != 0 { + dc := c.GetDefaultCache() + for _, s := range []string{dc.GetBadger().Uuid, dc.GetEtcd().Uuid, dc.GetNats().Uuid, dc.GetNuts().Uuid, dc.GetOlric().Uuid, dc.GetOtter().Uuid, dc.GetRedis().Uuid, dc.GetSimpleFS().Uuid} { + if s != "" { + if st := core.GetRegisteredStorer(s); st != nil { + storers = append(storers, st.(types.Storer)) + } + } + } + + storers = reorderStorers(storers, c.GetDefaultCache().GetStorers()) + + if len(storers) > 0 { + names := []string{} + for _, storer := range storers { + names = append(names, storer.Name()) + } + c.GetLogger().Debugf("You're running Souin with the following storages in this order %s", strings.Join(names, ", ")) + } + } + if len(storers) == 0 { + c.GetLogger().Warn("You're running Souin with the default storage that is not optimized and for development purpose. We recommend to use at least one of the storages from https://github.com/darkweak/storages") + + memoryStorer, _ := storage.Factory(c) + if st := core.GetRegisteredStorer(types.DefaultStorageName + "-"); st != nil { + memoryStorer = st.(types.Storer) + } else { + core.RegisterStorage(memoryStorer) + } + storers = append(storers, memoryStorer) } - fmt.Println("Storers initialized.") + + c.GetLogger().Debugf("Storer initialized: %#v.", storers) regexpUrls := helpers.InitializeRegexp(c) - surrogateStorage := surrogate.InitializeSurrogate(c, storers[0].Name()) - fmt.Println("Surrogate storage initialized.") + surrogateStorage := surrogate.InitializeSurrogate(c, fmt.Sprintf("%s-%s", storers[0].Name(), storers[0].Uuid())) + c.GetLogger().Debug("Surrogate storage initialized.") var excludedRegexp *regexp.Regexp = nil if c.GetDefaultCache().GetRegex().Exclude != "" { excludedRegexp = regexp.MustCompile(c.GetDefaultCache().GetRegex().Exclude) @@ -54,7 +149,9 @@ func NewHTTPCacheHandler(c configurationtypes.AbstractConfigurationInterface) *S Headers: c.GetDefaultCache().GetHeaders(), DefaultCacheControl: c.GetDefaultCache().GetDefaultCacheControl(), } - fmt.Println("Souin configuration is now loaded.") + c.GetLogger().Info("Souin configuration is now loaded.") + + registerMappingKeysEviction(c.GetLogger(), storers) return &SouinBaseHandler{ Configuration: c, @@ -137,6 +234,7 @@ func (s *SouinBaseHandler) Store( rq *http.Request, requestCc *cacheobject.RequestCacheDirectives, cachedKey string, + uri string, ) error { statusCode := customWriter.GetStatusCode() if !isCacheableCode(statusCode) && !s.hasAllowedAdditionalStatusCodesToCache(statusCode) { @@ -161,6 +259,7 @@ func (s *SouinBaseHandler) Store( } responseCc, _ := cacheobject.ParseResponseCacheControl(rfc.HeaderAllCommaSepValuesString(customWriter.Header(), headerName)) + s.Configuration.GetLogger().Debugf("Response cache-control %+v", responseCc) if responseCc == nil { customWriter.Header().Set("Cache-Status", fmt.Sprintf("%s; fwd=uri-miss; key=%s; detail=INVALID-RESPONSE-CACHE-CONTROL", rq.Context().Value(context.CacheName), rfc.GetCacheKeyFromCtx(rq.Context()))) return nil @@ -251,13 +350,18 @@ func (s *SouinBaseHandler) Store( } res.Header.Set(rfc.StoredLengthHeader, res.Header.Get("Content-Length")) response, err := httputil.DumpResponse(&res, true) - if err == nil && (bLen > 0 || canStatusCodeEmptyContent(statusCode) || s.hasAllowedAdditionalStatusCodesToCache(statusCode)) { + if err == nil && (bLen > 0 || rq.Method == http.MethodHead || canStatusCodeEmptyContent(statusCode) || s.hasAllowedAdditionalStatusCodesToCache(statusCode)) { variedHeaders, isVaryStar := rfc.VariedHeaderAllCommaSepValues(res.Header) if isVaryStar { // "Implies that the response is uncacheable" status += "; detail=UPSTREAM-VARY-STAR" } else { variedKey := cachedKey + rfc.GetVariedCacheKey(rq, variedHeaders) + if rq.Context().Value(context.Hashed).(bool) { + cachedKey = fmt.Sprint(xxhash.Sum64String(cachedKey)) + variedKey = fmt.Sprint(xxhash.Sum64String(variedKey)) + } + s.Configuration.GetLogger().Debugf("Store the response for %s with duration %v", variedKey, ma) var wg sync.WaitGroup mu := sync.Mutex{} @@ -271,32 +375,61 @@ func (s *SouinBaseHandler) Store( hn := strings.Split(hname, ":") vhs.Set(hn[0], rq.Header.Get(hn[0])) } - for _, storer := range s.Storers { - wg.Add(1) - go func(currentStorer types.Storer) { - defer wg.Done() - if currentStorer.SetMultiLevel( - cachedKey, - variedKey, - response, - vhs, - res.Header.Get("Etag"), ma, - variedKey, - ) == nil { - res.Request = rq - } else { - mu.Lock() - fails = append(fails, fmt.Sprintf("; detail=%s-INSERTION-ERROR", currentStorer.Name())) - mu.Unlock() + if upstreamStorerTarget := res.Header.Get("X-Souin-Storer"); upstreamStorerTarget != "" { + res.Header.Del("X-Souin-Storer") + + var overridedStorer types.Storer + for _, storer := range s.Storers { + if strings.Contains(strings.ToLower(storer.Name()), strings.ToLower(upstreamStorerTarget)) { + overridedStorer = storer } - }(storer) + } + + if overridedStorer.SetMultiLevel( + cachedKey, + variedKey, + response, + vhs, + res.Header.Get("Etag"), ma, + variedKey, + ) == nil { + s.Configuration.GetLogger().Debugf("Stored the key %s in the %s provider", variedKey, overridedStorer.Name()) + res.Request = rq + } else { + fails = append(fails, fmt.Sprintf("; detail=%s-INSERTION-ERROR", overridedStorer.Name())) + } + } else { + for _, storer := range s.Storers { + wg.Add(1) + go func(currentStorer types.Storer, currentRes http.Response) { + defer wg.Done() + if currentStorer.SetMultiLevel( + cachedKey, + variedKey, + response, + vhs, + currentRes.Header.Get("Etag"), ma, + variedKey, + ) == nil { + s.Configuration.GetLogger().Debugf("Stored the key %s in the %s provider", variedKey, currentStorer.Name()) + currentRes.Request = rq + } else { + mu.Lock() + fails = append(fails, fmt.Sprintf("; detail=%s-INSERTION-ERROR", currentStorer.Name())) + mu.Unlock() + } + }(storer, res) + } } wg.Wait() if len(fails) < s.storersLen { - go func(rs http.Response, key string) { - _ = s.SurrogateKeyStorer.Store(&rs, key, "") - }(res, variedKey) + if !s.Configuration.IsSurrogateDisabled() { + go func(rs http.Response, key string) { + _ = s.SurrogateKeyStorer.Store(&rs, key, uri) + }(res, variedKey) + } + status += "; stored" } @@ -318,10 +451,11 @@ func (s *SouinBaseHandler) Store( } type singleflightValue struct { - body []byte - headers http.Header - requestHeaders http.Header - code int + body []byte + headers http.Header + requestHeaders http.Header + code int + disableCoalescing bool } func (s *SouinBaseHandler) Upstream( @@ -330,7 +464,12 @@ func (s *SouinBaseHandler) Upstream( next handlerFunc, requestCc *cacheobject.RequestCacheDirectives, cachedKey string, + uri string, + disableCoalescing bool, ) error { + s.Configuration.GetLogger().Debug("Request the upstream server") + prometheus.Increment(prometheus.RequestCounter) + var recoveredFromErr error = nil defer func() { // In case of "http.ErrAbortHandler" panic, @@ -347,16 +486,19 @@ func (s *SouinBaseHandler) Upstream( }() singleflightCacheKey := cachedKey - if s.Configuration.GetDefaultCache().IsCoalescingDisable() { + if s.Configuration.GetDefaultCache().IsCoalescingDisable() || disableCoalescing { singleflightCacheKey += uuid.NewString() } - sfValue, err, _ := s.singleflightPool.Do(singleflightCacheKey, func() (interface{}, error) { + sfValue, err, shared := s.singleflightPool.Do(singleflightCacheKey, func() (interface{}, error) { if e := next(customWriter, rq); e != nil { + s.Configuration.GetLogger().Warnf("%#v", e) customWriter.Header().Set("Cache-Status", fmt.Sprintf("%s; fwd=uri-miss; key=%s; detail=SERVE-HTTP-ERROR", rq.Context().Value(context.CacheName), rfc.GetCacheKeyFromCtx(rq.Context()))) return nil, e } - s.SurrogateKeyStorer.Invalidate(rq.Method, customWriter.Header()) + if !s.Configuration.IsSurrogateDisabled() { + s.SurrogateKeyStorer.Invalidate(rq.Method, customWriter.Header()) + } statusCode := customWriter.GetStatusCode() if !isCacheableCode(statusCode) && !s.hasAllowedAdditionalStatusCodesToCache(statusCode) { @@ -373,16 +515,17 @@ func (s *SouinBaseHandler) Upstream( customWriter.Header().Set(headerName, s.DefaultMatchedUrl.DefaultCacheControl) } - err := s.Store(customWriter, rq, requestCc, cachedKey) + err := s.Store(customWriter, rq, requestCc, cachedKey, uri) defer customWriter.handleBuffer(func(b *bytes.Buffer) { b.Reset() }) return singleflightValue{ - body: customWriter.Buf.Bytes(), - headers: customWriter.Header().Clone(), - requestHeaders: rq.Header, - code: statusCode, + body: customWriter.Buf.Bytes(), + headers: customWriter.Header().Clone(), + requestHeaders: rq.Header, + code: statusCode, + disableCoalescing: strings.Contains(cacheControl, "private") || customWriter.Header().Get("Set-Cookie") != "", }, err }) if recoveredFromErr != nil { @@ -393,37 +536,47 @@ func (s *SouinBaseHandler) Upstream( } if sfWriter, ok := sfValue.(singleflightValue); ok { + if shared && sfWriter.disableCoalescing { + return s.Upstream(customWriter, rq, next, requestCc, cachedKey, uri, true) + } + if vary := sfWriter.headers.Get("Vary"); vary != "" { variedHeaders, isVaryStar := rfc.VariedHeaderAllCommaSepValues(sfWriter.headers) if !isVaryStar { for _, vh := range variedHeaders { if rq.Header.Get(vh) != sfWriter.requestHeaders.Get(vh) { // cachedKey += rfc.GetVariedCacheKey(rq, variedHeaders) - return s.Upstream(customWriter, rq, next, requestCc, cachedKey) + return s.Upstream(customWriter, rq, next, requestCc, cachedKey, uri, false) } } } } - customWriter.Buf.Reset() - _, _ = customWriter.Write(sfWriter.body) - // Yaegi sucks, we can't use maps. - for k := range sfWriter.headers { - customWriter.Header().Set(k, sfWriter.headers.Get(k)) + + if shared { + s.Configuration.GetLogger().Infof("Reused response from concurrent request with the key %s", cachedKey) } + _, _ = customWriter.Write(sfWriter.body) + maps.Copy(customWriter.Header(), sfWriter.headers) customWriter.WriteHeader(sfWriter.code) } return nil } -func (s *SouinBaseHandler) Revalidate(validator *types.Revalidator, next handlerFunc, customWriter *CustomWriter, rq *http.Request, requestCc *cacheobject.RequestCacheDirectives, cachedKey string, uri string) error { +func (s *SouinBaseHandler) Revalidate(validator *core.Revalidator, next handlerFunc, customWriter *CustomWriter, rq *http.Request, requestCc *cacheobject.RequestCacheDirectives, cachedKey string, uri string) error { + s.Configuration.GetLogger().Debug("Revalidate the request with the upstream server") + prometheus.Increment(prometheus.RequestRevalidationCounter) + singleflightCacheKey := cachedKey if s.Configuration.GetDefaultCache().IsCoalescingDisable() { singleflightCacheKey += uuid.NewString() } - sfValue, err, _ := s.singleflightPool.Do(singleflightCacheKey, func() (interface{}, error) { + sfValue, err, shared := s.singleflightPool.Do(singleflightCacheKey, func() (interface{}, error) { err := next(customWriter, rq) - s.SurrogateKeyStorer.Invalidate(rq.Method, customWriter.Header()) + + if !s.Configuration.IsSurrogateDisabled() { + s.SurrogateKeyStorer.Invalidate(rq.Method, customWriter.Header()) + } statusCode := customWriter.GetStatusCode() if err == nil { @@ -448,7 +601,7 @@ func (s *SouinBaseHandler) Revalidate(validator *types.Revalidator, next handler } if statusCode != http.StatusNotModified { - err = s.Store(customWriter, rq, requestCc, cachedKey) + err = s.Store(customWriter, rq, requestCc, cachedKey, uri) } } @@ -473,11 +626,11 @@ func (s *SouinBaseHandler) Revalidate(validator *types.Revalidator, next handler }) if sfWriter, ok := sfValue.(singleflightValue); ok { - _, _ = customWriter.Write(sfWriter.body) - // Yaegi sucks, we can't use maps. - for k := range sfWriter.headers { - customWriter.Header().Set(k, sfWriter.headers.Get(k)) + if shared { + s.Configuration.GetLogger().Infof("Reused response from concurrent request with the key %s", cachedKey) } + _, _ = customWriter.Write(sfWriter.body) + maps.Copy(customWriter.Header(), sfWriter.headers) customWriter.WriteHeader(sfWriter.code) } @@ -493,8 +646,7 @@ func (s *SouinBaseHandler) HandleInternally(r *http.Request) (bool, http.Handler } } - // Because Yægi interpretation sucks, we have to return the empty function instead of nil. - return false, func(w http.ResponseWriter, r *http.Request) {} + return false, nil } type handlerFunc = func(http.ResponseWriter, *http.Request) error @@ -508,7 +660,62 @@ func (s *statusCodeLogger) WriteHeader(code int) { s.ResponseWriter.WriteHeader(code) } +func (s *SouinBaseHandler) backfillStorers(idx int, cachedKey string, rq *http.Request, response *http.Response) { + if idx == 0 { + return + } + + storedDuration, err := time.ParseDuration(response.Header.Get(rfc.StoredTTLHeader)) + if err != nil { + return + } + + dateHeader, err := http.ParseTime(response.Header.Get("Date")) + if err != nil { + return + } + + now := time.Now() + + ma := storedDuration - now.Sub(dateHeader) + + variedHeaders, _ := rfc.VariedHeaderAllCommaSepValues(response.Header) + variedKey := cachedKey + rfc.GetVariedCacheKey(rq, variedHeaders) + + if rq.Context().Value(context.Hashed).(bool) { + cachedKey = fmt.Sprint(xxhash.Sum64String(cachedKey)) + variedKey = fmt.Sprint(xxhash.Sum64String(variedKey)) + } + + vhs := http.Header{} + for _, hname := range variedHeaders { + hn := strings.Split(hname, ":") + vhs.Set(hn[0], rq.Header.Get(hn[0])) + } + + res, _ := httputil.DumpResponse(response, true) + + for _, currentStorer := range s.Storers[:idx] { + err = currentStorer.SetMultiLevel( + cachedKey, + variedKey, + res, + vhs, + response.Header.Get("Etag"), ma, + variedKey, + ) + if err != nil { + s.Configuration.GetLogger().Errorf("Error while backfilling the storer %s: %v", currentStorer.Name(), err) + } + } +} + func (s *SouinBaseHandler) ServeHTTP(rw http.ResponseWriter, rq *http.Request, next handlerFunc) error { + start := time.Now() + defer func(s time.Time) { + prometheus.Add(prometheus.AvgResponseTime, float64(time.Since(s).Milliseconds())) + }(start) + s.Configuration.GetLogger().Debugf("Incomming request %+v", rq) if b, handler := s.HandleInternally(rq); b { handler(rw, rq) return nil @@ -530,14 +737,17 @@ func (s *SouinBaseHandler) ServeHTTP(rw http.ResponseWriter, rq *http.Request, n } err := next(nrw, req) - s.SurrogateKeyStorer.Invalidate(req.Method, rw.Header()) + + if !s.Configuration.IsSurrogateDisabled() { + s.SurrogateKeyStorer.Invalidate(req.Method, rw.Header()) + } if err == nil && req.Method != http.MethodGet && nrw.statusCode < http.StatusBadRequest { // Invalidate related GET keys when the method is not allowed and the response is valid req.Method = http.MethodGet keyname := s.context.SetContext(req, rq).Context().Value(context.Key).(string) for _, storer := range s.Storers { - storer.Delete("IDX_" + keyname) + storer.Delete(core.MappingKeyPrefix + keyname) } } @@ -551,29 +761,26 @@ func (s *SouinBaseHandler) ServeHTTP(rw http.ResponseWriter, rq *http.Request, n rw.Header().Set("Cache-Status", cacheName+"; fwd=bypass; detail=CACHE-CONTROL-EXTRACTION-ERROR") err := next(rw, req) - s.SurrogateKeyStorer.Invalidate(req.Method, rw.Header()) + + if !s.Configuration.IsSurrogateDisabled() { + s.SurrogateKeyStorer.Invalidate(req.Method, rw.Header()) + } return err } req = s.context.SetContext(req, rq) - - isMutationRequest := false - // Yaegi sucks AGAIN, it considers the value as nil if we directly try to cast as bool - mutationRequestValue := req.Context().Value(context.IsMutationRequest) - if mutationRequestValue != nil { - isMutationRequest = mutationRequestValue.(bool) - } - - if isMutationRequest { + if req.Context().Value(context.IsMutationRequest).(bool) { rw.Header().Set("Cache-Status", cacheName+"; fwd=bypass; detail=IS-MUTATION-REQUEST") err := next(rw, req) - s.SurrogateKeyStorer.Invalidate(req.Method, rw.Header()) + + if !s.Configuration.IsSurrogateDisabled() { + s.SurrogateKeyStorer.Invalidate(req.Method, rw.Header()) + } return err } - cachedKey := req.Context().Value(context.Key).(string) // Need to copy URL path before calling next because it can alter the URI @@ -581,7 +788,10 @@ func (s *SouinBaseHandler) ServeHTTP(rw http.ResponseWriter, rq *http.Request, n bufPool := s.bufPool.Get().(*bytes.Buffer) bufPool.Reset() defer s.bufPool.Put(bufPool) + customWriter := NewCustomWriter(req, rw, bufPool) + customWriter.Headers.Add("Range", req.Header.Get("Range")) + // req.Header.Del("Range") go func(req *http.Request, crw *CustomWriter) { <-req.Context().Done() @@ -590,22 +800,37 @@ func (s *SouinBaseHandler) ServeHTTP(rw http.ResponseWriter, rq *http.Request, n crw.mutex.Unlock() }(req, customWriter) + backfillIds := 0 + + s.Configuration.GetLogger().Debugf("Request cache-control %+v", requestCc) if modeContext.Bypass_request || !requestCc.NoCache { validator := rfc.ParseRequest(req) var fresh, stale *http.Response var storerName string + finalKey := cachedKey + if req.Context().Value(context.Hashed).(bool) { + finalKey = fmt.Sprint(xxhash.Sum64String(finalKey)) + } for _, currentStorer := range s.Storers { - fresh, stale = currentStorer.GetMultiLevel(cachedKey, req, validator) + fresh, stale = currentStorer.GetMultiLevel(finalKey, req, validator) if fresh != nil || stale != nil { storerName = currentStorer.Name() + s.Configuration.GetLogger().Debugf("Found at least one valid response in the %s storage", storerName) break } + + backfillIds++ } headerName, _ := s.SurrogateKeyStorer.GetSurrogateControl(customWriter.Header()) if fresh != nil && (!modeContext.Strict || rfc.ValidateCacheControl(fresh, requestCc)) { + go func() { + s.backfillStorers(backfillIds, cachedKey, req, fresh) + }() + response := fresh + if validator.ResponseETag != "" && validator.Matched { rfc.SetCacheStatusHeader(response, storerName) for h, v := range response.Header { @@ -637,6 +862,7 @@ func (s *SouinBaseHandler) ServeHTTP(rw http.ResponseWriter, rq *http.Request, n return err } if resCc, _ := cacheobject.ParseResponseCacheControl(rfc.HeaderAllCommaSepValuesString(response.Header, headerName)); !modeContext.Bypass_response && resCc.NoCachePresent { + prometheus.Increment(prometheus.NoCachedResponseCounter) err := s.Revalidate(validator, next, customWriter, req, requestCc, cachedKey, uri) _, _ = customWriter.Send() @@ -648,10 +874,12 @@ func (s *SouinBaseHandler) ServeHTTP(rw http.ResponseWriter, rq *http.Request, n customWriter.Header()[h] = v } customWriter.WriteHeader(response.StatusCode) + s.Configuration.GetLogger().Debugf("Serve from cache %+v", req) customWriter.handleBuffer(func(b *bytes.Buffer) { _, _ = io.Copy(b, response.Body) }) _, err := customWriter.Send() + prometheus.Increment(prometheus.CachedResponseCounter) return err } @@ -674,7 +902,7 @@ func (s *SouinBaseHandler) ServeHTTP(rw http.ResponseWriter, rq *http.Request, n }) _, err := customWriter.Send() customWriter = NewCustomWriter(req, rw, bufPool) - go func(v *types.Revalidator, goCw *CustomWriter, goRq *http.Request, goNext func(http.ResponseWriter, *http.Request) error, goCc *cacheobject.RequestCacheDirectives, goCk string, goUri string) { + go func(v *core.Revalidator, goCw *CustomWriter, goRq *http.Request, goNext func(http.ResponseWriter, *http.Request) error, goCc *cacheobject.RequestCacheDirectives, goCk string, goUri string) { _ = s.Revalidate(v, goNext, goCw, goRq, goCc, goCk, goUri) }(validator, customWriter, req, next, requestCc, cachedKey, uri) buf := s.bufPool.Get().(*bytes.Buffer) @@ -693,10 +921,7 @@ func (s *SouinBaseHandler) ServeHTTP(rw http.ResponseWriter, rq *http.Request, n code := fmt.Sprintf("; fwd-status=%d", statusCode) rfc.HitStaleCache(&response.Header) response.Header.Set("Cache-Status", response.Header.Get("Cache-Status")+code) - // Yaegi sucks, we can't use maps. - for k := range response.Header { - customWriter.Header().Set(k, response.Header.Get(k)) - } + maps.Copy(customWriter.Header(), response.Header) customWriter.WriteHeader(response.StatusCode) customWriter.handleBuffer(func(b *bytes.Buffer) { b.Reset() @@ -719,10 +944,7 @@ func (s *SouinBaseHandler) ServeHTTP(rw http.ResponseWriter, rq *http.Request, n if !validator.Matched { rfc.SetCacheStatusHeader(response, storerName) customWriter.WriteHeader(response.StatusCode) - // Yaegi sucks, we can't use maps. - for k := range response.Header { - customWriter.Header().Set(k, response.Header.Get(k)) - } + maps.Copy(customWriter.Header(), response.Header) customWriter.handleBuffer(func(b *bytes.Buffer) { _, _ = io.Copy(b, response.Body) }) @@ -750,10 +972,7 @@ func (s *SouinBaseHandler) ServeHTTP(rw http.ResponseWriter, rq *http.Request, n if !modeContext.Strict || rfc.ValidateMaxAgeCachedStaleResponse(requestCc, responseCc, response, int(addTime.Seconds())) != nil { customWriter.WriteHeader(response.StatusCode) rfc.HitStaleCache(&response.Header) - // Yaegi sucks, we can't use maps. - for k := range response.Header { - customWriter.Header().Set(k, response.Header.Get(k)) - } + maps.Copy(customWriter.Header(), response.Header) customWriter.handleBuffer(func(b *bytes.Buffer) { _, _ = io.Copy(b, response.Body) }) @@ -764,6 +983,20 @@ func (s *SouinBaseHandler) ServeHTTP(rw http.ResponseWriter, rq *http.Request, n } } else if stale != nil { response := stale + + if !modeContext.Strict { + rfc.SetCacheStatusHeader(response, storerName) + customWriter.WriteHeader(response.StatusCode) + rfc.HitStaleCache(&response.Header) + maps.Copy(customWriter.Header(), response.Header) + customWriter.handleBuffer(func(b *bytes.Buffer) { + _, _ = io.Copy(b, response.Body) + }) + _, err := customWriter.Send() + + return err + } + addTime, _ := time.ParseDuration(response.Header.Get(rfc.StoredTTLHeader)) responseCc, _ := cacheobject.ParseResponseCacheControl(rfc.HeaderAllCommaSepValuesString(response.Header, "Cache-Control")) @@ -780,10 +1013,7 @@ func (s *SouinBaseHandler) ServeHTTP(rw http.ResponseWriter, rq *http.Request, n code := fmt.Sprintf("; fwd-status=%d", statusCode) rfc.HitStaleCache(&response.Header) response.Header.Set("Cache-Status", response.Header.Get("Cache-Status")+code) - // Yaegi sucks, we can't use maps. - for k := range response.Header { - customWriter.Header().Set(k, response.Header.Get(k)) - } + maps.Copy(customWriter.Header(), response.Header) customWriter.WriteHeader(response.StatusCode) customWriter.handleBuffer(func(b *bytes.Buffer) { b.Reset() @@ -800,28 +1030,33 @@ func (s *SouinBaseHandler) ServeHTTP(rw http.ResponseWriter, rq *http.Request, n } errorCacheCh := make(chan error) + defer close(errorCacheCh) go func(vr *http.Request, cw *CustomWriter) { - errorCacheCh <- s.Upstream(cw, vr, next, requestCc, cachedKey) + defer func() { + if r := recover(); r != nil { + s.Configuration.GetLogger().Infof("recovered due to closed errorCacheCh chan, the request context has finished prematurely %s", req.URL) + } + }() + prometheus.Increment(prometheus.NoCachedResponseCounter) + errorCacheCh <- s.Upstream(cw, vr, next, requestCc, cachedKey, uri, false) }(req, customWriter) select { case <-req.Context().Done(): - switch req.Context().Err() { case baseCtx.DeadlineExceeded: - customWriter.WriteHeader(http.StatusGatewayTimeout) rw.Header().Set("Cache-Status", cacheName+"; fwd=bypass; detail=DEADLINE-EXCEEDED") + customWriter.Rw.WriteHeader(http.StatusGatewayTimeout) _, _ = customWriter.Rw.Write([]byte("Internal server error")) + s.Configuration.GetLogger().Infof("Internal server error on endpoint %s: %v", req.URL, s.Storers) return baseCtx.DeadlineExceeded case baseCtx.Canceled: return baseCtx.Canceled default: return nil } - case v := <-errorCacheCh: - switch v { case nil: _, _ = customWriter.Send() diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/middleware/writer.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/middleware/writer.go index 13208fad1..c4e836861 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/middleware/writer.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/middleware/writer.go @@ -5,8 +5,10 @@ import ( "fmt" "net/http" "strconv" + "strings" "sync" + "github.com/darkweak/go-esi/esi" "github.com/darkweak/souin/pkg/rfc" ) @@ -85,18 +87,134 @@ func (r *CustomWriter) Write(b []byte) (int, error) { return len(b), nil } +type rangeValue struct { + from, to int64 +} + +const separator = "--SOUIN-HTTP-CACHE-SEPARATOR" + +func parseRange(rangeHeaders []string, contentRange string) ([]rangeValue, rangeValue, int64) { + if len(rangeHeaders) == 0 { + return nil, rangeValue{}, -1 + } + + crv := rangeValue{from: 0, to: 0} + var total int64 = -1 + if contentRange != "" { + crVal := strings.Split(strings.TrimPrefix(contentRange, "bytes "), "/") + total, _ = strconv.ParseInt(crVal[1], 10, 64) + total-- + + crSplit := strings.Split(crVal[0], "-") + crv.from, _ = strconv.ParseInt(crSplit[0], 10, 64) + crv.to, _ = strconv.ParseInt(crSplit[1], 10, 64) + } + + values := make([]rangeValue, len(rangeHeaders)) + + for idx, header := range rangeHeaders { + ranges := strings.Split(header, "-") + rv := rangeValue{from: -1, to: total} + + // e.g. Range: -5 + if len(ranges) == 2 && ranges[0] == "" { + ranges[0] = "-" + ranges[1] + from, _ := strconv.ParseInt(ranges[0], 10, 64) + rv.from = total + from + + values[idx] = rv + + continue + } + + rv.from, _ = strconv.ParseInt(ranges[0], 10, 64) + + if ranges[1] != "" { + rv.to, _ = strconv.ParseInt(ranges[1], 10, 64) + rv.to++ + } + + values[idx] = rv + } + + return values, crv, total + 1 +} + // Send delays the response to handle Cache-Status func (r *CustomWriter) Send() (int, error) { - defer r.Buf.Reset() - fmt.Println("Upstream Send", r.Buf.Len()) + defer r.handleBuffer(func(b *bytes.Buffer) { + b.Reset() + }) storedLength := r.Header().Get(rfc.StoredLengthHeader) if storedLength != "" { r.Header().Set("Content-Length", storedLength) } - b := r.Buf.Bytes() - if len(b) != 0 { - r.Header().Set("Content-Length", strconv.Itoa(len(b))) + + result := r.Buf.Bytes() + + result = esi.Parse(result, r.Req) + + if r.Headers.Get("Range") != "" { + + var bufStr string + mimeType := r.Header().Get("Content-Type") + + r.WriteHeader(http.StatusPartialContent) + + rangeHeader, contentRangeValue, total := parseRange( + strings.Split(strings.TrimPrefix(r.Headers.Get("Range"), "bytes="), ", "), + r.Header().Get("Content-Range"), + ) + bodyBytes := r.Buf.Bytes() + bufLen := int64(r.Buf.Len()) + if total > 0 { + bufLen = total + } + + if len(rangeHeader) == 1 { + header := rangeHeader[0] + internalFrom := (header.from - contentRangeValue.from) % bufLen + internalTo := (header.to - contentRangeValue.from) % bufLen + + content := bodyBytes[internalFrom:] + + r.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", contentRangeValue.from, contentRangeValue.to, bufLen)) + + if internalTo >= 0 { + content = content[:internalTo-internalFrom] + r.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", header.from, header.to, bufLen)) + } + + result = content + } + + if len(rangeHeader) > 1 { + r.Header().Set("Content-Type", "multipart/byteranges; boundary="+separator) + + for _, header := range rangeHeader { + + content := bodyBytes[header.from:] + if header.to >= 0 { + content = content[:header.to-header.from] + } + + bufStr += fmt.Sprintf(` +%s +Content-Type: %s +Content-Range: bytes %d-%d/%d + +%s +`, separator, mimeType, header.from, header.to, r.Buf.Len(), content) + } + + result = []byte(bufStr + separator + "--") + } } + + if len(result) != 0 { + r.Header().Set("Content-Length", strconv.Itoa(len(result))) + } + r.Header().Del(rfc.StoredLengthHeader) r.Header().Del(rfc.StoredTTLHeader) @@ -105,5 +223,5 @@ func (r *CustomWriter) Send() (int, error) { r.headersSent = true } - return r.Rw.Write(b) + return r.Rw.Write(result) } diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/rfc/revalidation.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/rfc/revalidation.go index ccb6e73be..c2d63e4aa 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/rfc/revalidation.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/rfc/revalidation.go @@ -1,66 +1,31 @@ package rfc import ( - "bufio" - "bytes" - "encoding/json" "net/http" "strings" "time" - "github.com/darkweak/souin/pkg/storage/types" + "github.com/darkweak/storages/core" ) -func ValidateETagFromHeader(etag string, validator *types.Revalidator) { - validator.ResponseETag = etag - validator.NeedRevalidation = validator.NeedRevalidation || validator.ResponseETag != "" - validator.Matched = validator.ResponseETag == "" || (validator.ResponseETag != "" && len(validator.RequestETags) == 0) - - if len(validator.RequestETags) == 0 { - validator.NotModified = false - return - } - - // If-None-Match - if validator.IfNoneMatchPresent { - for _, ifNoneMatch := range validator.IfNoneMatch { - // Asrterisk special char to match any of ETag - if ifNoneMatch == "*" { - validator.Matched = true - return - } - if ifNoneMatch == validator.ResponseETag { - validator.Matched = true - return - } - } - - validator.Matched = false - return - } - - // If-Match - if validator.IfMatchPresent { - validator.Matched = false - if validator.ResponseETag == "" { - return - } - - for _, ifMatch := range validator.IfMatch { - // Asrterisk special char to match any of ETag - if ifMatch == "*" { - validator.Matched = true - return - } - if ifMatch == validator.ResponseETag { - validator.Matched = true - return - } - } - } +type Revalidator struct { + Matched bool + IfNoneMatchPresent bool + IfMatchPresent bool + IfModifiedSincePresent bool + IfUnmodifiedSincePresent bool + IfUnmotModifiedSincePresent bool + NeedRevalidation bool + NotModified bool + IfModifiedSince time.Time + IfUnmodifiedSince time.Time + IfNoneMatch []string + IfMatch []string + RequestETags []string + ResponseETag string } -func ParseRequest(req *http.Request) *types.Revalidator { +func ParseRequest(req *http.Request) *core.Revalidator { var rqEtags []string if len(req.Header.Get("If-None-Match")) > 0 { rqEtags = strings.Split(req.Header.Get("If-None-Match"), ",") @@ -68,7 +33,7 @@ func ParseRequest(req *http.Request) *types.Revalidator { for i, tag := range rqEtags { rqEtags[i] = strings.Trim(tag, " ") } - validator := types.Revalidator{ + validator := core.Revalidator{ NotModified: len(rqEtags) > 0, RequestETags: rqEtags, } @@ -94,116 +59,3 @@ func ParseRequest(req *http.Request) *types.Revalidator { return &validator } - -func DecodeMapping(item []byte) (*StorageMapper, error) { - mapping := &StorageMapper{} - e := json.Unmarshal(item, mapping) - - return mapping, e -} - -func MappingElection(provider types.Storer, item []byte, req *http.Request, validator *types.Revalidator) (resultFresh *http.Response, resultStale *http.Response, e error) { - mapping := &StorageMapper{} - - if len(item) != 0 { - mapping, e = DecodeMapping(item) - if e != nil { - return resultFresh, resultStale, e - } - } - - for keyName, keyItem := range mapping.Mapping { - valid := true - - for hname, hval := range keyItem.VariedHeaders { - if req.Header.Get(hname) != strings.Join(hval, ", ") { - valid = false - - break - } - } - - if !valid { - continue - } - - ValidateETagFromHeader(keyItem.Etag, validator) - - if validator.Matched { - // If the key is fresh enough. - if time.Since(keyItem.FreshTime) < 0 { - response := provider.Get(keyName) - if response != nil { - if resultFresh, e = http.ReadResponse(bufio.NewReader(bytes.NewBuffer(response)), req); e != nil { - return resultFresh, resultStale, e - } - - return resultFresh, resultStale, e - } - } - - // If the key is still stale. - if time.Since(keyItem.StaleTime) < 0 { - response := provider.Get(keyName) - if response != nil { - if resultStale, e = http.ReadResponse(bufio.NewReader(bytes.NewBuffer(response)), req); e != nil { - return resultFresh, resultStale, e - } - } - } - } - } - - return resultFresh, resultStale, e -} - -type KeyIndex struct { - StoredAt time.Time `json:"stored_at,omitempty"` - FreshTime time.Time `json:"fresh_time,omitempty"` - StaleTime time.Time `json:"stale_time,omitempty"` - VariedHeaders map[string][]string `json:"varied_headers,omitempty"` - Etag string `json:"etag,omitempty"` - RealKey string `json:"real_key,omitempty"` -} -type StorageMapper struct { - Mapping map[string]*KeyIndex `json:"mapping,omitempty"` -} - -func MappingUpdater(key string, item []byte, now, freshTime, staleTime time.Time, variedHeaders http.Header, etag, realKey string) (val []byte, e error) { - mapping := &StorageMapper{} - if len(item) != 0 { - e = json.Unmarshal(item, mapping) - if e != nil { - return nil, e - } - } - - if mapping.Mapping == nil { - mapping.Mapping = make(map[string]*KeyIndex) - } - - var pbvariedeheader map[string][]string - if variedHeaders != nil { - pbvariedeheader = make(map[string][]string) - } - - for k, v := range variedHeaders { - pbvariedeheader[k] = append(pbvariedeheader[k], v...) - } - - mapping.Mapping[key] = &KeyIndex{ - StoredAt: now, - FreshTime: freshTime, - StaleTime: staleTime, - VariedHeaders: pbvariedeheader, - Etag: etag, - RealKey: realKey, - } - - val, e = json.Marshal(mapping) - if e != nil { - return nil, e - } - - return val, e -} diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/rfc/vary.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/rfc/vary.go index bc958f40d..d1c0e157e 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/rfc/vary.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/rfc/vary.go @@ -6,6 +6,8 @@ import ( "net/url" "slices" "strings" + + "github.com/darkweak/storages/core" ) const ( @@ -15,10 +17,14 @@ const ( // GetVariedCacheKey returns the varied cache key for req and resp. func GetVariedCacheKey(rq *http.Request, headers []string) string { - if len(headers) == 0 { + isVaryDisabled := rq.Context().Value(core.DISABLE_VARY_CTX) + if isVaryDisabled != nil && isVaryDisabled.(bool) { return "" } + if len(headers) == 0 { + return "" + } for i, v := range headers { h := strings.TrimSpace(rq.Header.Get(v)) if strings.Contains(h, ";") || strings.Contains(h, ":") { diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/storage/abstractProvider.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/storage/abstractProvider.go deleted file mode 100644 index 577d5fade..000000000 --- a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/storage/abstractProvider.go +++ /dev/null @@ -1,57 +0,0 @@ -package storage - -import ( - "net/http" - "net/url" - "strings" - - "github.com/darkweak/souin/configurationtypes" - "github.com/darkweak/souin/pkg/storage/types" -) - -const ( - VarySeparator = "{-VARY-}" - DecodedHeaderSeparator = ";" - encodedHeaderSemiColonSeparator = "%3B" - encodedHeaderColonSeparator = "%3A" - StalePrefix = "STALE_" -) - -type StorerInstanciator func(configurationtypes.AbstractConfigurationInterface) (types.Storer, error) - -func NewStorages(configuration configurationtypes.AbstractConfigurationInterface) ([]types.Storer, error) { - s, err := Factory(configuration) - return []types.Storer{s}, err -} - -func varyVoter(baseKey string, req *http.Request, currentKey string) bool { - if currentKey == baseKey { - return true - } - - if strings.Contains(currentKey, VarySeparator) && strings.HasPrefix(currentKey, baseKey+VarySeparator) { - list := currentKey[(strings.LastIndex(currentKey, VarySeparator) + len(VarySeparator)):] - if len(list) == 0 { - return false - } - - for _, item := range strings.Split(list, ";") { - index := strings.LastIndex(item, ":") - if len(item) < index+1 { - return false - } - - hVal := item[index+1:] - if strings.Contains(hVal, encodedHeaderSemiColonSeparator) || strings.Contains(hVal, encodedHeaderColonSeparator) { - hVal, _ = url.QueryUnescape(hVal) - } - if req.Header.Get(item[:index]) != hVal { - return false - } - } - - return true - } - - return false -} diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/storage/cacheProvider.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/storage/cacheProvider.go deleted file mode 100644 index f0ff8847e..000000000 --- a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/storage/cacheProvider.go +++ /dev/null @@ -1,178 +0,0 @@ -package storage - -import ( - "bufio" - "bytes" - "net/http" - "regexp" - "strings" - "time" - - "github.com/akyoto/cache" - t "github.com/darkweak/souin/configurationtypes" - "github.com/darkweak/souin/pkg/rfc" - "github.com/darkweak/souin/pkg/storage/types" -) - -// Cache provider type -type Cache struct { - *cache.Cache - stale time.Duration -} - -var sharedCache *Cache - -// Factory function create new Cache instance -func Factory(c t.AbstractConfigurationInterface) (types.Storer, error) { - provider := cache.New(1 * time.Second) - - if sharedCache == nil { - sharedCache = &Cache{Cache: provider, stale: c.GetDefaultCache().GetStale()} - } - - return sharedCache, nil -} - -// Name returns the storer name -func (provider *Cache) Name() string { - return "CACHE" -} - -// Uuid returns an unique identifier -func (provider *Cache) Uuid() string { - return "" -} - -// ListKeys method returns the list of existing keys -func (provider *Cache) ListKeys() []string { - var keys []string - provider.Range(func(key, _ interface{}) bool { - keys = append(keys, key.(string)) - return true - }) - - return keys -} - -// MapKeys method returns the map of existing keys -func (provider *Cache) MapKeys(prefix string) map[string]string { - keys := map[string]string{} - provider.Range(func(key, value interface{}) bool { - if strings.HasPrefix(key.(string), prefix) { - k, _ := strings.CutPrefix(key.(string), prefix) - keys[k] = string(value.([]byte)) - } - return true - }) - - return keys -} - -// Get method returns the populated response if exists, empty response then -func (provider *Cache) Get(key string) []byte { - result, found := provider.Cache.Get(key) - - if !found { - return []byte{} - } - - return result.([]byte) -} - -// GetMultiLevel tries to load the key and check if one of linked keys is a fresh/stale candidate. -func (provider *Cache) GetMultiLevel(key string, req *http.Request, validator *types.Revalidator) (fresh *http.Response, stale *http.Response) { - result, found := provider.Cache.Get("IDX_" + key) - if !found { - return - } - - fresh, stale, _ = rfc.MappingElection(provider, result.([]byte), req, validator) - - return -} - -// SetMultiLevel tries to store the key with the given value and update the mapping key to store metadata. -func (provider *Cache) SetMultiLevel(baseKey, variedKey string, value []byte, variedHeaders http.Header, etag string, duration time.Duration, realKey string) error { - now := time.Now() - - var e error - - provider.Cache.Set(variedKey, value, duration) - - mappingKey := "IDX_" + baseKey - item, ok := provider.Cache.Get(mappingKey) - var val []byte - if ok { - val = item.([]byte) - } - - val, e = rfc.MappingUpdater(variedKey, val, now, now.Add(duration), now.Add(duration+provider.stale), variedHeaders, etag, realKey) - if e != nil { - return e - } - - provider.Cache.Set(mappingKey, val, 0) - return nil -} - -// Prefix method returns the populated response if exists, empty response then -func (provider *Cache) Prefix(key string, req *http.Request, validator *types.Revalidator) *http.Response { - var result *http.Response - - provider.Range(func(k, v interface{}) bool { - if !strings.HasPrefix(k.(string), key) { - return true - } - - if k == key || varyVoter(key, req, k.(string)) { - if res, err := http.ReadResponse(bufio.NewReader(bytes.NewBuffer(v.([]byte))), req); err == nil { - rfc.ValidateETagFromHeader(res.Header.Get("etag"), validator) - if validator.Matched { - result = res - return false - } - } - return true - } - - return true - }) - - return result -} - -// Set method will store the response in Cache provider -func (provider *Cache) Set(key string, value []byte, duration time.Duration) error { - provider.Cache.Set(key, value, duration) - - return nil -} - -// Delete method will delete the response in Cache provider if exists corresponding to key param -func (provider *Cache) Delete(key string) { - provider.Cache.Delete(key) -} - -// DeleteMany method will delete the responses in Cache provider if exists corresponding to the regex key param -func (provider *Cache) DeleteMany(key string) { - re, _ := regexp.Compile(key) - - provider.Range(func(current, _ any) bool { - if (re != nil && re.MatchString(current.(string))) || strings.HasPrefix(current.(string), key) { - provider.Delete(current.(string)) - } - return true - }) -} - -// Init method will -func (provider *Cache) Init() error { - return nil -} - -// Reset method will reset or close provider -func (provider *Cache) Reset() error { - provider.DeleteMany("*") - - return nil -} diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/storage/defaultProvider.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/storage/defaultProvider.go new file mode 100644 index 000000000..c6abe23cc --- /dev/null +++ b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/storage/defaultProvider.go @@ -0,0 +1,213 @@ +package storage + +import ( + "bytes" + "net/http" + "regexp" + "strings" + "sync" + "time" + + "github.com/darkweak/souin/configurationtypes" + "github.com/darkweak/souin/pkg/storage/types" + "github.com/darkweak/storages/core" + "github.com/pierrec/lz4/v4" +) + +// Default provider type +type Default struct { + m *sync.Map + stale time.Duration + logger core.Logger + + mu sync.Mutex +} + +type item struct { + invalidAt time.Time + value []byte +} + +// Factory function create new Default instance +func Factory(c configurationtypes.AbstractConfigurationInterface) (types.Storer, error) { + return &Default{m: &sync.Map{}, logger: c.GetLogger(), stale: c.GetDefaultCache().GetStale()}, nil +} + +// Name returns the storer name +func (provider *Default) Name() string { + return types.DefaultStorageName +} + +// Uuid returns an unique identifier +func (provider *Default) Uuid() string { + return "" +} + +// MapKeys method returns a map with the key and value +func (provider *Default) MapKeys(prefix string) map[string]string { + provider.mu.Lock() + defer provider.mu.Unlock() + + now := time.Now() + keys := map[string]string{} + + provider.m.Range(func(key, value any) bool { + if strings.HasPrefix(key.(string), prefix) { + k, _ := strings.CutPrefix(key.(string), prefix) + if v, ok := value.(item); ok { + if v.invalidAt.After(now) { + keys[k] = string(v.value) + } + + return true + } + + if v, ok := value.(*core.StorageMapper); ok { + for _, v := range v.Mapping { + if v.StaleTime.AsTime().After(now) { + keys[v.RealKey] = string(provider.Get(v.RealKey)) + } + } + } + } + + return true + }) + + return keys +} + +// ListKeys method returns the list of existing keys +func (provider *Default) ListKeys() []string { + now := time.Now() + keys := []string{} + + provider.m.Range(func(key, value any) bool { + if strings.HasPrefix(key.(string), core.MappingKeyPrefix) { + mapping, err := core.DecodeMapping(value.([]byte)) + if err == nil { + for _, v := range mapping.Mapping { + if v.StaleTime.AsTime().After(now) { + keys = append(keys, v.RealKey) + } else { + provider.m.Delete(v.RealKey) + } + } + } + } + + return true + }) + + return keys +} + +// Get method returns the populated response if exists, empty response then +func (provider *Default) Get(key string) []byte { + result, ok := provider.m.Load(key) + if !ok || result == nil { + return nil + } + + res, ok := result.(item) + if !ok { + return nil + } + + if res.invalidAt.After(time.Now()) { + return res.value + } + + return nil +} + +// GetMultiLevel tries to load the key and check if one of linked keys is a fresh/stale candidate. +func (provider *Default) GetMultiLevel(key string, req *http.Request, validator *core.Revalidator) (fresh *http.Response, stale *http.Response) { + result, found := provider.m.Load(core.MappingKeyPrefix + key) + if !found { + return + } + + fresh, stale, _ = core.MappingElection(provider, result.([]byte), req, validator, provider.logger) + + return +} + +// SetMultiLevel tries to store the key with the given value and update the mapping key to store metadata. +func (provider *Default) SetMultiLevel(baseKey, variedKey string, value []byte, variedHeaders http.Header, etag string, duration time.Duration, realKey string) error { + now := time.Now() + + var e error + compressed := new(bytes.Buffer) + if _, e = lz4.NewWriter(compressed).ReadFrom(bytes.NewReader(value)); e != nil { + provider.logger.Errorf("Impossible to compress the key %s into Badger, %v", variedKey, e) + return e + } + + provider.m.Store(variedKey, item{ + invalidAt: now.Add(duration + provider.stale), + value: compressed.Bytes(), + }) + + mappingKey := core.MappingKeyPrefix + baseKey + item, ok := provider.m.Load(mappingKey) + var val []byte + if ok { + val = item.([]byte) + } + + val, e = core.MappingUpdater(variedKey, val, provider.logger, now, now.Add(duration), now.Add(duration+provider.stale), variedHeaders, etag, realKey) + if e != nil { + return e + } + + provider.logger.Debugf("Store the new mapping for the key %s in Default", variedKey) + provider.m.Store(mappingKey, val) + return nil +} + +// Set method will store the response in Badger provider +func (provider *Default) Set(key string, value []byte, duration time.Duration) error { + provider.m.Store(key, item{ + invalidAt: time.Now().Add(duration), + value: value, + }) + + return nil +} + +// Delete method will delete the response in Badger provider if exists corresponding to key param +func (provider *Default) Delete(key string) { + provider.m.Delete(key) +} + +// DeleteMany method will delete the responses in Badger provider if exists corresponding to the regex key param +func (provider *Default) DeleteMany(key string) { + re, e := regexp.Compile(key) + + if e != nil { + provider.logger.Infof("Failed to compile key %s, %v", key, e) + } + + provider.m.Range(func(current, _ any) bool { + if (re != nil && re.MatchString(current.(string))) || strings.HasPrefix(current.(string), key) { + provider.m.Delete(current) + } + + return true + }) +} + +// Init method will +func (provider *Default) Init() error { + return nil +} + +// Reset method will reset or close provider +func (provider *Default) Reset() error { + provider.mu.Lock() + provider.m = new(sync.Map) + provider.mu.Unlock() + + return nil +} diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/storage/types/types.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/storage/types/types.go index e41007745..e4c8603fc 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/storage/types/types.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/storage/types/types.go @@ -3,26 +3,11 @@ package types import ( "net/http" "time" -) -type Revalidator struct { - Matched bool - IfNoneMatchPresent bool - IfMatchPresent bool - IfModifiedSincePresent bool - IfUnmodifiedSincePresent bool - IfUnmotModifiedSincePresent bool - NeedRevalidation bool - NotModified bool - IfModifiedSince time.Time - IfUnmodifiedSince time.Time - IfNoneMatch []string - IfMatch []string - RequestETags []string - ResponseETag string -} + "github.com/darkweak/storages/core" +) -const DefaultStorageName = "CACHE" +const DefaultStorageName = "DEFAULT" const OneYearDuration = 365 * 24 * time.Hour type Storer interface { @@ -38,6 +23,6 @@ type Storer interface { Reset() error // Multi level storer to handle fresh/stale at once - GetMultiLevel(key string, req *http.Request, validator *Revalidator) (fresh *http.Response, stale *http.Response) + GetMultiLevel(key string, req *http.Request, validator *core.Revalidator) (fresh *http.Response, stale *http.Response) SetMultiLevel(baseKey, variedKey string, value []byte, variedHeaders http.Header, etag string, duration time.Duration, realKey string) error } diff --git a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/common.go b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/common.go index 1fb560034..6a82dd04e 100644 --- a/plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/common.go +++ b/plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/common.go @@ -1,7 +1,6 @@ package providers import ( - "fmt" "net/http" "net/url" "regexp" @@ -10,8 +9,8 @@ import ( "time" "github.com/darkweak/souin/configurationtypes" - "github.com/darkweak/souin/pkg/storage" "github.com/darkweak/souin/pkg/storage/types" + "github.com/darkweak/storages/core" ) const ( @@ -28,16 +27,38 @@ const ( cacheTags = "Cache-Tags" cacheTag = "Cache-Tag" - stalePrefix = "STALE_" surrogatePrefix = "SURROGATE_" ) var storageToInfiniteTTLMap = map[string]time.Duration{ - "CACHE": 365 * 24 * time.Hour, + "BADGER": types.OneYearDuration, + "ETCD": types.OneYearDuration, + "NUTS": 0, + "OLRIC": types.OneYearDuration, + "OTTER": types.OneYearDuration, + "REDIS": -1, + "SIMPLEFS": 0, + types.DefaultStorageName: types.OneYearDuration, } func (s *baseStorage) ParseHeaders(value string) []string { - return regexp.MustCompile(s.parent.getHeaderSeparator()+" *").Split(value, -1) + res := strings.Split(value, s.parent.getHeaderSeparator()) + for i, v := range res { + res[i] = strings.TrimSpace(v) + } + + return res +} + +func getCandidateHeader(header http.Header, getCandidates func() []string) (string, string) { + candidates := getCandidates() + for _, candidate := range candidates { + if h := header.Get(candidate); h != "" { + return candidate, h + } + } + + return candidates[len(candidates)-1], "" } func isSafeHTTPMethod(method string) bool { @@ -59,8 +80,11 @@ func uniqueTag(values []string) []string { } if _, found := tmp[item]; !found { tmp[item] = true - i, _ := url.QueryUnescape(item) - list = append(list, i) + + if strings.Contains(item, "%") { + item, _ = url.QueryUnescape(item) + } + list = append(list, item) } } @@ -79,17 +103,35 @@ type baseStorage struct { keysRegexp map[string]keysRegexpInner dynamic bool keepStale bool + logger core.Logger mu sync.Mutex duration time.Duration } -func (s *baseStorage) init(config configurationtypes.AbstractConfigurationInterface, _ string) { - storers, err := storage.NewStorages(config) - if err != nil { - panic(fmt.Sprintf("Impossible to instanciate the storer for the surrogate-keys: %v", err)) - } +func (s *baseStorage) init(config configurationtypes.AbstractConfigurationInterface, defaultStorerName string) { + if configuration, ok := config.GetSurrogateKeys()["_configuration"]; ok { + storer := core.GetRegisteredStorer(configuration.Storer) + if storer == nil { + storer = core.GetRegisteredStorer(types.DefaultStorageName + "-") + if storer == nil { + config.GetLogger().Errorf("Impossible to retrieve the storers %s for the surrogate-keys from it's configuration", configuration.Storer) + } + } + + s.Storage = storer + } else { + config.GetLogger().Debugf("Try to load the storer %s as surrogate backend", defaultStorerName) + storer := core.GetRegisteredStorer(defaultStorerName) + if storer == nil { + config.GetLogger().Errorf("Impossible to retrieve the storers %s for the surrogate-keys fallback to the default storage", configuration.Storer) + storer = core.GetRegisteredStorer(types.DefaultStorageName + "-") + if storer == nil { + config.GetLogger().Error("Impossible to retrieve the default storer") + } + } - s.Storage = storers[0] + s.Storage = storer + } s.Keys = config.GetSurrogateKeys() s.keepStale = config.GetDefaultCache().GetCDN().Strategy != "hard" @@ -115,8 +157,8 @@ func (s *baseStorage) init(config configurationtypes.AbstractConfigurationInterf } s.dynamic = config.GetDefaultCache().GetCDN().Dynamic + s.logger = config.GetLogger() s.keysRegexp = keysRegexp - s.mu = sync.Mutex{} s.duration = storageToInfiniteTTLMap[s.Storage.Name()] } @@ -125,8 +167,8 @@ func (s *baseStorage) storeTag(tag string, cacheKey string, re *regexp.Regexp) { s.mu.Lock() currentValue := string(s.Storage.Get(surrogatePrefix + tag)) if !re.MatchString(currentValue) { - fmt.Printf("Store the tag %s", tag) - _ = s.Storage.Set(surrogatePrefix+tag, []byte(currentValue+souinStorageSeparator+cacheKey), -1) + s.logger.Debugf("Store the tag %s", tag) + _ = s.Storage.Set(surrogatePrefix+tag, []byte(currentValue+souinStorageSeparator+cacheKey), s.duration) } } @@ -136,6 +178,7 @@ func (*baseStorage) candidateStore(tag string) bool { func (*baseStorage) getOrderedSurrogateKeyHeadersCandidate() []string { return []string{ + cacheGroupKey, surrogateKey, edgeCacheTag, cacheTags, @@ -152,14 +195,7 @@ func (*baseStorage) getOrderedSurrogateControlHeadersCandidate() []string { } func (s *baseStorage) GetSurrogateControl(header http.Header) (string, string) { - parent := s.parent.getOrderedSurrogateControlHeadersCandidate() - for _, candidate := range parent { - if h := header.Get(candidate); h != "" { - return candidate, h - } - } - - return parent[len(parent)-1], "" + return getCandidateHeader(header, s.parent.getOrderedSurrogateControlHeadersCandidate) } func (s *baseStorage) GetSurrogateControlName() string { @@ -167,23 +203,15 @@ func (s *baseStorage) GetSurrogateControlName() string { } func (s *baseStorage) getSurrogateKey(header http.Header) string { - for _, candidate := range s.parent.getOrderedSurrogateKeyHeadersCandidate() { - if h := header.Get(candidate); h != "" { - return h - } - } - - return "" + _, v := getCandidateHeader(header, s.parent.getOrderedSurrogateKeyHeadersCandidate) + return v } func (s *baseStorage) purgeTag(tag string) []string { - toInvalidate := string(s.Storage.Get(tag)) - fmt.Printf("Purge the tag %s", tag) - s.Storage.Delete(surrogatePrefix + tag) + toInvalidate := string(s.Storage.Get(surrogatePrefix + tag)) + s.logger.Debugf("Purge the tag %s", tag) if !s.keepStale { - toInvalidate = toInvalidate + "," + string(s.Storage.Get(stalePrefix+tag)) - fmt.Printf("Purge the tag %s", stalePrefix+tag) - s.Storage.Delete(surrogatePrefix + stalePrefix + tag) + s.Storage.Delete(surrogatePrefix + tag) } return strings.Split(toInvalidate, souinStorageSeparator) } @@ -193,11 +221,8 @@ func (s *baseStorage) Store(response *http.Response, cacheKey, uri string) error h := response.Header cacheKey = url.QueryEscape(cacheKey) - staleKey := stalePrefix + cacheKey urlRegexp := regexp.MustCompile("(^|" + regexp.QuoteMeta(souinStorageSeparator) + ")" + regexp.QuoteMeta(cacheKey) + "(" + regexp.QuoteMeta(souinStorageSeparator) + "|$)") - staleUrlRegexp := regexp.MustCompile("(^|" + regexp.QuoteMeta(souinStorageSeparator) + ")" + regexp.QuoteMeta(staleKey) + "(" + regexp.QuoteMeta(souinStorageSeparator) + "|$)") - keys := s.ParseHeaders(s.parent.getSurrogateKey(h)) for _, key := range keys { @@ -205,19 +230,21 @@ func (s *baseStorage) Store(response *http.Response, cacheKey, uri string) error if controls := s.ParseHeaders(v); len(controls) != 0 { if len(controls) == 1 && controls[0] == "" { s.storeTag(key, cacheKey, urlRegexp) - s.storeTag(stalePrefix+key, staleKey, staleUrlRegexp) + s.storeTag(uri, cacheKey, urlRegexp) continue } for _, control := range controls { if s.parent.candidateStore(control) { s.storeTag(key, cacheKey, urlRegexp) - s.storeTag(stalePrefix+key, staleKey, staleUrlRegexp) + s.storeTag(uri, cacheKey, urlRegexp) + + break } } } else { s.storeTag(key, cacheKey, urlRegexp) - s.storeTag(stalePrefix+key, staleKey, staleUrlRegexp) + s.storeTag(uri, cacheKey, urlRegexp) } } @@ -233,6 +260,8 @@ func (s *baseStorage) Purge(header http.Header) (cacheKeys []string, surrogateKe toInvalidate = append(toInvalidate, s.purgeTag(su)...) } + s.logger.Debugf("Purge the following tags: %+v", toInvalidate) + return uniqueTag(toInvalidate), surrogates } @@ -252,7 +281,5 @@ func (s *baseStorage) List() map[string]string { // Destruct method will shutdown properly the provider func (s *baseStorage) Destruct() error { - s.Storage.DeleteMany(surrogatePrefix + ".*") - - return nil + return s.Storage.Reset() } diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/.gitignore b/plugins/traefik/vendor/github.com/francoispqt/gojay/.gitignore deleted file mode 100644 index 43ebdc4b9..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -vendor -*.out -*.log -*.test -.vscode diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/.travis.yml b/plugins/traefik/vendor/github.com/francoispqt/gojay/.travis.yml deleted file mode 100644 index df04aa240..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: go - -go: - - "1.10.x" - - "1.11.x" - - "1.12.x" - -script: - - go get github.com/golang/dep/cmd/dep github.com/stretchr/testify - - dep ensure -v -vendor-only - - go test ./gojay/codegen/test/... -race - - go test -race -coverprofile=coverage.txt -covermode=atomic - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/Gopkg.lock b/plugins/traefik/vendor/github.com/francoispqt/gojay/Gopkg.lock deleted file mode 100644 index d642e9a75..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/Gopkg.lock +++ /dev/null @@ -1,163 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - digest = "1:1a37f9f2ae10d161d9688fb6008ffa14e1631e5068cc3e9698008b9e8d40d575" - name = "cloud.google.com/go" - packages = ["compute/metadata"] - pruneopts = "" - revision = "457ea5c15ccf3b87db582c450e80101989da35f7" - version = "v0.40.0" - -[[projects]] - digest = "1:968d8903d598e3fae738325d3410f33f07ea6a2b9ee5591e9c262ee37df6845a" - name = "github.com/go-errors/errors" - packages = ["."] - pruneopts = "" - revision = "a6af135bd4e28680facf08a3d206b454abc877a4" - version = "v1.0.1" - -[[projects]] - digest = "1:529d738b7976c3848cae5cf3a8036440166835e389c1f617af701eeb12a0518d" - name = "github.com/golang/protobuf" - packages = ["proto"] - pruneopts = "" - revision = "b5d812f8a3706043e23a9cd5babf2e5423744d30" - version = "v1.3.1" - -[[projects]] - branch = "master" - digest = "1:cae59d7b8243c671c9f544965522ba35c0fec48ee80adb9f1400cd2f33abbbec" - name = "github.com/mailru/easyjson" - packages = [ - ".", - "buffer", - "jlexer", - "jwriter", - ] - pruneopts = "" - revision = "1ea4449da9834f4d333f1cc461c374aea217d249" - -[[projects]] - digest = "1:1d7e1867c49a6dd9856598ef7c3123604ea3daabf5b83f303ff457bcbc410b1d" - name = "github.com/pkg/errors" - packages = ["."] - pruneopts = "" - revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4" - version = "v0.8.1" - -[[projects]] - digest = "1:8d4bbd8ab012efc77ab6b97286f2aff262bcdeac9803bb57d75cf7d0a5e6a877" - name = "github.com/viant/assertly" - packages = ["."] - pruneopts = "" - revision = "04f45e0aeb6f3455884877b047a97bcc95dc9493" - version = "v0.4.8" - -[[projects]] - digest = "1:5913451bc2d274673c0716efe226a137625740cd9380641f4d8300ff4f2d82a0" - name = "github.com/viant/toolbox" - packages = [ - ".", - "cred", - "data", - "storage", - "url", - ] - pruneopts = "" - revision = "1be8e4d172138324f40d55ea61a2aeab0c5ce864" - version = "v0.24.0" - -[[projects]] - branch = "master" - digest = "1:9d150270ca2c3356f2224a0878daa1652e4d0b25b345f18b4f6e156cc4b8ec5e" - name = "golang.org/x/crypto" - packages = [ - "blowfish", - "curve25519", - "ed25519", - "ed25519/internal/edwards25519", - "internal/chacha20", - "internal/subtle", - "poly1305", - "ssh", - ] - pruneopts = "" - revision = "f99c8df09eb5bff426315721bfa5f16a99cad32c" - -[[projects]] - branch = "master" - digest = "1:5a56f211e7c12a65c5585c629457a2fb91d8719844ee8fab92727ea8adb5721c" - name = "golang.org/x/net" - packages = [ - "context", - "context/ctxhttp", - "websocket", - ] - pruneopts = "" - revision = "461777fb6f67e8cb9d70cda16573678d085a74cf" - -[[projects]] - branch = "master" - digest = "1:01bdbbc604dcd5afb6f66a717f69ad45e9643c72d5bc11678d44ffa5c50f9e42" - name = "golang.org/x/oauth2" - packages = [ - ".", - "google", - "internal", - "jws", - "jwt", - ] - pruneopts = "" - revision = "0f29369cfe4552d0e4bcddc57cc75f4d7e672a33" - -[[projects]] - branch = "master" - digest = "1:8ddb956f67d4c176abbbc42b7514aaeaf9ea30daa24e27d2cf30ad82f9334a2c" - name = "golang.org/x/sys" - packages = ["cpu"] - pruneopts = "" - revision = "1e42afee0f762ed3d76e6dd942e4181855fd1849" - -[[projects]] - digest = "1:47f391ee443f578f01168347818cb234ed819521e49e4d2c8dd2fb80d48ee41a" - name = "google.golang.org/appengine" - packages = [ - ".", - "internal", - "internal/app_identity", - "internal/base", - "internal/datastore", - "internal/log", - "internal/modules", - "internal/remote_api", - "internal/urlfetch", - "urlfetch", - ] - pruneopts = "" - revision = "b2f4a3cf3c67576a2ee09e1fe62656a5086ce880" - version = "v1.6.1" - -[[projects]] - digest = "1:cedccf16b71e86db87a24f8d4c70b0a855872eb967cb906a66b95de56aefbd0d" - name = "gopkg.in/yaml.v2" - packages = ["."] - pruneopts = "" - revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" - version = "v2.2.2" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/go-errors/errors", - "github.com/mailru/easyjson", - "github.com/mailru/easyjson/jlexer", - "github.com/mailru/easyjson/jwriter", - "github.com/viant/assertly", - "github.com/viant/toolbox", - "github.com/viant/toolbox/url", - "golang.org/x/net/websocket", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/Gopkg.toml b/plugins/traefik/vendor/github.com/francoispqt/gojay/Gopkg.toml deleted file mode 100644 index fa607923a..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/Gopkg.toml +++ /dev/null @@ -1,23 +0,0 @@ -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" - - -ignored = ["github.com/francoispqt/benchmarks*","github.com/stretchr/testify*","github.com/stretchr/testify","github.com/json-iterator/go","github.com/buger/jsonparser"] diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/LICENSE b/plugins/traefik/vendor/github.com/francoispqt/gojay/LICENSE deleted file mode 100644 index df215964e..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016 gojay - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/Makefile b/plugins/traefik/vendor/github.com/francoispqt/gojay/Makefile deleted file mode 100644 index ce9572391..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -.PHONY: test -test: - go test -race -run=^Test -v - -.PHONY: cover -cover: - go test -coverprofile=coverage.out -covermode=atomic - -.PHONY: coverhtml -coverhtml: - go tool cover -html=coverage.out \ No newline at end of file diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/README.md b/plugins/traefik/vendor/github.com/francoispqt/gojay/README.md deleted file mode 100644 index b2abd291d..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/README.md +++ /dev/null @@ -1,855 +0,0 @@ -[![Build Status](https://travis-ci.org/francoispqt/gojay.svg?branch=master)](https://travis-ci.org/francoispqt/gojay) -[![codecov](https://codecov.io/gh/francoispqt/gojay/branch/master/graph/badge.svg)](https://codecov.io/gh/francoispqt/gojay) -[![Go Report Card](https://goreportcard.com/badge/github.com/francoispqt/gojay)](https://goreportcard.com/report/github.com/francoispqt/gojay) -[![Go doc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square -)](https://godoc.org/github.com/francoispqt/gojay) -![MIT License](https://img.shields.io/badge/license-mit-blue.svg?style=flat-square) -[![Sourcegraph](https://sourcegraph.com/github.com/francoispqt/gojay/-/badge.svg)](https://sourcegraph.com/github.com/francoispqt/gojay) -![stability-stable](https://img.shields.io/badge/stability-stable-green.svg) - -# GoJay - - - -GoJay is a performant JSON encoder/decoder for Golang (currently the most performant, [see benchmarks](#benchmark-results)). - -It has a simple API and doesn't use reflection. It relies on small interfaces to decode/encode structures and slices. - -Gojay also comes with powerful stream decoding features and an even faster [Unsafe](#unsafe-api) API. - -There is also a [code generation tool](https://github.com/francoispqt/gojay/tree/master/gojay) to make usage easier and faster. - -# Why another JSON parser? - -I looked at other fast decoder/encoder and realised it was mostly hardly readable static code generation or a lot of reflection, poor streaming features, and not so fast in the end. - -Also, I wanted to build a decoder that could consume an io.Reader of line or comma delimited JSON, in a JIT way. To consume a flow of JSON objects from a TCP connection for example or from a standard output. Same way I wanted to build an encoder that could encode a flow of data to a io.Writer. - -This is how GoJay aims to be a very fast, JIT stream parser with 0 reflection, low allocation with a friendly API. - -# Get started - -```bash -go get github.com/francoispqt/gojay -``` - -* [Encoder](#encoding) -* [Decoder](#decoding) -* [Stream API](#stream-api) -* [Code Generation](https://github.com/francoispqt/gojay/tree/master/gojay) - -## Decoding - -Decoding is done through two different API similar to standard `encoding/json`: -* [Unmarshal](#unmarshal-api) -* [Decode](#decode-api) - - -Example of basic stucture decoding with Unmarshal: -```go -import "github.com/francoispqt/gojay" - -type user struct { - id int - name string - email string -} -// implement gojay.UnmarshalerJSONObject -func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { - switch key { - case "id": - return dec.Int(&u.id) - case "name": - return dec.String(&u.name) - case "email": - return dec.String(&u.email) - } - return nil -} -func (u *user) NKeys() int { - return 3 -} - -func main() { - u := &user{} - d := []byte(`{"id":1,"name":"gojay","email":"gojay@email.com"}`) - err := gojay.UnmarshalJSONObject(d, u) - if err != nil { - log.Fatal(err) - } -} -``` - -with Decode: -```go -func main() { - u := &user{} - dec := gojay.NewDecoder(bytes.NewReader([]byte(`{"id":1,"name":"gojay","email":"gojay@email.com"}`))) - err := dec.DecodeObject(d, u) - if err != nil { - log.Fatal(err) - } -} -``` - -### Unmarshal API - -Unmarshal API decodes a `[]byte` to a given pointer with a single function. - -Behind the doors, Unmarshal API borrows a `*gojay.Decoder` resets its settings and decodes the data to the given pointer and releases the `*gojay.Decoder` to the pool when it finishes, whether it encounters an error or not. - -If it cannot find the right Decoding strategy for the type of the given pointer, it returns an `InvalidUnmarshalError`. You can test the error returned by doing `if ok := err.(InvalidUnmarshalError); ok {}`. - -Unmarshal API comes with three functions: -* Unmarshal -```go -func Unmarshal(data []byte, v interface{}) error -``` - -* UnmarshalJSONObject -```go -func UnmarshalJSONObject(data []byte, v gojay.UnmarshalerJSONObject) error -``` - -* UnmarshalJSONArray -```go -func UnmarshalJSONArray(data []byte, v gojay.UnmarshalerJSONArray) error -``` - - -### Decode API - -Decode API decodes a `[]byte` to a given pointer by creating or borrowing a `*gojay.Decoder` with an `io.Reader` and calling `Decode` methods. - -__Getting a *gojay.Decoder or Borrowing__ - -You can either get a fresh `*gojay.Decoder` calling `dec := gojay.NewDecoder(io.Reader)` or borrow one from the pool by calling `dec := gojay.BorrowDecoder(io.Reader)`. - -After using a decoder, you can release it by calling `dec.Release()`. Beware, if you reuse the decoder after releasing it, it will panic with an error of type `InvalidUsagePooledDecoderError`. If you want to fully benefit from the pooling, you must release your decoders after using. - -Example getting a fresh an releasing: -```go -str := "" -dec := gojay.NewDecoder(strings.NewReader(`"test"`)) -defer dec.Release() -if err := dec.Decode(&str); err != nil { - log.Fatal(err) -} -``` -Example borrowing a decoder and releasing: -```go -str := "" -dec := gojay.BorrowDecoder(strings.NewReader(`"test"`)) -defer dec.Release() -if err := dec.Decode(&str); err != nil { - log.Fatal(err) -} -``` - -`*gojay.Decoder` has multiple methods to decode to specific types: -* Decode -```go -func (dec *gojay.Decoder) Decode(v interface{}) error -``` -* DecodeObject -```go -func (dec *gojay.Decoder) DecodeObject(v gojay.UnmarshalerJSONObject) error -``` -* DecodeArray -```go -func (dec *gojay.Decoder) DecodeArray(v gojay.UnmarshalerJSONArray) error -``` -* DecodeInt -```go -func (dec *gojay.Decoder) DecodeInt(v *int) error -``` -* DecodeBool -```go -func (dec *gojay.Decoder) DecodeBool(v *bool) error -``` -* DecodeString -```go -func (dec *gojay.Decoder) DecodeString(v *string) error -``` - -All DecodeXxx methods are used to decode top level JSON values. If you are decoding keys or items of a JSON object or array, don't use the Decode methods. - -Example: -```go -reader := strings.NewReader(`"John Doe"`) -dec := NewDecoder(reader) - -var str string -err := dec.DecodeString(&str) -if err != nil { - log.Fatal(err) -} - -fmt.Println(str) // John Doe -``` - -### Structs and Maps -#### UnmarshalerJSONObject Interface - -To unmarshal a JSON object to a structure, the structure must implement the `UnmarshalerJSONObject` interface: -```go -type UnmarshalerJSONObject interface { - UnmarshalJSONObject(*gojay.Decoder, string) error - NKeys() int -} -``` -`UnmarshalJSONObject` method takes two arguments, the first one is a pointer to the Decoder (*gojay.Decoder) and the second one is the string value of the current key being parsed. If the JSON data is not an object, the UnmarshalJSONObject method will never be called. - -`NKeys` method must return the number of keys to Unmarshal in the JSON object or 0. If zero is returned, all keys will be parsed. - -Example of implementation for a struct: -```go -type user struct { - id int - name string - email string -} -// implement UnmarshalerJSONObject -func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { - switch key { - case "id": - return dec.Int(&u.id) - case "name": - return dec.String(&u.name) - case "email": - return dec.String(&u.email) - } - return nil -} -func (u *user) NKeys() int { - return 3 -} -``` - -Example of implementation for a `map[string]string`: -```go -// define our custom map type implementing UnmarshalerJSONObject -type message map[string]string - -// Implementing Unmarshaler -func (m message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - str := "" - err := dec.String(&str) - if err != nil { - return err - } - m[k] = str - return nil -} - -// we return 0, it tells the Decoder to decode all keys -func (m message) NKeys() int { - return 0 -} -``` - -### Arrays, Slices and Channels - -To unmarshal a JSON object to a slice an array or a channel, it must implement the UnmarshalerJSONArray interface: -```go -type UnmarshalerJSONArray interface { - UnmarshalJSONArray(*gojay.Decoder) error -} -``` -UnmarshalJSONArray method takes one argument, a pointer to the Decoder (*gojay.Decoder). If the JSON data is not an array, the Unmarshal method will never be called. - -Example of implementation with a slice: -```go -type testSlice []string -// implement UnmarshalerJSONArray -func (t *testSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { - str := "" - if err := dec.String(&str); err != nil { - return err - } - *t = append(*t, str) - return nil -} - -func main() { - dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim"]`)) - var slice testSlice - err := dec.DecodeArray(&slice) - if err != nil { - log.Fatal(err) - } - fmt.Println(slice) // [Tom Jim] - dec.Release() -} -``` - -Example of implementation with a channel: -```go -type testChannel chan string -// implement UnmarshalerJSONArray -func (c testChannel) UnmarshalJSONArray(dec *gojay.Decoder) error { - str := "" - if err := dec.String(&str); err != nil { - return err - } - c <- str - return nil -} - -func main() { - dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim"]`)) - c := make(testChannel, 2) - err := dec.DecodeArray(c) - if err != nil { - log.Fatal(err) - } - for i := 0; i < 2; i++ { - fmt.Println(<-c) - } - close(c) - dec.Release() -} -``` - -Example of implementation with an array: -```go -type testArray [3]string -// implement UnmarshalerJSONArray -func (a *testArray) UnmarshalJSONArray(dec *Decoder) error { - var str string - if err := dec.String(&str); err != nil { - return err - } - a[dec.Index()] = str - return nil -} - -func main() { - dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim", "Bob"]`)) - var a testArray - err := dec.DecodeArray(&a) - fmt.Println(a) // [Tom Jim Bob] - dec.Release() -} -``` - -### Other types -To decode other types (string, int, int32, int64, uint32, uint64, float, booleans), you don't need to implement any interface. - -Example of encoding strings: -```go -func main() { - json := []byte(`"Jay"`) - var v string - err := gojay.Unmarshal(json, &v) - if err != nil { - log.Fatal(err) - } - fmt.Println(v) // Jay -} -``` - -### Decode values methods -When decoding a JSON object of a JSON array using `UnmarshalerJSONObject` or `UnmarshalerJSONArray` interface, the `gojay.Decoder` provides dozens of methods to Decode multiple types. - -Non exhaustive list of methods available (to see all methods, check the godoc): -```go -dec.Int -dec.Int8 -dec.Int16 -dec.Int32 -dec.Int64 -dec.Uint8 -dec.Uint16 -dec.Uint32 -dec.Uint64 -dec.String -dec.Time -dec.Bool -dec.SQLNullString -dec.SQLNullInt64 -``` - - -## Encoding - -Encoding is done through two different API similar to standard `encoding/json`: -* [Marshal](#marshal-api) -* [Encode](#encode-api) - -Example of basic structure encoding with Marshal: -```go -import "github.com/francoispqt/gojay" - -type user struct { - id int - name string - email string -} - -// implement MarshalerJSONObject -func (u *user) MarshalJSONObject(enc *gojay.Encoder) { - enc.IntKey("id", u.id) - enc.StringKey("name", u.name) - enc.StringKey("email", u.email) -} -func (u *user) IsNil() bool { - return u == nil -} - -func main() { - u := &user{1, "gojay", "gojay@email.com"} - b, err := gojay.MarshalJSONObject(u) - if err != nil { - log.Fatal(err) - } - fmt.Println(string(b)) // {"id":1,"name":"gojay","email":"gojay@email.com"} -} -``` - -with Encode: -```go -func main() { - u := &user{1, "gojay", "gojay@email.com"} - b := strings.Builder{} - enc := gojay.NewEncoder(&b) - if err := enc.Encode(u); err != nil { - log.Fatal(err) - } - fmt.Println(b.String()) // {"id":1,"name":"gojay","email":"gojay@email.com"} -} -``` - -### Marshal API - -Marshal API encodes a value to a JSON `[]byte` with a single function. - -Behind the doors, Marshal API borrows a `*gojay.Encoder` resets its settings and encodes the data to an internal byte buffer and releases the `*gojay.Encoder` to the pool when it finishes, whether it encounters an error or not. - -If it cannot find the right Encoding strategy for the type of the given value, it returns an `InvalidMarshalError`. You can test the error returned by doing `if ok := err.(InvalidMarshalError); ok {}`. - -Marshal API comes with three functions: -* Marshal -```go -func Marshal(v interface{}) ([]byte, error) -``` - -* MarshalJSONObject -```go -func MarshalJSONObject(v gojay.MarshalerJSONObject) ([]byte, error) -``` - -* MarshalJSONArray -```go -func MarshalJSONArray(v gojay.MarshalerJSONArray) ([]byte, error) -``` - -### Encode API - -Encode API decodes a value to JSON by creating or borrowing a `*gojay.Encoder` sending it to an `io.Writer` and calling `Encode` methods. - -__Getting a *gojay.Encoder or Borrowing__ - -You can either get a fresh `*gojay.Encoder` calling `enc := gojay.NewEncoder(io.Writer)` or borrow one from the pool by calling `enc := gojay.BorrowEncoder(io.Writer)`. - -After using an encoder, you can release it by calling `enc.Release()`. Beware, if you reuse the encoder after releasing it, it will panic with an error of type `InvalidUsagePooledEncoderError`. If you want to fully benefit from the pooling, you must release your encoders after using. - -Example getting a fresh encoder an releasing: -```go -str := "test" -b := strings.Builder{} -enc := gojay.NewEncoder(&b) -defer enc.Release() -if err := enc.Encode(str); err != nil { - log.Fatal(err) -} -``` -Example borrowing an encoder and releasing: -```go -str := "test" -b := strings.Builder{} -enc := gojay.BorrowEncoder(b) -defer enc.Release() -if err := enc.Encode(str); err != nil { - log.Fatal(err) -} -``` - -`*gojay.Encoder` has multiple methods to encoder specific types to JSON: -* Encode -```go -func (enc *gojay.Encoder) Encode(v interface{}) error -``` -* EncodeObject -```go -func (enc *gojay.Encoder) EncodeObject(v gojay.MarshalerJSONObject) error -``` -* EncodeArray -```go -func (enc *gojay.Encoder) EncodeArray(v gojay.MarshalerJSONArray) error -``` -* EncodeInt -```go -func (enc *gojay.Encoder) EncodeInt(n int) error -``` -* EncodeInt64 -```go -func (enc *gojay.Encoder) EncodeInt64(n int64) error -``` -* EncodeFloat -```go -func (enc *gojay.Encoder) EncodeFloat(n float64) error -``` -* EncodeBool -```go -func (enc *gojay.Encoder) EncodeBool(v bool) error -``` -* EncodeString -```go -func (enc *gojay.Encoder) EncodeString(s string) error -``` - -### Structs and Maps - -To encode a structure, the structure must implement the MarshalerJSONObject interface: -```go -type MarshalerJSONObject interface { - MarshalJSONObject(enc *gojay.Encoder) - IsNil() bool -} -``` -`MarshalJSONObject` method takes one argument, a pointer to the Encoder (*gojay.Encoder). The method must add all the keys in the JSON Object by calling Decoder's methods. - -IsNil method returns a boolean indicating if the interface underlying value is nil or not. It is used to safely ensure that the underlying value is not nil without using Reflection. - -Example of implementation for a struct: -```go -type user struct { - id int - name string - email string -} - -// implement MarshalerJSONObject -func (u *user) MarshalJSONObject(enc *gojay.Encoder) { - enc.IntKey("id", u.id) - enc.StringKey("name", u.name) - enc.StringKey("email", u.email) -} -func (u *user) IsNil() bool { - return u == nil -} -``` - -Example of implementation for a `map[string]string`: -```go -// define our custom map type implementing MarshalerJSONObject -type message map[string]string - -// Implementing Marshaler -func (m message) MarshalJSONObject(enc *gojay.Encoder) { - for k, v := range m { - enc.StringKey(k, v) - } -} - -func (m message) IsNil() bool { - return m == nil -} -``` - -### Arrays and Slices -To encode an array or a slice, the slice/array must implement the MarshalerJSONArray interface: -```go -type MarshalerJSONArray interface { - MarshalJSONArray(enc *gojay.Encoder) - IsNil() bool -} -``` -`MarshalJSONArray` method takes one argument, a pointer to the Encoder (*gojay.Encoder). The method must add all element in the JSON Array by calling Decoder's methods. - -`IsNil` method returns a boolean indicating if the interface underlying value is nil(empty) or not. It is used to safely ensure that the underlying value is not nil without using Reflection and also to in `OmitEmpty` feature. - -Example of implementation: -```go -type users []*user -// implement MarshalerJSONArray -func (u *users) MarshalJSONArray(enc *gojay.Encoder) { - for _, e := range u { - enc.Object(e) - } -} -func (u *users) IsNil() bool { - return len(u) == 0 -} -``` - -### Other types -To encode other types (string, int, float, booleans), you don't need to implement any interface. - -Example of encoding strings: -```go -func main() { - name := "Jay" - b, err := gojay.Marshal(name) - if err != nil { - log.Fatal(err) - } - fmt.Println(string(b)) // "Jay" -} -``` - -# Stream API - -### Stream Decoding -GoJay ships with a powerful stream decoder. - -It allows to read continuously from an io.Reader stream and do JIT decoding writing unmarshalled JSON to a channel to allow async consuming. - -When using the Stream API, the Decoder implements context.Context to provide graceful cancellation. - -To decode a stream of JSON, you must call `gojay.Stream.DecodeStream` and pass it a `UnmarshalerStream` implementation. - -```go -type UnmarshalerStream interface { - UnmarshalStream(*StreamDecoder) error -} -``` - -Example of implementation of stream reading from a WebSocket connection: -```go -// implement UnmarshalerStream -type ChannelStream chan *user - -func (c ChannelStream) UnmarshalStream(dec *gojay.StreamDecoder) error { - u := &user{} - if err := dec.Object(u); err != nil { - return err - } - c <- u - return nil -} - -func main() { - // get our websocket connection - origin := "http://localhost/" - url := "ws://localhost:12345/ws" - ws, err := websocket.Dial(url, "", origin) - if err != nil { - log.Fatal(err) - } - // create our channel which will receive our objects - streamChan := ChannelStream(make(chan *user)) - // borrow a decoder - dec := gojay.Stream.BorrowDecoder(ws) - // start decoding, it will block until a JSON message is decoded from the WebSocket - // or until Done channel is closed - go dec.DecodeStream(streamChan) - for { - select { - case v := <-streamChan: - // Got something from my websocket! - log.Println(v) - case <-dec.Done(): - log.Println("finished reading from WebSocket") - os.Exit(0) - } - } -} -``` - -### Stream Encoding -GoJay ships with a powerful stream encoder part of the Stream API. - -It allows to write continuously to an io.Writer and do JIT encoding of data fed to a channel to allow async consuming. You can set multiple consumers on the channel to be as performant as possible. Consumers are non blocking and are scheduled individually in their own go routine. - -When using the Stream API, the Encoder implements context.Context to provide graceful cancellation. - -To encode a stream of data, you must call `EncodeStream` and pass it a `MarshalerStream` implementation. - -```go -type MarshalerStream interface { - MarshalStream(enc *gojay.StreamEncoder) -} -``` - -Example of implementation of stream writing to a WebSocket: -```go -// Our structure which will be pushed to our stream -type user struct { - id int - name string - email string -} - -func (u *user) MarshalJSONObject(enc *gojay.Encoder) { - enc.IntKey("id", u.id) - enc.StringKey("name", u.name) - enc.StringKey("email", u.email) -} -func (u *user) IsNil() bool { - return u == nil -} - -// Our MarshalerStream implementation -type StreamChan chan *user - -func (s StreamChan) MarshalStream(enc *gojay.StreamEncoder) { - select { - case <-enc.Done(): - return - case o := <-s: - enc.Object(o) - } -} - -// Our main function -func main() { - // get our websocket connection - origin := "http://localhost/" - url := "ws://localhost:12345/ws" - ws, err := websocket.Dial(url, "", origin) - if err != nil { - log.Fatal(err) - } - // we borrow an encoder set stdout as the writer, - // set the number of consumer to 10 - // and tell the encoder to separate each encoded element - // added to the channel by a new line character - enc := gojay.Stream.BorrowEncoder(ws).NConsumer(10).LineDelimited() - // instantiate our MarshalerStream - s := StreamChan(make(chan *user)) - // start the stream encoder - // will block its goroutine until enc.Cancel(error) is called - // or until something is written to the channel - go enc.EncodeStream(s) - // write to our MarshalerStream - for i := 0; i < 1000; i++ { - s <- &user{i, "username", "user@email.com"} - } - // Wait - <-enc.Done() -} -``` - -# Unsafe API - -Unsafe API has the same functions than the regular API, it only has `Unmarshal API` for now. It is unsafe because it makes assumptions on the quality of the given JSON. - -If you are not sure if your JSON is valid, don't use the Unsafe API. - -Also, the `Unsafe` API does not copy the buffer when using Unmarshal API, which, in case of string decoding, can lead to data corruption if a byte buffer is reused. Using the `Decode` API makes `Unsafe` API safer as the io.Reader relies on `copy` builtin method and `Decoder` will have its own internal buffer :) - -Access the `Unsafe` API this way: -```go -gojay.Unsafe.Unmarshal(b, v) -``` - - -# Benchmarks - -Benchmarks encode and decode three different data based on size (small, medium, large). - -To run benchmark for decoder: -```bash -cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/decoder && make bench -``` - -To run benchmark for encoder: -```bash -cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/encoder && make bench -``` - -# Benchmark Results -## Decode - - - -### Small Payload -[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_small_test.go) - -[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_small.go) - -| | ns/op | bytes/op | allocs/op | -|-----------------|-----------|--------------|-----------| -| Std Library | 2547 | 496 | 4 | -| JsonIter | 2046 | 312 | 12 | -| JsonParser | 1408 | 0 | 0 | -| EasyJson | 929 | 240 | 2 | -| **GoJay** | **807** | **256** | **2** | -| **GoJay-unsafe**| **712** | **112** | **1** | - -### Medium Payload -[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_medium_test.go) - -[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_medium.go) - -| | ns/op | bytes/op | allocs/op | -|-----------------|-----------|----------|-----------| -| Std Library | 30148 | 2152 | 496 | -| JsonIter | 16309 | 2976 | 80 | -| JsonParser | 7793 | 0 | 0 | -| EasyJson | 7957 | 232 | 6 | -| **GoJay** | **4984** | **2448** | **8** | -| **GoJay-unsafe**| **4809** | **144** | **7** | - -### Large Payload -[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_large_test.go) - -[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_large.go) - -| | ns/op | bytes/op | allocs/op | -|-----------------|-----------|-------------|-----------| -| JsonIter | 210078 | 41712 | 1136 | -| EasyJson | 106626 | 160 | 2 | -| JsonParser | 66813 | 0 | 0 | -| **GoJay** | **52153** | **31241** | **77** | -| **GoJay-unsafe**| **48277** | **2561** | **76** | - -## Encode - - - -### Small Struct -[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_small_test.go) - -[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_small.go) - -| | ns/op | bytes/op | allocs/op | -|----------------|----------|--------------|-----------| -| Std Library | 1280 | 464 | 3 | -| EasyJson | 871 | 944 | 6 | -| JsonIter | 866 | 272 | 3 | -| **GoJay** | **543** | **112** | **1** | -| **GoJay-func** | **347** | **0** | **0** | - -### Medium Struct -[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_medium_test.go) - -[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_medium.go) - -| | ns/op | bytes/op | allocs/op | -|-------------|----------|--------------|-----------| -| Std Library | 5006 | 1496 | 25 | -| JsonIter | 2232 | 1544 | 20 | -| EasyJson | 1997 | 1544 | 19 | -| **GoJay** | **1522** | **312** | **14** | - -### Large Struct -[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_large_test.go) - -[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_large.go) - -| | ns/op | bytes/op | allocs/op | -|-------------|-----------|--------------|-----------| -| Std Library | 66441 | 20576 | 332 | -| JsonIter | 35247 | 20255 | 328 | -| EasyJson | 32053 | 15474 | 327 | -| **GoJay** | **27847** | **9802** | **318** | - -# Contributing - -Contributions are welcome :) - -If you encounter issues please report it in Github and/or send an email at [francois@parquet.ninja](mailto:francois@parquet.ninja) - diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode.go deleted file mode 100644 index fbd07f76c..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode.go +++ /dev/null @@ -1,386 +0,0 @@ -package gojay - -import ( - "fmt" - "io" -) - -// UnmarshalJSONArray parses the JSON-encoded data and stores the result in the value pointed to by v. -// -// v must implement UnmarshalerJSONArray. -// -// If a JSON value is not appropriate for a given target type, or if a JSON number -// overflows the target type, UnmarshalJSONArray skips that field and completes the unmarshaling as best it can. -func UnmarshalJSONArray(data []byte, v UnmarshalerJSONArray) error { - dec := borrowDecoder(nil, 0) - defer dec.Release() - dec.data = make([]byte, len(data)) - copy(dec.data, data) - dec.length = len(data) - _, err := dec.decodeArray(v) - if err != nil { - return err - } - if dec.err != nil { - return dec.err - } - return nil -} - -// UnmarshalJSONObject parses the JSON-encoded data and stores the result in the value pointed to by v. -// -// v must implement UnmarshalerJSONObject. -// -// If a JSON value is not appropriate for a given target type, or if a JSON number -// overflows the target type, UnmarshalJSONObject skips that field and completes the unmarshaling as best it can. -func UnmarshalJSONObject(data []byte, v UnmarshalerJSONObject) error { - dec := borrowDecoder(nil, 0) - defer dec.Release() - dec.data = make([]byte, len(data)) - copy(dec.data, data) - dec.length = len(data) - _, err := dec.decodeObject(v) - if err != nil { - return err - } - if dec.err != nil { - return dec.err - } - return nil -} - -// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v. -// If v is nil, not an implementation of UnmarshalerJSONObject or UnmarshalerJSONArray or not one of the following types: -// *string, **string, *int, **int, *int8, **int8, *int16, **int16, *int32, **int32, *int64, **int64, *uint8, **uint8, *uint16, **uint16, -// *uint32, **uint32, *uint64, **uint64, *float64, **float64, *float32, **float32, *bool, **bool -// Unmarshal returns an InvalidUnmarshalError. -// -// -// If a JSON value is not appropriate for a given target type, or if a JSON number -// overflows the target type, Unmarshal skips that field and completes the unmarshaling as best it can. -// If no more serious errors are encountered, Unmarshal returns an UnmarshalTypeError describing the earliest such error. -// In any case, it's not guaranteed that all the remaining fields following the problematic one will be unmarshaled into the target object. -func Unmarshal(data []byte, v interface{}) error { - var err error - var dec *Decoder - switch vt := v.(type) { - case *string: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeString(vt) - case **string: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeStringNull(vt) - case *int: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt(vt) - case **int: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeIntNull(vt) - case *int8: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt8(vt) - case **int8: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt8Null(vt) - case *int16: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt16(vt) - case **int16: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt16Null(vt) - case *int32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt32(vt) - case **int32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt32Null(vt) - case *int64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt64(vt) - case **int64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt64Null(vt) - case *uint8: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint8(vt) - case **uint8: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint8Null(vt) - case *uint16: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint16(vt) - case **uint16: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint16Null(vt) - case *uint32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint32(vt) - case **uint32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint32Null(vt) - case *uint64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint64(vt) - case **uint64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint64Null(vt) - case *float64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeFloat64(vt) - case **float64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeFloat64Null(vt) - case *float32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeFloat32(vt) - case **float32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeFloat32Null(vt) - case *bool: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeBool(vt) - case **bool: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeBoolNull(vt) - case UnmarshalerJSONObject: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = make([]byte, len(data)) - copy(dec.data, data) - _, err = dec.decodeObject(vt) - case UnmarshalerJSONArray: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = make([]byte, len(data)) - copy(dec.data, data) - _, err = dec.decodeArray(vt) - case *interface{}: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = make([]byte, len(data)) - copy(dec.data, data) - err = dec.decodeInterface(vt) - default: - return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt)) - } - defer dec.Release() - if err != nil { - return err - } - return dec.err -} - -// UnmarshalerJSONObject is the interface to implement to decode a JSON Object. -type UnmarshalerJSONObject interface { - UnmarshalJSONObject(*Decoder, string) error - NKeys() int -} - -// UnmarshalerJSONArray is the interface to implement to decode a JSON Array. -type UnmarshalerJSONArray interface { - UnmarshalJSONArray(*Decoder) error -} - -// A Decoder reads and decodes JSON values from an input stream. -type Decoder struct { - r io.Reader - data []byte - err error - isPooled byte - called byte - child byte - cursor int - length int - keysDone int - arrayIndex int -} - -// Decode reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -// The differences between Decode and Unmarshal are: -// - Decode reads from an io.Reader in the Decoder, whereas Unmarshal reads from a []byte -// - Decode leaves to the user the option of borrowing and releasing a Decoder, whereas Unmarshal internally always borrows a Decoder and releases it when the unmarshaling is completed -func (dec *Decoder) Decode(v interface{}) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - var err error - switch vt := v.(type) { - case *string: - err = dec.decodeString(vt) - case **string: - err = dec.decodeStringNull(vt) - case *int: - err = dec.decodeInt(vt) - case **int: - err = dec.decodeIntNull(vt) - case *int8: - err = dec.decodeInt8(vt) - case **int8: - err = dec.decodeInt8Null(vt) - case *int16: - err = dec.decodeInt16(vt) - case **int16: - err = dec.decodeInt16Null(vt) - case *int32: - err = dec.decodeInt32(vt) - case **int32: - err = dec.decodeInt32Null(vt) - case *int64: - err = dec.decodeInt64(vt) - case **int64: - err = dec.decodeInt64Null(vt) - case *uint8: - err = dec.decodeUint8(vt) - case **uint8: - err = dec.decodeUint8Null(vt) - case *uint16: - err = dec.decodeUint16(vt) - case **uint16: - err = dec.decodeUint16Null(vt) - case *uint32: - err = dec.decodeUint32(vt) - case **uint32: - err = dec.decodeUint32Null(vt) - case *uint64: - err = dec.decodeUint64(vt) - case **uint64: - err = dec.decodeUint64Null(vt) - case *float64: - err = dec.decodeFloat64(vt) - case **float64: - err = dec.decodeFloat64Null(vt) - case *float32: - err = dec.decodeFloat32(vt) - case **float32: - err = dec.decodeFloat32Null(vt) - case *bool: - err = dec.decodeBool(vt) - case **bool: - err = dec.decodeBoolNull(vt) - case UnmarshalerJSONObject: - _, err = dec.decodeObject(vt) - case UnmarshalerJSONArray: - _, err = dec.decodeArray(vt) - case *EmbeddedJSON: - err = dec.decodeEmbeddedJSON(vt) - case *interface{}: - err = dec.decodeInterface(vt) - default: - return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt)) - } - if err != nil { - return err - } - return dec.err -} - -// Non exported - -func isDigit(b byte) bool { - switch b { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - return true - default: - return false - } -} - -func (dec *Decoder) read() bool { - if dec.r != nil { - // if we reach the end, double the buffer to ensure there's always more space - if len(dec.data) == dec.length { - nLen := dec.length * 2 - if nLen == 0 { - nLen = 512 - } - Buf := make([]byte, nLen, nLen) - copy(Buf, dec.data) - dec.data = Buf - } - var n int - var err error - for n == 0 { - n, err = dec.r.Read(dec.data[dec.length:]) - if err != nil { - if err != io.EOF { - dec.err = err - return false - } - if n == 0 { - return false - } - dec.length = dec.length + n - return true - } - } - dec.length = dec.length + n - return true - } - return false -} - -func (dec *Decoder) nextChar() byte { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - } - d := dec.data[dec.cursor] - return d - } - return 0 -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_array.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_array.go deleted file mode 100644 index 297f2ee74..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_array.go +++ /dev/null @@ -1,247 +0,0 @@ -package gojay - -import "reflect" - -// DecodeArray reads the next JSON-encoded value from the decoder's input (io.Reader) -// and stores it in the value pointed to by v. -// -// v must implement UnmarshalerJSONArray. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeArray(v UnmarshalerJSONArray) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - _, err := dec.decodeArray(v) - return err -} -func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) { - // remember last array index in case of nested arrays - lastArrayIndex := dec.arrayIndex - dec.arrayIndex = 0 - defer func() { - dec.arrayIndex = lastArrayIndex - }() - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - case '[': - dec.cursor = dec.cursor + 1 - // array is open, char is not space start readings - for dec.nextChar() != 0 { - // closing array - if dec.data[dec.cursor] == ']' { - dec.cursor = dec.cursor + 1 - return dec.cursor, nil - } - // calling unmarshall function for each element of the slice - err := arr.UnmarshalJSONArray(dec) - if err != nil { - return 0, err - } - dec.arrayIndex++ - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) - case 'n': - // is null - dec.cursor++ - err := dec.assertNull() - if err != nil { - return 0, err - } - return dec.cursor, nil - case '{', '"', 'f', 't', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - // can't unmarshall to struct - // we skip array and set Error - dec.err = dec.makeInvalidUnmarshalErr(arr) - err := dec.skipData() - if err != nil { - return 0, err - } - return dec.cursor, nil - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeArrayNull(v interface{}) (int, error) { - // remember last array index in case of nested arrays - lastArrayIndex := dec.arrayIndex - dec.arrayIndex = 0 - defer func() { - dec.arrayIndex = lastArrayIndex - }() - vv := reflect.ValueOf(v) - vvt := vv.Type() - if vvt.Kind() != reflect.Ptr || vvt.Elem().Kind() != reflect.Ptr { - dec.err = ErrUnmarshalPtrExpected - return 0, dec.err - } - // not an array not an error, but do not know what to do - // do not check syntax - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - case '[': - dec.cursor = dec.cursor + 1 - // create our new type - elt := vv.Elem() - n := reflect.New(elt.Type().Elem()) - var arr UnmarshalerJSONArray - var ok bool - if arr, ok = n.Interface().(UnmarshalerJSONArray); !ok { - dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONArray)(nil)) - return 0, dec.err - } - // array is open, char is not space start readings - for dec.nextChar() != 0 { - // closing array - if dec.data[dec.cursor] == ']' { - elt.Set(n) - dec.cursor = dec.cursor + 1 - return dec.cursor, nil - } - // calling unmarshall function for each element of the slice - err := arr.UnmarshalJSONArray(dec) - if err != nil { - return 0, err - } - dec.arrayIndex++ - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) - case 'n': - // is null - dec.cursor++ - err := dec.assertNull() - if err != nil { - return 0, err - } - return dec.cursor, nil - case '{', '"', 'f', 't', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - // can't unmarshall to struct - // we skip array and set Error - dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONArray)(nil)) - err := dec.skipData() - if err != nil { - return 0, err - } - return dec.cursor, nil - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) skipArray() (int, error) { - var arraysOpen = 1 - var arraysClosed = 0 - // var stringOpen byte = 0 - for j := dec.cursor; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case ']': - arraysClosed++ - // everything is closed return - if arraysOpen == arraysClosed { - // add char to object data - return j + 1, nil - } - case '[': - arraysOpen++ - case '"': - j++ - var isInEscapeSeq bool - var isFirstQuote = true - for ; j < dec.length || dec.read(); j++ { - if dec.data[j] != '"' { - continue - } - if dec.data[j-1] != '\\' || (!isInEscapeSeq && !isFirstQuote) { - break - } else { - isInEscapeSeq = false - } - if isFirstQuote { - isFirstQuote = false - } - // loop backward and count how many anti slash found - // to see if string is effectively escaped - ct := 0 - for i := j - 1; i > 0; i-- { - if dec.data[i] != '\\' { - break - } - ct++ - } - // is pair number of slashes, quote is not escaped - if ct&1 == 0 { - break - } - isInEscapeSeq = true - } - default: - continue - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -// DecodeArrayFunc is a func type implementing UnmarshalerJSONArray. -// Use it to cast a `func(*Decoder) error` to Unmarshal an array on the fly. - -type DecodeArrayFunc func(*Decoder) error - -// UnmarshalJSONArray implements UnmarshalerJSONArray. -func (f DecodeArrayFunc) UnmarshalJSONArray(dec *Decoder) error { - return f(dec) -} - -// IsNil implements UnmarshalerJSONArray. -func (f DecodeArrayFunc) IsNil() bool { - return f == nil -} - -// Add Values functions - -// AddArray decodes the JSON value within an object or an array to a UnmarshalerJSONArray. -func (dec *Decoder) AddArray(v UnmarshalerJSONArray) error { - return dec.Array(v) -} - -// AddArrayNull decodes the JSON value within an object or an array to a UnmarshalerJSONArray. -func (dec *Decoder) AddArrayNull(v interface{}) error { - return dec.ArrayNull(v) -} - -// Array decodes the JSON value within an object or an array to a UnmarshalerJSONArray. -func (dec *Decoder) Array(v UnmarshalerJSONArray) error { - newCursor, err := dec.decodeArray(v) - if err != nil { - return err - } - dec.cursor = newCursor - dec.called |= 1 - return nil -} - -// ArrayNull decodes the JSON value within an object or an array to a UnmarshalerJSONArray. -// v should be a pointer to an UnmarshalerJSONArray, -// if `null` value is encountered in JSON, it will leave the value v untouched, -// else it will create a new instance of the UnmarshalerJSONArray behind v. -func (dec *Decoder) ArrayNull(v interface{}) error { - newCursor, err := dec.decodeArrayNull(v) - if err != nil { - return err - } - dec.cursor = newCursor - dec.called |= 1 - return nil -} - -// Index returns the index of an array being decoded. -func (dec *Decoder) Index() int { - return dec.arrayIndex -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_bool.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_bool.go deleted file mode 100644 index 1dc304ba7..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_bool.go +++ /dev/null @@ -1,241 +0,0 @@ -package gojay - -// DecodeBool reads the next JSON-encoded value from the decoder's input (io.Reader) -// and stores it in the boolean pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeBool(v *bool) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeBool(v) -} -func (dec *Decoder) decodeBool(v *bool) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - case 't': - dec.cursor++ - err := dec.assertTrue() - if err != nil { - return err - } - *v = true - return nil - case 'f': - dec.cursor++ - err := dec.assertFalse() - if err != nil { - return err - } - *v = false - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - *v = false - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return nil -} -func (dec *Decoder) decodeBoolNull(v **bool) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - case 't': - dec.cursor++ - err := dec.assertTrue() - if err != nil { - return err - } - if *v == nil { - *v = new(bool) - } - **v = true - return nil - case 'f': - dec.cursor++ - err := dec.assertFalse() - if err != nil { - return err - } - if *v == nil { - *v = new(bool) - } - **v = false - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return nil -} - -func (dec *Decoder) assertTrue() error { - i := 0 - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch i { - case 0: - if dec.data[dec.cursor] != 'r' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 1: - if dec.data[dec.cursor] != 'u' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 2: - if dec.data[dec.cursor] != 'e' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 3: - switch dec.data[dec.cursor] { - case ' ', '\b', '\t', '\n', ',', ']', '}': - // dec.cursor-- - return nil - default: - return dec.raiseInvalidJSONErr(dec.cursor) - } - } - i++ - } - if i == 3 { - return nil - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) assertNull() error { - i := 0 - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch i { - case 0: - if dec.data[dec.cursor] != 'u' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 1: - if dec.data[dec.cursor] != 'l' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 2: - if dec.data[dec.cursor] != 'l' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 3: - switch dec.data[dec.cursor] { - case ' ', '\t', '\n', ',', ']', '}': - // dec.cursor-- - return nil - default: - return dec.raiseInvalidJSONErr(dec.cursor) - } - } - i++ - } - if i == 3 { - return nil - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) assertFalse() error { - i := 0 - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch i { - case 0: - if dec.data[dec.cursor] != 'a' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 1: - if dec.data[dec.cursor] != 'l' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 2: - if dec.data[dec.cursor] != 's' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 3: - if dec.data[dec.cursor] != 'e' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 4: - switch dec.data[dec.cursor] { - case ' ', '\t', '\n', ',', ']', '}': - // dec.cursor-- - return nil - default: - return dec.raiseInvalidJSONErr(dec.cursor) - } - } - i++ - } - if i == 4 { - return nil - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -// Add Values functions - -// AddBool decodes the JSON value within an object or an array to a *bool. -// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned. -// If next key is null, bool will be false. -func (dec *Decoder) AddBool(v *bool) error { - return dec.Bool(v) -} - -// AddBoolNull decodes the JSON value within an object or an array to a *bool. -// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned. -// If next key is null, bool will be false. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddBoolNull(v **bool) error { - return dec.BoolNull(v) -} - -// Bool decodes the JSON value within an object or an array to a *bool. -// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned. -// If next key is null, bool will be false. -func (dec *Decoder) Bool(v *bool) error { - err := dec.decodeBool(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// BoolNull decodes the JSON value within an object or an array to a *bool. -// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned. -// If next key is null, bool will be false. -func (dec *Decoder) BoolNull(v **bool) error { - err := dec.decodeBoolNull(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_embedded_json.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_embedded_json.go deleted file mode 100644 index 67fcc2eae..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_embedded_json.go +++ /dev/null @@ -1,85 +0,0 @@ -package gojay - -// EmbeddedJSON is a raw encoded JSON value. -// It can be used to delay JSON decoding or precompute a JSON encoding. -type EmbeddedJSON []byte - -func (dec *Decoder) decodeEmbeddedJSON(ej *EmbeddedJSON) error { - var err error - if ej == nil { - return InvalidUnmarshalError("Invalid nil pointer given") - } - var beginOfEmbeddedJSON int - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - // is null - case 'n': - beginOfEmbeddedJSON = dec.cursor - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - case 't': - beginOfEmbeddedJSON = dec.cursor - dec.cursor++ - err := dec.assertTrue() - if err != nil { - return err - } - // is false - case 'f': - beginOfEmbeddedJSON = dec.cursor - dec.cursor++ - err := dec.assertFalse() - if err != nil { - return err - } - // is an object - case '{': - beginOfEmbeddedJSON = dec.cursor - dec.cursor = dec.cursor + 1 - dec.cursor, err = dec.skipObject() - // is string - case '"': - beginOfEmbeddedJSON = dec.cursor - dec.cursor = dec.cursor + 1 - err = dec.skipString() // why no new dec.cursor in result? - // is array - case '[': - beginOfEmbeddedJSON = dec.cursor - dec.cursor = dec.cursor + 1 - dec.cursor, err = dec.skipArray() - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': - beginOfEmbeddedJSON = dec.cursor - dec.cursor, err = dec.skipNumber() - } - break - } - if err == nil { - if dec.cursor-1 >= beginOfEmbeddedJSON { - *ej = append(*ej, dec.data[beginOfEmbeddedJSON:dec.cursor]...) - } - dec.called |= 1 - } - return err -} - -// AddEmbeddedJSON adds an EmbeddedsJSON to the value pointed by v. -// It can be used to delay JSON decoding or precompute a JSON encoding. -func (dec *Decoder) AddEmbeddedJSON(v *EmbeddedJSON) error { - return dec.EmbeddedJSON(v) -} - -// EmbeddedJSON adds an EmbeddedsJSON to the value pointed by v. -// It can be used to delay JSON decoding or precompute a JSON encoding. -func (dec *Decoder) EmbeddedJSON(v *EmbeddedJSON) error { - err := dec.decodeEmbeddedJSON(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_interface.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_interface.go deleted file mode 100644 index 015790d85..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_interface.go +++ /dev/null @@ -1,130 +0,0 @@ -package gojay - -// TODO @afiune for now we are using the standard json unmarshaling but in -// the future it would be great to implement one here inside this repo -import "encoding/json" - -// DecodeInterface reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by i. -// -// i must be an interface poiter -func (dec *Decoder) DecodeInterface(i *interface{}) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - err := dec.decodeInterface(i) - return err -} - -func (dec *Decoder) decodeInterface(i *interface{}) error { - start, end, err := dec.getObject() - if err != nil { - dec.cursor = start - return err - } - - // if start & end are equal the object is a null, don't unmarshal - if start == end { - return nil - } - - object := dec.data[start:end] - if err = json.Unmarshal(object, i); err != nil { - return err - } - - dec.cursor = end - return nil -} - -// @afiune Maybe return the type as well? -func (dec *Decoder) getObject() (start int, end int, err error) { - // start cursor - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - // is null - case 'n': - dec.cursor++ - err = dec.assertNull() - if err != nil { - return - } - // Set start & end to the same cursor to indicate the object - // is a null and should not be unmarshal - start = dec.cursor - end = dec.cursor - return - case 't': - start = dec.cursor - dec.cursor++ - err = dec.assertTrue() - if err != nil { - return - } - end = dec.cursor - dec.cursor++ - return - // is false - case 'f': - start = dec.cursor - dec.cursor++ - err = dec.assertFalse() - if err != nil { - return - } - end = dec.cursor - dec.cursor++ - return - // is an object - case '{': - start = dec.cursor - dec.cursor++ - end, err = dec.skipObject() - dec.cursor = end - return - // is string - case '"': - start = dec.cursor - dec.cursor++ - start, end, err = dec.getString() - start-- - dec.cursor = end - return - // is array - case '[': - start = dec.cursor - dec.cursor++ - end, err = dec.skipArray() - dec.cursor = end - return - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': - start = dec.cursor - end, err = dec.skipNumber() - dec.cursor = end - return - default: - err = dec.raiseInvalidJSONErr(dec.cursor) - return - } - } - err = dec.raiseInvalidJSONErr(dec.cursor) - return -} - -// Add Values functions - -// AddInterface decodes the JSON value within an object or an array to a interface{}. -func (dec *Decoder) AddInterface(v *interface{}) error { - return dec.Interface(v) -} - -// Interface decodes the JSON value within an object or an array to an interface{}. -func (dec *Decoder) Interface(value *interface{}) error { - err := dec.decodeInterface(value) - if err != nil { - return err - } - dec.called |= 1 - return nil -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_number.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_number.go deleted file mode 100644 index 0042b471e..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_number.go +++ /dev/null @@ -1,118 +0,0 @@ -package gojay - -import ( - "math" -) - -var digits []int8 - -const maxInt64toMultiply = math.MaxInt64 / 10 -const maxInt32toMultiply = math.MaxInt32 / 10 -const maxInt16toMultiply = math.MaxInt16 / 10 -const maxInt8toMultiply = math.MaxInt8 / 10 -const maxUint8toMultiply = math.MaxUint8 / 10 -const maxUint16toMultiply = math.MaxUint16 / 10 -const maxUint32toMultiply = math.MaxUint32 / 10 -const maxUint64toMultiply = math.MaxUint64 / 10 -const maxUint32Length = 10 -const maxUint64Length = 20 -const maxUint16Length = 5 -const maxUint8Length = 3 -const maxInt32Length = 10 -const maxInt64Length = 19 -const maxInt16Length = 5 -const maxInt8Length = 3 -const invalidNumber = int8(-1) - -var pow10uint64 = [21]uint64{ - 0, - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, - 10000000000, - 100000000000, - 1000000000000, - 10000000000000, - 100000000000000, - 1000000000000000, - 10000000000000000, - 100000000000000000, - 1000000000000000000, - 10000000000000000000, -} - -var skipNumberEndCursorIncrement [256]int - -func init() { - digits = make([]int8, 256) - for i := 0; i < len(digits); i++ { - digits[i] = invalidNumber - } - for i := int8('0'); i <= int8('9'); i++ { - digits[i] = i - int8('0') - } - - for i := 0; i < 256; i++ { - switch i { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'e', 'E', '+', '-': - skipNumberEndCursorIncrement[i] = 1 - } - } -} - -func (dec *Decoder) skipNumber() (int, error) { - end := dec.cursor + 1 - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - end += skipNumberEndCursorIncrement[dec.data[j]] - - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'e', 'E', '+', '-', ' ', '\n', '\t', '\r': - continue - case ',', '}', ']': - return end, nil - default: - // invalid json we expect numbers, dot (single one), comma, or spaces - return end, dec.raiseInvalidJSONErr(dec.cursor) - } - } - - return end, nil -} - -func (dec *Decoder) getExponent() (int64, error) { - start := dec.cursor - end := dec.cursor - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { // is positive - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = dec.cursor + 1 - case '-': - dec.cursor++ - exp, err := dec.getExponent() - return -exp, err - case '+': - dec.cursor++ - return dec.getExponent() - default: - // if nothing return 0 - // could raise error - if start == end { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoi64(start, end-1), nil - } - } - if start == end { - - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoi64(start, end-1), nil -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_number_float.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_number_float.go deleted file mode 100644 index f76c5861e..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_number_float.go +++ /dev/null @@ -1,516 +0,0 @@ -package gojay - -// DecodeFloat64 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the float64 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeFloat64(v *float64) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeFloat64(v) -} -func (dec *Decoder) decodeFloat64(v *float64) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getFloat() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getFloatNegative() - if err != nil { - return err - } - *v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeFloat64Null(v **float64) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getFloat() - if err != nil { - return err - } - if *v == nil { - *v = new(float64) - } - **v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getFloatNegative() - if err != nil { - return err - } - if *v == nil { - *v = new(float64) - } - **v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getFloatNegative() (float64, error) { - // look for following numbers - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - return dec.getFloat() - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getFloat() (float64, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case '.': - // we get part before decimal as integer - beforeDecimal := dec.atoi64(start, end) - // then we get part after decimal as integer - start = j + 1 - // get number after the decimal point - for i := j + 1; i < dec.length || dec.read(); i++ { - c := dec.data[i] - if isDigit(c) { - end = i - // multiply the before decimal point portion by 10 using bitwise - // make sure it doesn't overflow - if end-start < 18 { - beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) - } - continue - } else if (c == 'e' || c == 'E') && j < i-1 { - // we have an exponent, convert first the value we got before the exponent - var afterDecimal int64 - expI := end - start + 2 - // if exp is too long, it means number is too long, just truncate the number - if expI >= len(pow10uint64) || expI < 0 { - expI = len(pow10uint64) - 2 - afterDecimal = dec.atoi64(start, start+expI-2) - } else { - // then we add both integers - // then we divide the number by the power found - afterDecimal = dec.atoi64(start, end) - } - dec.cursor = i + 1 - pow := pow10uint64[expI] - floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) - exp, err := dec.getExponent() - if err != nil { - return 0, err - } - pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // absolute exponent - if pExp >= int64(len(pow10uint64)) || pExp < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - // if exponent is negative - if exp < 0 { - return float64(floatVal) * (1 / float64(pow10uint64[pExp])), nil - } - return float64(floatVal) * float64(pow10uint64[pExp]), nil - } - dec.cursor = i - break - } - if end >= dec.length || end < start { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - var afterDecimal int64 - expI := end - start + 2 - // if exp is too long, it means number is too long, just truncate the number - if expI >= len(pow10uint64) || expI < 0 { - expI = 19 - afterDecimal = dec.atoi64(start, start+expI-2) - } else { - afterDecimal = dec.atoi64(start, end) - } - - pow := pow10uint64[expI] - // then we add both integers - // then we divide the number by the power found - return float64(beforeDecimal+afterDecimal) / float64(pow), nil - case 'e', 'E': - dec.cursor = j + 1 - // we get part before decimal as integer - beforeDecimal := uint64(dec.atoi64(start, end)) - // get exponent - exp, err := dec.getExponent() - if err != nil { - return 0, err - } - pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs - if pExp >= int64(len(pow10uint64)) || pExp < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - // if exponent is negative - if exp < 0 { - return float64(beforeDecimal) * (1 / float64(pow10uint64[pExp])), nil - } - return float64(beforeDecimal) * float64(pow10uint64[pExp]), nil - case ' ', '\n', '\t', '\r', ',', '}', ']': // does not have decimal - dec.cursor = j - return float64(dec.atoi64(start, end)), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return float64(dec.atoi64(start, end)), nil -} - -// DecodeFloat32 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the float32 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeFloat32(v *float32) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeFloat32(v) -} -func (dec *Decoder) decodeFloat32(v *float32) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getFloat32() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getFloat32Negative() - if err != nil { - return err - } - *v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeFloat32Null(v **float32) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getFloat32() - if err != nil { - return err - } - if *v == nil { - *v = new(float32) - } - **v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getFloat32Negative() - if err != nil { - return err - } - if *v == nil { - *v = new(float32) - } - **v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getFloat32Negative() (float32, error) { - // look for following numbers - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - return dec.getFloat32() - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getFloat32() (float32, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case '.': - // we get part before decimal as integer - beforeDecimal := dec.atoi64(start, end) - // then we get part after decimal as integer - start = j + 1 - // get number after the decimal point - // multiple the before decimal point portion by 10 using bitwise - for i := j + 1; i < dec.length || dec.read(); i++ { - c := dec.data[i] - if isDigit(c) { - end = i - // multiply the before decimal point portion by 10 using bitwise - // make sure it desn't overflow - if end-start < 9 { - beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) - } - continue - } else if (c == 'e' || c == 'E') && j < i-1 { - // we get the number before decimal - var afterDecimal int64 - expI := end - start + 2 - // if exp is too long, it means number is too long, just truncate the number - if expI >= 12 || expI < 0 { - expI = 10 - afterDecimal = dec.atoi64(start, start+expI-2) - } else { - afterDecimal = dec.atoi64(start, end) - } - dec.cursor = i + 1 - pow := pow10uint64[expI] - // then we add both integers - // then we divide the number by the power found - floatVal := float32(beforeDecimal+afterDecimal) / float32(pow) - exp, err := dec.getExponent() - if err != nil { - return 0, err - } - pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs - if pExp >= int64(len(pow10uint64)) || pExp < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - // if exponent is negative - if exp < 0 { - return float32(floatVal) * (1 / float32(pow10uint64[pExp])), nil - } - return float32(floatVal) * float32(pow10uint64[pExp]), nil - } - dec.cursor = i - break - } - if end >= dec.length || end < start { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - // then we add both integers - // then we divide the number by the power found - var afterDecimal int64 - expI := end - start + 2 - // if exp is too long, it means number is too long, just truncate the number - if expI >= 12 || expI < 0 { - expI = 10 - afterDecimal = dec.atoi64(start, start+expI-2) - } else { - // then we add both integers - // then we divide the number by the power found - afterDecimal = dec.atoi64(start, end) - } - pow := pow10uint64[expI] - return float32(beforeDecimal+afterDecimal) / float32(pow), nil - case 'e', 'E': - dec.cursor = j + 1 - // we get part before decimal as integer - beforeDecimal := dec.atoi64(start, end) - // get exponent - exp, err := dec.getExponent() - if err != nil { - return 0, err - } - pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 - if pExp >= int64(len(pow10uint64)) || pExp < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - // if exponent is negative - if exp < 0 { - return float32(beforeDecimal) * (1 / float32(pow10uint64[pExp])), nil - } - return float32(beforeDecimal) * float32(pow10uint64[pExp]), nil - case ' ', '\n', '\t', '\r', ',', '}', ']': // does not have decimal - dec.cursor = j - return float32(dec.atoi64(start, end)), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return float32(dec.atoi64(start, end)), nil -} - -// Add Values functions - -// AddFloat decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddFloat(v *float64) error { - return dec.Float64(v) -} - -// AddFloatNull decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddFloatNull(v **float64) error { - return dec.Float64Null(v) -} - -// AddFloat64 decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddFloat64(v *float64) error { - return dec.Float64(v) -} - -// AddFloat64Null decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddFloat64Null(v **float64) error { - return dec.Float64Null(v) -} - -// AddFloat32 decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddFloat32(v *float32) error { - return dec.Float32(v) -} - -// AddFloat32Null decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddFloat32Null(v **float32) error { - return dec.Float32Null(v) -} - -// Float decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Float(v *float64) error { - return dec.Float64(v) -} - -// FloatNull decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) FloatNull(v **float64) error { - return dec.Float64Null(v) -} - -// Float64 decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Float64(v *float64) error { - err := dec.decodeFloat64(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Float64Null decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Float64Null(v **float64) error { - err := dec.decodeFloat64Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Float32 decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Float32(v *float32) error { - err := dec.decodeFloat32(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Float32Null decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Float32Null(v **float32) error { - err := dec.decodeFloat32Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_number_int.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_number_int.go deleted file mode 100644 index 8429049fb..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_number_int.go +++ /dev/null @@ -1,1338 +0,0 @@ -package gojay - -import ( - "fmt" - "math" -) - -// DecodeInt reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeInt(v *int) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeInt(v) -} -func (dec *Decoder) decodeInt(v *int) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - // we don't look for 0 as leading zeros are invalid per RFC - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt64() - if err != nil { - return err - } - *v = int(val) - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt64Negative() - if err != nil { - return err - } - *v = -int(val) - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to int, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) decodeIntNull(v **int) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - // we don't look for 0 as leading zeros are invalid per RFC - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt64() - if err != nil { - return err - } - if *v == nil { - *v = new(int) - } - **v = int(val) - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt64Negative() - if err != nil { - return err - } - if *v == nil { - *v = new(int) - } - **v = -int(val) - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to int, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -// DecodeInt16 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int16 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeInt16(v *int16) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeInt16(v) -} -func (dec *Decoder) decodeInt16(v *int16) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - // we don't look for 0 as leading zeros are invalid per RFC - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt16() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt16Negative() - if err != nil { - return err - } - *v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeInt16Null(v **int16) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - // we don't look for 0 as leading zeros are invalid per RFC - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt16() - if err != nil { - return err - } - if *v == nil { - *v = new(int16) - } - **v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt16Negative() - if err != nil { - return err - } - if *v == nil { - *v = new(int16) - } - **v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getInt16Negative() (int16, error) { - // look for following numbers - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '1', '2', '3', '4', '5', '6', '7', '8', '9': - return dec.getInt16() - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getInt16() (int16, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case '.': - // if dot is found - // look for exponent (e,E) as exponent can change the - // way number should be parsed to int. - // if no exponent found, just unmarshal the number before decimal point - j++ - startDecimal := j - endDecimal := j - 1 - for ; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - endDecimal = j - continue - case 'e', 'E': - if startDecimal > endDecimal { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - dec.cursor = j + 1 - // can try unmarshalling to int as Exponent might change decimal number to non decimal - // let's get the float value first - // we get part before decimal as integer - beforeDecimal := dec.atoi16(start, end) - // get number after the decimal point - // multiple the before decimal point portion by 10 using bitwise - for i := startDecimal; i <= endDecimal; i++ { - beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) - } - // then we add both integers - // then we divide the number by the power found - afterDecimal := dec.atoi16(startDecimal, endDecimal) - expI := endDecimal - startDecimal + 2 - if expI >= len(pow10uint64) || expI < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - pow := pow10uint64[expI] - floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) - // we have the floating value, now multiply by the exponent - exp, err := dec.getExponent() - if err != nil { - return 0, err - } - pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs - if pExp >= int64(len(pow10uint64)) || pExp < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - val := floatVal * float64(pow10uint64[pExp]) - return int16(val), nil - case ' ', '\t', '\n', ',', ']', '}': - dec.cursor = j - return dec.atoi16(start, end), nil - default: - dec.cursor = j - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return dec.atoi16(start, end), nil - case 'e', 'E': - // get init n - dec.cursor = j + 1 - return dec.getInt16WithExp(dec.atoi16(start, end)) - case ' ', '\n', '\t', '\r', ',', '}', ']': - dec.cursor = j - return dec.atoi16(start, end), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoi16(start, end), nil -} - -func (dec *Decoder) getInt16WithExp(init int16) (int16, error) { - var exp uint16 - var sign = int16(1) - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '+': - continue - case '-': - sign = -1 - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint16(digits[dec.data[dec.cursor]]) - exp = (exp << 3) + (exp << 1) + uintv - dec.cursor++ - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint16(digits[dec.data[dec.cursor]]) - exp = (exp << 3) + (exp << 1) + uintv - case ' ', '\t', '\n', '}', ',', ']': - exp = exp + 1 - if exp >= uint16(len(pow10uint64)) { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - if sign == -1 { - return init * (1 / int16(pow10uint64[exp])), nil - } - return init * int16(pow10uint64[exp]), nil - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - exp = exp + 1 - if exp >= uint16(len(pow10uint64)) { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - if sign == -1 { - return init * (1 / int16(pow10uint64[exp])), nil - } - return init * int16(pow10uint64[exp]), nil - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -// DecodeInt8 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int8 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeInt8(v *int8) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeInt8(v) -} -func (dec *Decoder) decodeInt8(v *int8) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - // we don't look for 0 as leading zeros are invalid per RFC - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt8() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt8Negative() - if err != nil { - return err - } - *v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeInt8Null(v **int8) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - // we don't look for 0 as leading zeros are invalid per RFC - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt8() - if err != nil { - return err - } - if *v == nil { - *v = new(int8) - } - **v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt8Negative() - if err != nil { - return err - } - if *v == nil { - *v = new(int8) - } - **v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getInt8Negative() (int8, error) { - // look for following numbers - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '1', '2', '3', '4', '5', '6', '7', '8', '9': - return dec.getInt8() - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getInt8() (int8, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case '.': - // if dot is found - // look for exponent (e,E) as exponent can change the - // way number should be parsed to int. - // if no exponent found, just unmarshal the number before decimal point - j++ - startDecimal := j - endDecimal := j - 1 - for ; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - endDecimal = j - continue - case 'e', 'E': - if startDecimal > endDecimal { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - dec.cursor = j + 1 - // can try unmarshalling to int as Exponent might change decimal number to non decimal - // let's get the float value first - // we get part before decimal as integer - beforeDecimal := dec.atoi8(start, end) - // get number after the decimal point - // multiple the before decimal point portion by 10 using bitwise - for i := startDecimal; i <= endDecimal; i++ { - beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) - } - // then we add both integers - // then we divide the number by the power found - afterDecimal := dec.atoi8(startDecimal, endDecimal) - expI := endDecimal - startDecimal + 2 - if expI >= len(pow10uint64) || expI < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - pow := pow10uint64[expI] - floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) - // we have the floating value, now multiply by the exponent - exp, err := dec.getExponent() - if err != nil { - return 0, err - } - pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs - if pExp >= int64(len(pow10uint64)) || pExp < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - val := floatVal * float64(pow10uint64[pExp]) - return int8(val), nil - case ' ', '\t', '\n', ',', ']', '}': - dec.cursor = j - return dec.atoi8(start, end), nil - default: - dec.cursor = j - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return dec.atoi8(start, end), nil - case 'e', 'E': - // get init n - dec.cursor = j + 1 - return dec.getInt8WithExp(dec.atoi8(start, end)) - case ' ', '\n', '\t', '\r', ',', '}', ']': - dec.cursor = j - return dec.atoi8(start, end), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoi8(start, end), nil -} - -func (dec *Decoder) getInt8WithExp(init int8) (int8, error) { - var exp uint8 - var sign = int8(1) - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '+': - continue - case '-': - sign = -1 - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint8(digits[dec.data[dec.cursor]]) - exp = (exp << 3) + (exp << 1) + uintv - dec.cursor++ - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint8(digits[dec.data[dec.cursor]]) - exp = (exp << 3) + (exp << 1) + uintv - case ' ', '\t', '\n', '}', ',', ']': - if exp+1 >= uint8(len(pow10uint64)) { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - if sign == -1 { - return init * (1 / int8(pow10uint64[exp+1])), nil - } - return init * int8(pow10uint64[exp+1]), nil - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - if exp+1 >= uint8(len(pow10uint64)) { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - if sign == -1 { - return init * (1 / int8(pow10uint64[exp+1])), nil - } - return init * int8(pow10uint64[exp+1]), nil - default: - dec.err = dec.raiseInvalidJSONErr(dec.cursor) - return 0, dec.err - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -// DecodeInt32 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int32 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeInt32(v *int32) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeInt32(v) -} -func (dec *Decoder) decodeInt32(v *int32) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt32() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt32Negative() - if err != nil { - return err - } - *v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeInt32Null(v **int32) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt32() - if err != nil { - return err - } - if *v == nil { - *v = new(int32) - } - **v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt32Negative() - if err != nil { - return err - } - if *v == nil { - *v = new(int32) - } - **v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getInt32Negative() (int32, error) { - // look for following numbers - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '1', '2', '3', '4', '5', '6', '7', '8', '9': - return dec.getInt32() - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getInt32() (int32, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case '.': - // if dot is found - // look for exponent (e,E) as exponent can change the - // way number should be parsed to int. - // if no exponent found, just unmarshal the number before decimal point - j++ - startDecimal := j - endDecimal := j - 1 - for ; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - endDecimal = j - continue - case 'e', 'E': - // if eg 1.E - if startDecimal > endDecimal { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - dec.cursor = j + 1 - // can try unmarshalling to int as Exponent might change decimal number to non decimal - // let's get the float value first - // we get part before decimal as integer - beforeDecimal := dec.atoi64(start, end) - // get number after the decimal point - // multiple the before decimal point portion by 10 using bitwise - for i := startDecimal; i <= endDecimal; i++ { - beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) - } - // then we add both integers - // then we divide the number by the power found - afterDecimal := dec.atoi64(startDecimal, endDecimal) - expI := endDecimal - startDecimal + 2 - if expI >= len(pow10uint64) || expI < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - pow := pow10uint64[expI] - floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) - // we have the floating value, now multiply by the exponent - exp, err := dec.getExponent() - if err != nil { - return 0, err - } - pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs - if pExp >= int64(len(pow10uint64)) || pExp < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - val := floatVal * float64(pow10uint64[pExp]) - return int32(val), nil - case ' ', '\t', '\n', ',', ']', '}': - dec.cursor = j - return dec.atoi32(start, end), nil - default: - dec.cursor = j - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return dec.atoi32(start, end), nil - case 'e', 'E': - // get init n - dec.cursor = j + 1 - return dec.getInt32WithExp(dec.atoi32(start, end)) - case ' ', '\n', '\t', '\r', ',', '}', ']': - dec.cursor = j - return dec.atoi32(start, end), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoi32(start, end), nil -} - -func (dec *Decoder) getInt32WithExp(init int32) (int32, error) { - var exp uint32 - var sign = int32(1) - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '+': - continue - case '-': - sign = -1 - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint32(digits[dec.data[dec.cursor]]) - exp = (exp << 3) + (exp << 1) + uintv - dec.cursor++ - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint32(digits[dec.data[dec.cursor]]) - exp = (exp << 3) + (exp << 1) + uintv - case ' ', '\t', '\n', '}', ',', ']': - if exp+1 >= uint32(len(pow10uint64)) { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - if sign == -1 { - return init * (1 / int32(pow10uint64[exp+1])), nil - } - return init * int32(pow10uint64[exp+1]), nil - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - if exp+1 >= uint32(len(pow10uint64)) { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - if sign == -1 { - return init * (1 / int32(pow10uint64[exp+1])), nil - } - return init * int32(pow10uint64[exp+1]), nil - default: - dec.err = dec.raiseInvalidJSONErr(dec.cursor) - return 0, dec.err - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -// DecodeInt64 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int64 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeInt64(v *int64) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeInt64(v) -} - -func (dec *Decoder) decodeInt64(v *int64) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt64() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt64Negative() - if err != nil { - return err - } - *v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeInt64Null(v **int64) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt64() - if err != nil { - return err - } - if *v == nil { - *v = new(int64) - } - **v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt64Negative() - if err != nil { - return err - } - if *v == nil { - *v = new(int64) - } - **v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getInt64Negative() (int64, error) { - // look for following numbers - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '1', '2', '3', '4', '5', '6', '7', '8', '9': - return dec.getInt64() - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getInt64() (int64, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case ' ', '\t', '\n', ',', '}', ']': - dec.cursor = j - return dec.atoi64(start, end), nil - case '.': - // if dot is found - // look for exponent (e,E) as exponent can change the - // way number should be parsed to int. - // if no exponent found, just unmarshal the number before decimal point - j++ - startDecimal := j - endDecimal := j - 1 - for ; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - endDecimal = j - continue - case 'e', 'E': - // if eg 1.E - if startDecimal > endDecimal { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - dec.cursor = j + 1 - // can try unmarshalling to int as Exponent might change decimal number to non decimal - // let's get the float value first - // we get part before decimal as integer - beforeDecimal := dec.atoi64(start, end) - // get number after the decimal point - // multiple the before decimal point portion by 10 using bitwise - for i := startDecimal; i <= endDecimal; i++ { - beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) - } - // then we add both integers - // then we divide the number by the power found - afterDecimal := dec.atoi64(startDecimal, endDecimal) - expI := endDecimal - startDecimal + 2 - if expI >= len(pow10uint64) || expI < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - pow := pow10uint64[expI] - floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) - // we have the floating value, now multiply by the exponent - exp, err := dec.getExponent() - if err != nil { - return 0, err - } - pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs - if pExp >= int64(len(pow10uint64)) || pExp < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - val := floatVal * float64(pow10uint64[pExp]) - return int64(val), nil - case ' ', '\t', '\n', ',', ']', '}': - dec.cursor = j - return dec.atoi64(start, end), nil - default: - dec.cursor = j - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return dec.atoi64(start, end), nil - case 'e', 'E': - // get init n - dec.cursor = j + 1 - return dec.getInt64WithExp(dec.atoi64(start, end)) - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoi64(start, end), nil -} - -func (dec *Decoder) getInt64WithExp(init int64) (int64, error) { - var exp uint64 - var sign = int64(1) - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '+': - continue - case '-': - sign = -1 - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint64(digits[dec.data[dec.cursor]]) - exp = (exp << 3) + (exp << 1) + uintv - dec.cursor++ - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint64(digits[dec.data[dec.cursor]]) - exp = (exp << 3) + (exp << 1) + uintv - case ' ', '\t', '\n', '}', ',', ']': - if exp+1 >= uint64(len(pow10uint64)) { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - if sign == -1 { - return init * (1 / int64(pow10uint64[exp+1])), nil - } - return init * int64(pow10uint64[exp+1]), nil - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - if exp+1 >= uint64(len(pow10uint64)) { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - if sign == -1 { - return init * (1 / int64(pow10uint64[exp+1])), nil - } - return init * int64(pow10uint64[exp+1]), nil - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) atoi64(start, end int) int64 { - var ll = end + 1 - start - var val = int64(digits[dec.data[start]]) - end = end + 1 - if ll < maxInt64Length { - for i := start + 1; i < end; i++ { - intv := int64(digits[dec.data[i]]) - val = (val << 3) + (val << 1) + intv - } - return val - } else if ll == maxInt64Length { - for i := start + 1; i < end; i++ { - intv := int64(digits[dec.data[i]]) - if val > maxInt64toMultiply { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val = (val << 3) + (val << 1) - if math.MaxInt64-val < intv { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val += intv - } - } else { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - return val -} - -func (dec *Decoder) atoi32(start, end int) int32 { - var ll = end + 1 - start - var val = int32(digits[dec.data[start]]) - end = end + 1 - - // overflowing - if ll < maxInt32Length { - for i := start + 1; i < end; i++ { - intv := int32(digits[dec.data[i]]) - val = (val << 3) + (val << 1) + intv - } - } else if ll == maxInt32Length { - for i := start + 1; i < end; i++ { - intv := int32(digits[dec.data[i]]) - if val > maxInt32toMultiply { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val = (val << 3) + (val << 1) - if math.MaxInt32-val < intv { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val += intv - } - } else { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - return val -} - -func (dec *Decoder) atoi16(start, end int) int16 { - var ll = end + 1 - start - var val = int16(digits[dec.data[start]]) - end = end + 1 - // overflowing - if ll < maxInt16Length { - for i := start + 1; i < end; i++ { - intv := int16(digits[dec.data[i]]) - val = (val << 3) + (val << 1) + intv - } - } else if ll == maxInt16Length { - for i := start + 1; i < end; i++ { - intv := int16(digits[dec.data[i]]) - if val > maxInt16toMultiply { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val = (val << 3) + (val << 1) - if math.MaxInt16-val < intv { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val += intv - } - } else { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - return val -} - -func (dec *Decoder) atoi8(start, end int) int8 { - var ll = end + 1 - start - var val = int8(digits[dec.data[start]]) - end = end + 1 - // overflowing - if ll < maxInt8Length { - for i := start + 1; i < end; i++ { - intv := int8(digits[dec.data[i]]) - val = (val << 3) + (val << 1) + intv - } - } else if ll == maxInt8Length { - for i := start + 1; i < end; i++ { - intv := int8(digits[dec.data[i]]) - if val > maxInt8toMultiply { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val = (val << 3) + (val << 1) - if math.MaxInt8-val < intv { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val += intv - } - } else { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - return val -} - -// Add Values functions - -// AddInt decodes the JSON value within an object or an array to an *int. -// If next key value overflows int, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddInt(v *int) error { - return dec.Int(v) -} - -// AddIntNull decodes the JSON value within an object or an array to an *int. -// If next key value overflows int, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddIntNull(v **int) error { - return dec.IntNull(v) -} - -// AddInt8 decodes the JSON value within an object or an array to an *int. -// If next key value overflows int8, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddInt8(v *int8) error { - return dec.Int8(v) -} - -// AddInt8Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows int8, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddInt8Null(v **int8) error { - return dec.Int8Null(v) -} - -// AddInt16 decodes the JSON value within an object or an array to an *int. -// If next key value overflows int16, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddInt16(v *int16) error { - return dec.Int16(v) -} - -// AddInt16Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows int16, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddInt16Null(v **int16) error { - return dec.Int16Null(v) -} - -// AddInt32 decodes the JSON value within an object or an array to an *int. -// If next key value overflows int32, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddInt32(v *int32) error { - return dec.Int32(v) -} - -// AddInt32Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows int32, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddInt32Null(v **int32) error { - return dec.Int32Null(v) -} - -// AddInt64 decodes the JSON value within an object or an array to an *int. -// If next key value overflows int64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddInt64(v *int64) error { - return dec.Int64(v) -} - -// AddInt64Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows int64, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddInt64Null(v **int64) error { - return dec.Int64Null(v) -} - -// Int decodes the JSON value within an object or an array to an *int. -// If next key value overflows int, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int(v *int) error { - err := dec.decodeInt(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// IntNull decodes the JSON value within an object or an array to an *int. -// If next key value overflows int, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) IntNull(v **int) error { - err := dec.decodeIntNull(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Int8 decodes the JSON value within an object or an array to an *int. -// If next key value overflows int8, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int8(v *int8) error { - err := dec.decodeInt8(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Int8Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows int8, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int8Null(v **int8) error { - err := dec.decodeInt8Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Int16 decodes the JSON value within an object or an array to an *int. -// If next key value overflows int16, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int16(v *int16) error { - err := dec.decodeInt16(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Int16Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows int16, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int16Null(v **int16) error { - err := dec.decodeInt16Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Int32 decodes the JSON value within an object or an array to an *int. -// If next key value overflows int32, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int32(v *int32) error { - err := dec.decodeInt32(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Int32Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows int32, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int32Null(v **int32) error { - err := dec.decodeInt32Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Int64 decodes the JSON value within an object or an array to an *int. -// If next key value overflows int64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int64(v *int64) error { - err := dec.decodeInt64(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Int64Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows int64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int64Null(v **int64) error { - err := dec.decodeInt64Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_number_uint.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_number_uint.go deleted file mode 100644 index b57ef7ab6..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_number_uint.go +++ /dev/null @@ -1,715 +0,0 @@ -package gojay - -import ( - "math" -) - -// DecodeUint8 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint8 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeUint8(v *uint8) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeUint8(v) -} - -func (dec *Decoder) decodeUint8(v *uint8) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint8() - if err != nil { - return err - } - *v = val - return nil - case '-': // if negative, we just set it to 0 and set error - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeUint8Null(v **uint8) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint8() - if err != nil { - return err - } - if *v == nil { - *v = new(uint8) - } - **v = val - return nil - case '-': // if negative, we just set it to 0 and set error - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - if *v == nil { - *v = new(uint8) - } - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getUint8() (uint8, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case ' ', '\n', '\t', '\r': - continue - case '.', ',', '}', ']': - dec.cursor = j - return dec.atoui8(start, end), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoui8(start, end), nil -} - -// DecodeUint16 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint16 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeUint16(v *uint16) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeUint16(v) -} - -func (dec *Decoder) decodeUint16(v *uint16) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint16() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeUint16Null(v **uint16) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint16() - if err != nil { - return err - } - if *v == nil { - *v = new(uint16) - } - **v = val - return nil - case '-': - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - if *v == nil { - *v = new(uint16) - } - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getUint16() (uint16, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case ' ', '\n', '\t', '\r': - continue - case '.', ',', '}', ']': - dec.cursor = j - return dec.atoui16(start, end), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoui16(start, end), nil -} - -// DecodeUint32 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint32 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeUint32(v *uint32) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeUint32(v) -} - -func (dec *Decoder) decodeUint32(v *uint32) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint32() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeUint32Null(v **uint32) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint32() - if err != nil { - return err - } - if *v == nil { - *v = new(uint32) - } - **v = val - return nil - case '-': - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - if *v == nil { - *v = new(uint32) - } - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getUint32() (uint32, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case ' ', '\n', '\t', '\r': - continue - case '.', ',', '}', ']': - dec.cursor = j - return dec.atoui32(start, end), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoui32(start, end), nil -} - -// DecodeUint64 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint64 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeUint64(v *uint64) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeUint64(v) -} -func (dec *Decoder) decodeUint64(v *uint64) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint64() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeUint64Null(v **uint64) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint64() - if err != nil { - return err - } - if *v == nil { - *v = new(uint64) - } - **v = val - return nil - case '-': - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - if *v == nil { - *v = new(uint64) - } - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getUint64() (uint64, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case ' ', '\n', '\t', '\r', '.', ',', '}', ']': - dec.cursor = j - return dec.atoui64(start, end), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoui64(start, end), nil -} - -func (dec *Decoder) atoui64(start, end int) uint64 { - var ll = end + 1 - start - var val = uint64(digits[dec.data[start]]) - end = end + 1 - if ll < maxUint64Length { - for i := start + 1; i < end; i++ { - uintv := uint64(digits[dec.data[i]]) - val = (val << 3) + (val << 1) + uintv - } - } else if ll == maxUint64Length { - for i := start + 1; i < end; i++ { - uintv := uint64(digits[dec.data[i]]) - if val > maxUint64toMultiply { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val = (val << 3) + (val << 1) - if math.MaxUint64-val < uintv { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val += uintv - } - } else { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - return val -} - -func (dec *Decoder) atoui32(start, end int) uint32 { - var ll = end + 1 - start - var val uint32 - val = uint32(digits[dec.data[start]]) - end = end + 1 - if ll < maxUint32Length { - for i := start + 1; i < end; i++ { - uintv := uint32(digits[dec.data[i]]) - val = (val << 3) + (val << 1) + uintv - } - } else if ll == maxUint32Length { - for i := start + 1; i < end; i++ { - uintv := uint32(digits[dec.data[i]]) - if val > maxUint32toMultiply { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val = (val << 3) + (val << 1) - if math.MaxUint32-val < uintv { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val += uintv - } - } else if ll > maxUint32Length { - dec.err = dec.makeInvalidUnmarshalErr(val) - val = 0 - } - return val -} - -func (dec *Decoder) atoui16(start, end int) uint16 { - var ll = end + 1 - start - var val uint16 - val = uint16(digits[dec.data[start]]) - end = end + 1 - if ll < maxUint16Length { - for i := start + 1; i < end; i++ { - uintv := uint16(digits[dec.data[i]]) - val = (val << 3) + (val << 1) + uintv - } - } else if ll == maxUint16Length { - for i := start + 1; i < end; i++ { - uintv := uint16(digits[dec.data[i]]) - if val > maxUint16toMultiply { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val = (val << 3) + (val << 1) - if math.MaxUint16-val < uintv { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val += uintv - } - } else if ll > maxUint16Length { - dec.err = dec.makeInvalidUnmarshalErr(val) - val = 0 - } - return val -} - -func (dec *Decoder) atoui8(start, end int) uint8 { - var ll = end + 1 - start - var val uint8 - val = uint8(digits[dec.data[start]]) - end = end + 1 - if ll < maxUint8Length { - for i := start + 1; i < end; i++ { - uintv := uint8(digits[dec.data[i]]) - val = (val << 3) + (val << 1) + uintv - } - } else if ll == maxUint8Length { - for i := start + 1; i < end; i++ { - uintv := uint8(digits[dec.data[i]]) - if val > maxUint8toMultiply { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val = (val << 3) + (val << 1) - if math.MaxUint8-val < uintv { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val += uintv - } - } else if ll > maxUint8Length { - dec.err = dec.makeInvalidUnmarshalErr(val) - val = 0 - } - return val -} - -// Add Values functions - -// AddUint8 decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint8, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddUint8(v *uint8) error { - return dec.Uint8(v) -} - -// AddUint8Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint8, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddUint8Null(v **uint8) error { - return dec.Uint8Null(v) -} - -// AddUint16 decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint16, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddUint16(v *uint16) error { - return dec.Uint16(v) -} - -// AddUint16Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint16, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddUint16Null(v **uint16) error { - return dec.Uint16Null(v) -} - -// AddUint32 decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint32, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddUint32(v *uint32) error { - return dec.Uint32(v) -} - -// AddUint32Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint32, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddUint32Null(v **uint32) error { - return dec.Uint32Null(v) -} - -// AddUint64 decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddUint64(v *uint64) error { - return dec.Uint64(v) -} - -// AddUint64Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint64, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddUint64Null(v **uint64) error { - return dec.Uint64Null(v) -} - -// Uint8 decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint8, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Uint8(v *uint8) error { - err := dec.decodeUint8(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Uint8Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint8, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Uint8Null(v **uint8) error { - err := dec.decodeUint8Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Uint16 decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint16, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Uint16(v *uint16) error { - err := dec.decodeUint16(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Uint16Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint16, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Uint16Null(v **uint16) error { - err := dec.decodeUint16Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Uint32 decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint32, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Uint32(v *uint32) error { - err := dec.decodeUint32(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Uint32Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint32, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Uint32Null(v **uint32) error { - err := dec.decodeUint32Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Uint64 decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Uint64(v *uint64) error { - err := dec.decodeUint64(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Uint64Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Uint64Null(v **uint64) error { - err := dec.decodeUint64Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_object.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_object.go deleted file mode 100644 index 0fec9d24e..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_object.go +++ /dev/null @@ -1,407 +0,0 @@ -package gojay - -import ( - "reflect" - "unsafe" -) - -// DecodeObject reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by v. -// -// v must implement UnmarshalerJSONObject. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeObject(j UnmarshalerJSONObject) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - _, err := dec.decodeObject(j) - return err -} -func (dec *Decoder) decodeObject(j UnmarshalerJSONObject) (int, error) { - keys := j.NKeys() - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - case '{': - dec.cursor = dec.cursor + 1 - // if keys is zero we will parse all keys - // we run two loops for micro optimization - if keys == 0 { - for dec.cursor < dec.length || dec.read() { - k, done, err := dec.nextKey() - if err != nil { - return 0, err - } else if done { - return dec.cursor, nil - } - err = j.UnmarshalJSONObject(dec, k) - if err != nil { - dec.err = err - return 0, err - } else if dec.called&1 == 0 { - err := dec.skipData() - if err != nil { - return 0, err - } - } else { - dec.keysDone++ - } - dec.called &= 0 - } - } else { - for (dec.cursor < dec.length || dec.read()) && dec.keysDone < keys { - k, done, err := dec.nextKey() - if err != nil { - return 0, err - } else if done { - return dec.cursor, nil - } - err = j.UnmarshalJSONObject(dec, k) - if err != nil { - dec.err = err - return 0, err - } else if dec.called&1 == 0 { - err := dec.skipData() - if err != nil { - return 0, err - } - } else { - dec.keysDone++ - } - dec.called &= 0 - } - } - // will get to that point when keysDone is not lower than keys anymore - // in that case, we make sure cursor goes to the end of object, but we skip - // unmarshalling - if dec.child&1 != 0 { - end, err := dec.skipObject() - dec.cursor = end - return dec.cursor, err - } - return dec.cursor, nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return 0, err - } - return dec.cursor, nil - default: - // can't unmarshal to struct - dec.err = dec.makeInvalidUnmarshalErr(j) - err := dec.skipData() - if err != nil { - return 0, err - } - return dec.cursor, nil - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) decodeObjectNull(v interface{}) (int, error) { - // make sure the value is a pointer - vv := reflect.ValueOf(v) - vvt := vv.Type() - if vvt.Kind() != reflect.Ptr || vvt.Elem().Kind() != reflect.Ptr { - dec.err = ErrUnmarshalPtrExpected - return 0, dec.err - } - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - case '{': - elt := vv.Elem() - n := reflect.New(elt.Type().Elem()) - elt.Set(n) - var j UnmarshalerJSONObject - var ok bool - if j, ok = n.Interface().(UnmarshalerJSONObject); !ok { - dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONObject)(nil)) - return 0, dec.err - } - keys := j.NKeys() - dec.cursor = dec.cursor + 1 - // if keys is zero we will parse all keys - // we run two loops for micro optimization - if keys == 0 { - for dec.cursor < dec.length || dec.read() { - k, done, err := dec.nextKey() - if err != nil { - return 0, err - } else if done { - return dec.cursor, nil - } - err = j.UnmarshalJSONObject(dec, k) - if err != nil { - dec.err = err - return 0, err - } else if dec.called&1 == 0 { - err := dec.skipData() - if err != nil { - return 0, err - } - } else { - dec.keysDone++ - } - dec.called &= 0 - } - } else { - for (dec.cursor < dec.length || dec.read()) && dec.keysDone < keys { - k, done, err := dec.nextKey() - if err != nil { - return 0, err - } else if done { - return dec.cursor, nil - } - err = j.UnmarshalJSONObject(dec, k) - if err != nil { - dec.err = err - return 0, err - } else if dec.called&1 == 0 { - err := dec.skipData() - if err != nil { - return 0, err - } - } else { - dec.keysDone++ - } - dec.called &= 0 - } - } - // will get to that point when keysDone is not lower than keys anymore - // in that case, we make sure cursor goes to the end of object, but we skip - // unmarshalling - if dec.child&1 != 0 { - end, err := dec.skipObject() - dec.cursor = end - return dec.cursor, err - } - return dec.cursor, nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return 0, err - } - return dec.cursor, nil - default: - // can't unmarshal to struct - dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONObject)(nil)) - err := dec.skipData() - if err != nil { - return 0, err - } - return dec.cursor, nil - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) skipObject() (int, error) { - var objectsOpen = 1 - var objectsClosed = 0 - for j := dec.cursor; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '}': - objectsClosed++ - // everything is closed return - if objectsOpen == objectsClosed { - // add char to object data - return j + 1, nil - } - case '{': - objectsOpen++ - case '"': - j++ - var isInEscapeSeq bool - var isFirstQuote = true - for ; j < dec.length || dec.read(); j++ { - if dec.data[j] != '"' { - continue - } - if dec.data[j-1] != '\\' || (!isInEscapeSeq && !isFirstQuote) { - break - } else { - isInEscapeSeq = false - } - if isFirstQuote { - isFirstQuote = false - } - // loop backward and count how many anti slash found - // to see if string is effectively escaped - ct := 0 - for i := j - 1; i > 0; i-- { - if dec.data[i] != '\\' { - break - } - ct++ - } - // is pair number of slashes, quote is not escaped - if ct&1 == 0 { - break - } - isInEscapeSeq = true - } - default: - continue - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) nextKey() (string, bool, error) { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - case '"': - dec.cursor = dec.cursor + 1 - start, end, err := dec.getString() - if err != nil { - return "", false, err - } - var found byte - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - if dec.data[dec.cursor] == ':' { - found |= 1 - break - } - } - if found&1 != 0 { - dec.cursor++ - d := dec.data[start : end-1] - return *(*string)(unsafe.Pointer(&d)), false, nil - } - return "", false, dec.raiseInvalidJSONErr(dec.cursor) - case '}': - dec.cursor = dec.cursor + 1 - return "", true, nil - default: - // can't unmarshall to struct - return "", false, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return "", false, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) skipData() error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - // is null - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - case 't': - dec.cursor++ - err := dec.assertTrue() - if err != nil { - return err - } - return nil - // is false - case 'f': - dec.cursor++ - err := dec.assertFalse() - if err != nil { - return err - } - return nil - // is an object - case '{': - dec.cursor = dec.cursor + 1 - end, err := dec.skipObject() - dec.cursor = end - return err - // is string - case '"': - dec.cursor = dec.cursor + 1 - err := dec.skipString() - return err - // is array - case '[': - dec.cursor = dec.cursor + 1 - end, err := dec.skipArray() - dec.cursor = end - return err - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': - end, err := dec.skipNumber() - dec.cursor = end - return err - } - return dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -// DecodeObjectFunc is a func type implementing UnmarshalerJSONObject. -// Use it to cast a `func(*Decoder, k string) error` to Unmarshal an object on the fly. -type DecodeObjectFunc func(*Decoder, string) error - -// UnmarshalJSONObject implements UnmarshalerJSONObject. -func (f DecodeObjectFunc) UnmarshalJSONObject(dec *Decoder, k string) error { - return f(dec, k) -} - -// NKeys implements UnmarshalerJSONObject. -func (f DecodeObjectFunc) NKeys() int { - return 0 -} - -// Add Values functions - -// AddObject decodes the JSON value within an object or an array to a UnmarshalerJSONObject. -func (dec *Decoder) AddObject(v UnmarshalerJSONObject) error { - return dec.Object(v) -} - -// AddObjectNull decodes the JSON value within an object or an array to a UnmarshalerJSONObject. -func (dec *Decoder) AddObjectNull(v interface{}) error { - return dec.ObjectNull(v) -} - -// Object decodes the JSON value within an object or an array to a UnmarshalerJSONObject. -func (dec *Decoder) Object(value UnmarshalerJSONObject) error { - initialKeysDone := dec.keysDone - initialChild := dec.child - dec.keysDone = 0 - dec.called = 0 - dec.child |= 1 - newCursor, err := dec.decodeObject(value) - if err != nil { - return err - } - dec.cursor = newCursor - dec.keysDone = initialKeysDone - dec.child = initialChild - dec.called |= 1 - return nil -} - -// ObjectNull decodes the JSON value within an object or an array to a UnmarshalerJSONObject. -// v should be a pointer to an UnmarshalerJSONObject, -// if `null` value is encountered in JSON, it will leave the value v untouched, -// else it will create a new instance of the UnmarshalerJSONObject behind v. -func (dec *Decoder) ObjectNull(v interface{}) error { - initialKeysDone := dec.keysDone - initialChild := dec.child - dec.keysDone = 0 - dec.called = 0 - dec.child |= 1 - newCursor, err := dec.decodeObjectNull(v) - if err != nil { - return err - } - dec.cursor = newCursor - dec.keysDone = initialKeysDone - dec.child = initialChild - dec.called |= 1 - return nil -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_pool.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_pool.go deleted file mode 100644 index 68c57138a..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_pool.go +++ /dev/null @@ -1,64 +0,0 @@ -package gojay - -import ( - "io" - "sync" -) - -var decPool = sync.Pool{ - New: newDecoderPool, -} - -func init() { - for i := 0; i < 32; i++ { - decPool.Put(NewDecoder(nil)) - } -} - -// NewDecoder returns a new decoder. -// It takes an io.Reader implementation as data input. -func NewDecoder(r io.Reader) *Decoder { - return &Decoder{ - called: 0, - cursor: 0, - keysDone: 0, - err: nil, - r: r, - data: make([]byte, 512), - length: 0, - isPooled: 0, - } -} -func newDecoderPool() interface{} { - return NewDecoder(nil) -} - -// BorrowDecoder borrows a Decoder from the pool. -// It takes an io.Reader implementation as data input. -// -// In order to benefit from the pool, a borrowed decoder must be released after usage. -func BorrowDecoder(r io.Reader) *Decoder { - return borrowDecoder(r, 512) -} -func borrowDecoder(r io.Reader, bufSize int) *Decoder { - dec := decPool.Get().(*Decoder) - dec.called = 0 - dec.keysDone = 0 - dec.cursor = 0 - dec.err = nil - dec.r = r - dec.length = 0 - dec.isPooled = 0 - if bufSize > 0 { - dec.data = make([]byte, bufSize) - } - return dec -} - -// Release sends back a Decoder to the pool. -// If a decoder is used after calling Release -// a panic will be raised with an InvalidUsagePooledDecoderError error. -func (dec *Decoder) Release() { - dec.isPooled = 1 - decPool.Put(dec) -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_slice.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_slice.go deleted file mode 100644 index dbbb4bf3a..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_slice.go +++ /dev/null @@ -1,89 +0,0 @@ -package gojay - -// AddSliceString unmarshals the next JSON array of strings to the given *[]string s -func (dec *Decoder) AddSliceString(s *[]string) error { - return dec.SliceString(s) -} - -// SliceString unmarshals the next JSON array of strings to the given *[]string s -func (dec *Decoder) SliceString(s *[]string) error { - err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error { - var str string - if err := dec.String(&str); err != nil { - return err - } - *s = append(*s, str) - return nil - })) - - if err != nil { - return err - } - return nil -} - -// AddSliceInt unmarshals the next JSON array of integers to the given *[]int s -func (dec *Decoder) AddSliceInt(s *[]int) error { - return dec.SliceInt(s) -} - -// SliceInt unmarshals the next JSON array of integers to the given *[]int s -func (dec *Decoder) SliceInt(s *[]int) error { - err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error { - var i int - if err := dec.Int(&i); err != nil { - return err - } - *s = append(*s, i) - return nil - })) - - if err != nil { - return err - } - return nil -} - -// AddFloat64 unmarshals the next JSON array of floats to the given *[]float64 s -func (dec *Decoder) AddSliceFloat64(s *[]float64) error { - return dec.SliceFloat64(s) -} - -// SliceFloat64 unmarshals the next JSON array of floats to the given *[]float64 s -func (dec *Decoder) SliceFloat64(s *[]float64) error { - err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error { - var i float64 - if err := dec.Float64(&i); err != nil { - return err - } - *s = append(*s, i) - return nil - })) - - if err != nil { - return err - } - return nil -} - -// AddBool unmarshals the next JSON array of boolegers to the given *[]bool s -func (dec *Decoder) AddSliceBool(s *[]bool) error { - return dec.SliceBool(s) -} - -// SliceBool unmarshals the next JSON array of boolegers to the given *[]bool s -func (dec *Decoder) SliceBool(s *[]bool) error { - err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error { - var b bool - if err := dec.Bool(&b); err != nil { - return err - } - *s = append(*s, b) - return nil - })) - - if err != nil { - return err - } - return nil -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_sqlnull.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_sqlnull.go deleted file mode 100644 index c25549f52..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_sqlnull.go +++ /dev/null @@ -1,157 +0,0 @@ -package gojay - -import "database/sql" - -// DecodeSQLNullString decodes a sql.NullString -func (dec *Decoder) DecodeSQLNullString(v *sql.NullString) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeSQLNullString(v) -} - -func (dec *Decoder) decodeSQLNullString(v *sql.NullString) error { - var str string - if err := dec.decodeString(&str); err != nil { - return err - } - v.String = str - v.Valid = true - return nil -} - -// DecodeSQLNullInt64 decodes a sql.NullInt64 -func (dec *Decoder) DecodeSQLNullInt64(v *sql.NullInt64) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeSQLNullInt64(v) -} - -func (dec *Decoder) decodeSQLNullInt64(v *sql.NullInt64) error { - var i int64 - if err := dec.decodeInt64(&i); err != nil { - return err - } - v.Int64 = i - v.Valid = true - return nil -} - -// DecodeSQLNullFloat64 decodes a sql.NullString with the given format -func (dec *Decoder) DecodeSQLNullFloat64(v *sql.NullFloat64) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeSQLNullFloat64(v) -} - -func (dec *Decoder) decodeSQLNullFloat64(v *sql.NullFloat64) error { - var i float64 - if err := dec.decodeFloat64(&i); err != nil { - return err - } - v.Float64 = i - v.Valid = true - return nil -} - -// DecodeSQLNullBool decodes a sql.NullString with the given format -func (dec *Decoder) DecodeSQLNullBool(v *sql.NullBool) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeSQLNullBool(v) -} - -func (dec *Decoder) decodeSQLNullBool(v *sql.NullBool) error { - var b bool - if err := dec.decodeBool(&b); err != nil { - return err - } - v.Bool = b - v.Valid = true - return nil -} - -// Add Values functions - -// AddSQLNullString decodes the JSON value within an object or an array to qn *sql.NullString -func (dec *Decoder) AddSQLNullString(v *sql.NullString) error { - return dec.SQLNullString(v) -} - -// SQLNullString decodes the JSON value within an object or an array to an *sql.NullString -func (dec *Decoder) SQLNullString(v *sql.NullString) error { - var b *string - if err := dec.StringNull(&b); err != nil { - return err - } - if b == nil { - v.Valid = false - } else { - v.String = *b - v.Valid = true - } - return nil -} - -// AddSQLNullInt64 decodes the JSON value within an object or an array to qn *sql.NullInt64 -func (dec *Decoder) AddSQLNullInt64(v *sql.NullInt64) error { - return dec.SQLNullInt64(v) -} - -// SQLNullInt64 decodes the JSON value within an object or an array to an *sql.NullInt64 -func (dec *Decoder) SQLNullInt64(v *sql.NullInt64) error { - var b *int64 - if err := dec.Int64Null(&b); err != nil { - return err - } - if b == nil { - v.Valid = false - } else { - v.Int64 = *b - v.Valid = true - } - return nil -} - -// AddSQLNullFloat64 decodes the JSON value within an object or an array to qn *sql.NullFloat64 -func (dec *Decoder) AddSQLNullFloat64(v *sql.NullFloat64) error { - return dec.SQLNullFloat64(v) -} - -// SQLNullFloat64 decodes the JSON value within an object or an array to an *sql.NullFloat64 -func (dec *Decoder) SQLNullFloat64(v *sql.NullFloat64) error { - var b *float64 - if err := dec.Float64Null(&b); err != nil { - return err - } - if b == nil { - v.Valid = false - } else { - v.Float64 = *b - v.Valid = true - } - return nil -} - -// AddSQLNullBool decodes the JSON value within an object or an array to an *sql.NullBool -func (dec *Decoder) AddSQLNullBool(v *sql.NullBool) error { - return dec.SQLNullBool(v) -} - -// SQLNullBool decodes the JSON value within an object or an array to an *sql.NullBool -func (dec *Decoder) SQLNullBool(v *sql.NullBool) error { - var b *bool - if err := dec.BoolNull(&b); err != nil { - return err - } - if b == nil { - v.Valid = false - } else { - v.Bool = *b - v.Valid = true - } - return nil -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_stream.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_stream.go deleted file mode 100644 index 74beee4d7..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_stream.go +++ /dev/null @@ -1,115 +0,0 @@ -package gojay - -import ( - "sync" - "time" -) - -// UnmarshalerStream is the interface to implement for a slice, an array or a slice -// to decode a line delimited JSON to. -type UnmarshalerStream interface { - UnmarshalStream(*StreamDecoder) error -} - -// Stream is a struct holding the Stream api -var Stream = stream{} - -type stream struct{} - -// A StreamDecoder reads and decodes JSON values from an input stream. -// -// It implements conext.Context and provide a channel to notify interruption. -type StreamDecoder struct { - mux sync.RWMutex - *Decoder - done chan struct{} - deadline *time.Time -} - -// DecodeStream reads the next line delimited JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by c. -// -// c must implement UnmarshalerStream. Ideally c is a channel. See example for implementation. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *StreamDecoder) DecodeStream(c UnmarshalerStream) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - if dec.r == nil { - dec.err = NoReaderError("No reader given to decode stream") - close(dec.done) - return dec.err - } - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - default: - // char is not space start reading - for dec.nextChar() != 0 { - // calling unmarshal stream - err := c.UnmarshalStream(dec) - if err != nil { - dec.err = err - close(dec.done) - return err - } - // garbage collects buffer - // we don't want the buffer to grow extensively - dec.data = dec.data[dec.cursor:] - dec.length = dec.length - dec.cursor - dec.cursor = 0 - } - // close the done channel to signal the end of the job - close(dec.done) - return nil - } - } - close(dec.done) - dec.mux.Lock() - err := dec.raiseInvalidJSONErr(dec.cursor) - dec.mux.Unlock() - return err -} - -// context.Context implementation - -// Done returns a channel that's closed when work is done. -// It implements context.Context -func (dec *StreamDecoder) Done() <-chan struct{} { - return dec.done -} - -// Deadline returns the time when work done on behalf of this context -// should be canceled. Deadline returns ok==false when no deadline is -// set. Successive calls to Deadline return the same results. -func (dec *StreamDecoder) Deadline() (time.Time, bool) { - if dec.deadline != nil { - return *dec.deadline, true - } - return time.Time{}, false -} - -// SetDeadline sets the deadline -func (dec *StreamDecoder) SetDeadline(t time.Time) { - dec.deadline = &t -} - -// Err returns nil if Done is not yet closed. -// If Done is closed, Err returns a non-nil error explaining why. -// It implements context.Context -func (dec *StreamDecoder) Err() error { - select { - case <-dec.done: - dec.mux.RLock() - defer dec.mux.RUnlock() - return dec.err - default: - return nil - } -} - -// Value implements context.Context -func (dec *StreamDecoder) Value(key interface{}) interface{} { - return nil -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_stream_pool.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_stream_pool.go deleted file mode 100644 index 8e1863b92..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_stream_pool.go +++ /dev/null @@ -1,59 +0,0 @@ -package gojay - -import ( - "io" - "sync" -) - -var streamDecPool = sync.Pool{ - New: newStreamDecoderPool, -} - -// NewDecoder returns a new StreamDecoder. -// It takes an io.Reader implementation as data input. -// It initiates the done channel returned by Done(). -func (s stream) NewDecoder(r io.Reader) *StreamDecoder { - dec := NewDecoder(r) - streamDec := &StreamDecoder{ - Decoder: dec, - done: make(chan struct{}, 1), - mux: sync.RWMutex{}, - } - return streamDec -} -func newStreamDecoderPool() interface{} { - return Stream.NewDecoder(nil) -} - -// BorrowDecoder borrows a StreamDecoder from the pool. -// It takes an io.Reader implementation as data input. -// It initiates the done channel returned by Done(). -// -// If no StreamEncoder is available in the pool, it returns a fresh one -func (s stream) BorrowDecoder(r io.Reader) *StreamDecoder { - return s.borrowDecoder(r, 512) -} - -func (s stream) borrowDecoder(r io.Reader, bufSize int) *StreamDecoder { - streamDec := streamDecPool.Get().(*StreamDecoder) - streamDec.called = 0 - streamDec.keysDone = 0 - streamDec.cursor = 0 - streamDec.err = nil - streamDec.r = r - streamDec.length = 0 - streamDec.isPooled = 0 - streamDec.done = make(chan struct{}, 1) - if bufSize > 0 { - streamDec.data = make([]byte, bufSize) - } - return streamDec -} - -// Release sends back a Decoder to the pool. -// If a decoder is used after calling Release -// a panic will be raised with an InvalidUsagePooledDecoderError error. -func (dec *StreamDecoder) Release() { - dec.isPooled = 1 - streamDecPool.Put(dec) -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_string.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_string.go deleted file mode 100644 index 694359c7b..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_string.go +++ /dev/null @@ -1,260 +0,0 @@ -package gojay - -import ( - "unsafe" -) - -// DecodeString reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the string pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeString(v *string) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeString(v) -} -func (dec *Decoder) decodeString(v *string) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - // is string - continue - case '"': - dec.cursor++ - start, end, err := dec.getString() - if err != nil { - return err - } - // we do minus one to remove the last quote - d := dec.data[start : end-1] - *v = *(*string)(unsafe.Pointer(&d)) - dec.cursor = end - return nil - // is nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return nil -} - -func (dec *Decoder) decodeStringNull(v **string) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - // is string - continue - case '"': - dec.cursor++ - start, end, err := dec.getString() - - if err != nil { - return err - } - if *v == nil { - *v = new(string) - } - // we do minus one to remove the last quote - d := dec.data[start : end-1] - **v = *(*string)(unsafe.Pointer(&d)) - dec.cursor = end - return nil - // is nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return nil -} - -func (dec *Decoder) parseEscapedString() error { - if dec.cursor >= dec.length && !dec.read() { - return dec.raiseInvalidJSONErr(dec.cursor) - } - switch dec.data[dec.cursor] { - case '"': - dec.data[dec.cursor] = '"' - case '\\': - dec.data[dec.cursor] = '\\' - case '/': - dec.data[dec.cursor] = '/' - case 'b': - dec.data[dec.cursor] = '\b' - case 'f': - dec.data[dec.cursor] = '\f' - case 'n': - dec.data[dec.cursor] = '\n' - case 'r': - dec.data[dec.cursor] = '\r' - case 't': - dec.data[dec.cursor] = '\t' - case 'u': - start := dec.cursor - dec.cursor++ - str, err := dec.parseUnicode() - if err != nil { - return err - } - diff := dec.cursor - start - dec.data = append(append(dec.data[:start-1], str...), dec.data[dec.cursor:]...) - dec.length = len(dec.data) - dec.cursor += len(str) - diff - 1 - - return nil - default: - return dec.raiseInvalidJSONErr(dec.cursor) - } - - dec.data = append(dec.data[:dec.cursor-1], dec.data[dec.cursor:]...) - dec.length-- - - // Since we've lost a character, our dec.cursor offset is now - // 1 past the escaped character which is precisely where we - // want it. - - return nil -} - -func (dec *Decoder) getString() (int, int, error) { - // extract key - var keyStart = dec.cursor - // var str *Builder - for dec.cursor < dec.length || dec.read() { - switch dec.data[dec.cursor] { - // string found - case '"': - dec.cursor = dec.cursor + 1 - return keyStart, dec.cursor, nil - // slash found - case '\\': - dec.cursor = dec.cursor + 1 - err := dec.parseEscapedString() - if err != nil { - return 0, 0, err - } - default: - dec.cursor = dec.cursor + 1 - continue - } - } - return 0, 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) skipEscapedString() error { - start := dec.cursor - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - if dec.data[dec.cursor] != '\\' { - d := dec.data[dec.cursor] - dec.cursor = dec.cursor + 1 - nSlash := dec.cursor - start - switch d { - case '"': - // nSlash must be odd - if nSlash&1 != 1 { - return dec.raiseInvalidJSONErr(dec.cursor) - } - return nil - case 'u': // is unicode, we skip the following characters and place the cursor one one byte backward to avoid it breaking when returning to skipString - if err := dec.skipString(); err != nil { - return err - } - dec.cursor-- - return nil - case 'n', 'r', 't', '/', 'f', 'b': - return nil - default: - // nSlash must be even - if nSlash&1 == 1 { - return dec.raiseInvalidJSONErr(dec.cursor) - } - return nil - } - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) skipString() error { - for dec.cursor < dec.length || dec.read() { - switch dec.data[dec.cursor] { - // found the closing quote - // let's return - case '"': - dec.cursor = dec.cursor + 1 - return nil - // solidus found start parsing an escaped string - case '\\': - dec.cursor = dec.cursor + 1 - err := dec.skipEscapedString() - if err != nil { - return err - } - default: - dec.cursor = dec.cursor + 1 - continue - } - } - return dec.raiseInvalidJSONErr(len(dec.data) - 1) -} - -// Add Values functions - -// AddString decodes the JSON value within an object or an array to a *string. -// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned. -func (dec *Decoder) AddString(v *string) error { - return dec.String(v) -} - -// AddStringNull decodes the JSON value within an object or an array to a *string. -// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddStringNull(v **string) error { - return dec.StringNull(v) -} - -// String decodes the JSON value within an object or an array to a *string. -// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned. -func (dec *Decoder) String(v *string) error { - err := dec.decodeString(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// StringNull decodes the JSON value within an object or an array to a **string. -// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) StringNull(v **string) error { - err := dec.decodeStringNull(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_string_unicode.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_string_unicode.go deleted file mode 100644 index 9e14d52b0..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_string_unicode.go +++ /dev/null @@ -1,98 +0,0 @@ -package gojay - -import ( - "unicode/utf16" - "unicode/utf8" -) - -func (dec *Decoder) getUnicode() (rune, error) { - i := 0 - r := rune(0) - for ; (dec.cursor < dec.length || dec.read()) && i < 4; dec.cursor++ { - c := dec.data[dec.cursor] - if c >= '0' && c <= '9' { - r = r*16 + rune(c-'0') - } else if c >= 'a' && c <= 'f' { - r = r*16 + rune(c-'a'+10) - } else if c >= 'A' && c <= 'F' { - r = r*16 + rune(c-'A'+10) - } else { - return 0, InvalidJSONError("Invalid unicode code point") - } - i++ - } - return r, nil -} - -func (dec *Decoder) appendEscapeChar(str []byte, c byte) ([]byte, error) { - switch c { - case 't': - str = append(str, '\t') - case 'n': - str = append(str, '\n') - case 'r': - str = append(str, '\r') - case 'b': - str = append(str, '\b') - case 'f': - str = append(str, '\f') - case '\\': - str = append(str, '\\') - default: - return nil, InvalidJSONError("Invalid JSON") - } - return str, nil -} - -func (dec *Decoder) parseUnicode() ([]byte, error) { - // get unicode after u - r, err := dec.getUnicode() - if err != nil { - return nil, err - } - // no error start making new string - str := make([]byte, 16, 16) - i := 0 - // check if code can be a surrogate utf16 - if utf16.IsSurrogate(r) { - if dec.cursor >= dec.length && !dec.read() { - return nil, dec.raiseInvalidJSONErr(dec.cursor) - } - c := dec.data[dec.cursor] - if c != '\\' { - i += utf8.EncodeRune(str, r) - return str[:i], nil - } - dec.cursor++ - if dec.cursor >= dec.length && !dec.read() { - return nil, dec.raiseInvalidJSONErr(dec.cursor) - } - c = dec.data[dec.cursor] - if c != 'u' { - i += utf8.EncodeRune(str, r) - str, err = dec.appendEscapeChar(str[:i], c) - if err != nil { - dec.err = err - return nil, err - } - i++ - dec.cursor++ - return str[:i], nil - } - dec.cursor++ - r2, err := dec.getUnicode() - if err != nil { - return nil, err - } - combined := utf16.DecodeRune(r, r2) - if combined == '\uFFFD' { - i += utf8.EncodeRune(str, r) - i += utf8.EncodeRune(str, r2) - } else { - i += utf8.EncodeRune(str, combined) - } - return str[:i], nil - } - i += utf8.EncodeRune(str, r) - return str[:i], nil -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_time.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_time.go deleted file mode 100644 index 68f906d7f..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_time.go +++ /dev/null @@ -1,53 +0,0 @@ -package gojay - -import ( - "time" -) - -// DecodeTime decodes time with the given format -func (dec *Decoder) DecodeTime(v *time.Time, format string) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeTime(v, format) -} - -func (dec *Decoder) decodeTime(v *time.Time, format string) error { - if format == time.RFC3339 { - var ej = make(EmbeddedJSON, 0, 20) - if err := dec.decodeEmbeddedJSON(&ej); err != nil { - return err - } - if err := v.UnmarshalJSON(ej); err != nil { - return err - } - return nil - } - var str string - if err := dec.decodeString(&str); err != nil { - return err - } - tt, err := time.Parse(format, str) - if err != nil { - return err - } - *v = tt - return nil -} - -// Add Values functions - -// AddTime decodes the JSON value within an object or an array to a *time.Time with the given format -func (dec *Decoder) AddTime(v *time.Time, format string) error { - return dec.Time(v, format) -} - -// Time decodes the JSON value within an object or an array to a *time.Time with the given format -func (dec *Decoder) Time(v *time.Time, format string) error { - err := dec.decodeTime(v, format) - if err != nil { - return err - } - dec.called |= 1 - return nil -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_unsafe.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_unsafe.go deleted file mode 100644 index 54448fba7..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/decode_unsafe.go +++ /dev/null @@ -1,120 +0,0 @@ -package gojay - -import ( - "fmt" -) - -// Unsafe is the structure holding the unsafe version of the API. -// The difference between unsafe api and regular api is that the regular API -// copies the buffer passed to Unmarshal functions to a new internal buffer. -// Making it safer because internally GoJay uses unsafe.Pointer to transform slice of bytes into a string. -var Unsafe = decUnsafe{} - -type decUnsafe struct{} - -func (u decUnsafe) UnmarshalJSONArray(data []byte, v UnmarshalerJSONArray) error { - dec := borrowDecoder(nil, 0) - defer dec.Release() - dec.data = data - dec.length = len(data) - _, err := dec.decodeArray(v) - return err -} - -func (u decUnsafe) UnmarshalJSONObject(data []byte, v UnmarshalerJSONObject) error { - dec := borrowDecoder(nil, 0) - defer dec.Release() - dec.data = data - dec.length = len(data) - _, err := dec.decodeObject(v) - return err -} - -func (u decUnsafe) Unmarshal(data []byte, v interface{}) error { - var err error - var dec *Decoder - switch vt := v.(type) { - case *string: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeString(vt) - case *int: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt(vt) - case *int8: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt8(vt) - case *int16: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt16(vt) - case *int32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt32(vt) - case *int64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt64(vt) - case *uint8: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint8(vt) - case *uint16: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint16(vt) - case *uint32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint32(vt) - case *uint64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint64(vt) - case *float64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeFloat64(vt) - case *float32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeFloat32(vt) - case *bool: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeBool(vt) - case UnmarshalerJSONObject: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - _, err = dec.decodeObject(vt) - case UnmarshalerJSONArray: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - _, err = dec.decodeArray(vt) - default: - return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt)) - } - defer dec.Release() - if err != nil { - return err - } - return dec.err -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode.go deleted file mode 100644 index 92edaafa0..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode.go +++ /dev/null @@ -1,202 +0,0 @@ -package gojay - -import ( - "encoding/json" - "fmt" - "io" -) - -var nullBytes = []byte("null") - -// MarshalJSONArray returns the JSON encoding of v, an implementation of MarshalerJSONArray. -// -// -// Example: -// type TestSlice []*TestStruct -// -// func (t TestSlice) MarshalJSONArray(enc *Encoder) { -// for _, e := range t { -// enc.AddObject(e) -// } -// } -// -// func main() { -// test := &TestSlice{ -// &TestStruct{123456}, -// &TestStruct{7890}, -// } -// b, _ := Marshal(test) -// fmt.Println(b) // [{"id":123456},{"id":7890}] -// } -func MarshalJSONArray(v MarshalerJSONArray) ([]byte, error) { - enc := BorrowEncoder(nil) - enc.grow(512) - enc.writeByte('[') - v.(MarshalerJSONArray).MarshalJSONArray(enc) - enc.writeByte(']') - - defer func() { - enc.buf = make([]byte, 0, 512) - enc.Release() - }() - - return enc.buf, nil -} - -// MarshalJSONObject returns the JSON encoding of v, an implementation of MarshalerJSONObject. -// -// Example: -// type Object struct { -// id int -// } -// func (s *Object) MarshalJSONObject(enc *gojay.Encoder) { -// enc.IntKey("id", s.id) -// } -// func (s *Object) IsNil() bool { -// return s == nil -// } -// -// func main() { -// test := &Object{ -// id: 123456, -// } -// b, _ := gojay.Marshal(test) -// fmt.Println(b) // {"id":123456} -// } -func MarshalJSONObject(v MarshalerJSONObject) ([]byte, error) { - enc := BorrowEncoder(nil) - enc.grow(512) - - defer func() { - enc.buf = make([]byte, 0, 512) - enc.Release() - }() - - return enc.encodeObject(v) -} - -// Marshal returns the JSON encoding of v. -// -// If v is nil, not an implementation MarshalerJSONObject or MarshalerJSONArray or not one of the following types: -// string, int, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float64, float32, bool -// Marshal returns an InvalidMarshalError. -func Marshal(v interface{}) ([]byte, error) { - return marshal(v, false) -} - -// MarshalAny returns the JSON encoding of v. -// -// If v is nil, not an implementation MarshalerJSONObject or MarshalerJSONArray or not one of the following types: -// string, int, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float64, float32, bool -// MarshalAny falls back to "json/encoding" package to marshal the value. -func MarshalAny(v interface{}) ([]byte, error) { - return marshal(v, true) -} - -func marshal(v interface{}, any bool) ([]byte, error) { - var ( - enc = BorrowEncoder(nil) - - buf []byte - err error - ) - - defer func() { - enc.buf = make([]byte, 0, 512) - enc.Release() - }() - - buf, err = func() ([]byte, error) { - switch vt := v.(type) { - case MarshalerJSONObject: - return enc.encodeObject(vt) - case MarshalerJSONArray: - return enc.encodeArray(vt) - case string: - return enc.encodeString(vt) - case bool: - return enc.encodeBool(vt) - case int: - return enc.encodeInt(vt) - case int64: - return enc.encodeInt64(vt) - case int32: - return enc.encodeInt(int(vt)) - case int16: - return enc.encodeInt(int(vt)) - case int8: - return enc.encodeInt(int(vt)) - case uint64: - return enc.encodeInt(int(vt)) - case uint32: - return enc.encodeInt(int(vt)) - case uint16: - return enc.encodeInt(int(vt)) - case uint8: - return enc.encodeInt(int(vt)) - case float64: - return enc.encodeFloat(vt) - case float32: - return enc.encodeFloat32(vt) - case *EmbeddedJSON: - return enc.encodeEmbeddedJSON(vt) - default: - if any { - return json.Marshal(vt) - } - - return nil, InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt)) - } - }() - return buf, err -} - -// MarshalerJSONObject is the interface to implement for struct to be encoded -type MarshalerJSONObject interface { - MarshalJSONObject(enc *Encoder) - IsNil() bool -} - -// MarshalerJSONArray is the interface to implement -// for a slice or an array to be encoded -type MarshalerJSONArray interface { - MarshalJSONArray(enc *Encoder) - IsNil() bool -} - -// An Encoder writes JSON values to an output stream. -type Encoder struct { - buf []byte - isPooled byte - w io.Writer - err error - hasKeys bool - keys []string -} - -// AppendBytes allows a modular usage by appending bytes manually to the current state of the buffer. -func (enc *Encoder) AppendBytes(b []byte) { - enc.writeBytes(b) -} - -// AppendByte allows a modular usage by appending a single byte manually to the current state of the buffer. -func (enc *Encoder) AppendByte(b byte) { - enc.writeByte(b) -} - -// Buf returns the Encoder's buffer. -func (enc *Encoder) Buf() []byte { - return enc.buf -} - -// Write writes to the io.Writer and resets the buffer. -func (enc *Encoder) Write() (int, error) { - i, err := enc.w.Write(enc.buf) - enc.buf = enc.buf[:0] - return i, err -} - -func (enc *Encoder) getPreviousRune() byte { - last := len(enc.buf) - 1 - return enc.buf[last] -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_array.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_array.go deleted file mode 100644 index 5e9d49e82..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_array.go +++ /dev/null @@ -1,212 +0,0 @@ -package gojay - -// EncodeArray encodes an implementation of MarshalerJSONArray to JSON -func (enc *Encoder) EncodeArray(v MarshalerJSONArray) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeArray(v) - _, err := enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} -func (enc *Encoder) encodeArray(v MarshalerJSONArray) ([]byte, error) { - enc.grow(200) - enc.writeByte('[') - v.MarshalJSONArray(enc) - enc.writeByte(']') - return enc.buf, enc.err -} - -// AddArray adds an implementation of MarshalerJSONArray to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement Marshaler -func (enc *Encoder) AddArray(v MarshalerJSONArray) { - enc.Array(v) -} - -// AddArrayOmitEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerAddArrayOmitEmpty -func (enc *Encoder) AddArrayOmitEmpty(v MarshalerJSONArray) { - enc.ArrayOmitEmpty(v) -} - -// AddArrayNullEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement Marshaler, if v is empty, `null` will be encoded` -func (enc *Encoder) AddArrayNullEmpty(v MarshalerJSONArray) { - enc.ArrayNullEmpty(v) -} - -// AddArrayKey adds an array or slice to be encoded, must be used inside an object as it will encode a key -// value must implement Marshaler -func (enc *Encoder) AddArrayKey(key string, v MarshalerJSONArray) { - enc.ArrayKey(key, v) -} - -// AddArrayKeyOmitEmpty adds an array or slice to be encoded and skips it if it is nil. -// Must be called inside an object as it will encode a key. -func (enc *Encoder) AddArrayKeyOmitEmpty(key string, v MarshalerJSONArray) { - enc.ArrayKeyOmitEmpty(key, v) -} - -// AddArrayKeyNullEmpty adds an array or slice to be encoded and skips it if it is nil. -// Must be called inside an object as it will encode a key. `null` will be encoded` -func (enc *Encoder) AddArrayKeyNullEmpty(key string, v MarshalerJSONArray) { - enc.ArrayKeyNullEmpty(key, v) -} - -// Array adds an implementation of MarshalerJSONArray to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement Marshaler -func (enc *Encoder) Array(v MarshalerJSONArray) { - if v.IsNil() { - enc.grow(3) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeByte('[') - enc.writeByte(']') - return - } - enc.grow(100) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeByte('[') - v.MarshalJSONArray(enc) - enc.writeByte(']') -} - -// ArrayOmitEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement Marshaler -func (enc *Encoder) ArrayOmitEmpty(v MarshalerJSONArray) { - if v.IsNil() { - return - } - enc.grow(4) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeByte('[') - v.MarshalJSONArray(enc) - enc.writeByte(']') -} - -// ArrayNullEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement Marshaler -func (enc *Encoder) ArrayNullEmpty(v MarshalerJSONArray) { - enc.grow(4) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v.IsNil() { - enc.writeBytes(nullBytes) - return - } - enc.writeByte('[') - v.MarshalJSONArray(enc) - enc.writeByte(']') -} - -// ArrayKey adds an array or slice to be encoded, must be used inside an object as it will encode a key -// value must implement Marshaler -func (enc *Encoder) ArrayKey(key string, v MarshalerJSONArray) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v.IsNil() { - enc.grow(2 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyArr) - enc.writeByte(']') - return - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyArr) - v.MarshalJSONArray(enc) - enc.writeByte(']') -} - -// ArrayKeyOmitEmpty adds an array or slice to be encoded and skips if it is nil. -// Must be called inside an object as it will encode a key. -func (enc *Encoder) ArrayKeyOmitEmpty(key string, v MarshalerJSONArray) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v.IsNil() { - return - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyArr) - v.MarshalJSONArray(enc) - enc.writeByte(']') -} - -// ArrayKeyNullEmpty adds an array or slice to be encoded and encodes `null`` if it is nil. -// Must be called inside an object as it will encode a key. -func (enc *Encoder) ArrayKeyNullEmpty(key string, v MarshalerJSONArray) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - if v.IsNil() { - enc.writeBytes(nullBytes) - return - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyArr) - v.MarshalJSONArray(enc) - enc.writeByte(']') -} - -// EncodeArrayFunc is a custom func type implementing MarshaleArray. -// Use it to cast a func(*Encoder) to Marshal an object. -// -// enc := gojay.NewEncoder(io.Writer) -// enc.EncodeArray(gojay.EncodeArrayFunc(func(enc *gojay.Encoder) { -// enc.AddStringKey("hello", "world") -// })) -type EncodeArrayFunc func(*Encoder) - -// MarshalJSONArray implements MarshalerJSONArray. -func (f EncodeArrayFunc) MarshalJSONArray(enc *Encoder) { - f(enc) -} - -// IsNil implements MarshalerJSONArray. -func (f EncodeArrayFunc) IsNil() bool { - return f == nil -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_bool.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_bool.go deleted file mode 100644 index 253e03789..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_bool.go +++ /dev/null @@ -1,164 +0,0 @@ -package gojay - -import "strconv" - -// EncodeBool encodes a bool to JSON -func (enc *Encoder) EncodeBool(v bool) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeBool(v) - _, err := enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} - -// encodeBool encodes a bool to JSON -func (enc *Encoder) encodeBool(v bool) ([]byte, error) { - enc.grow(5) - if v { - enc.writeString("true") - } else { - enc.writeString("false") - } - return enc.buf, enc.err -} - -// AddBool adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddBool(v bool) { - enc.Bool(v) -} - -// AddBoolOmitEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddBoolOmitEmpty(v bool) { - enc.BoolOmitEmpty(v) -} - -// AddBoolNullEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddBoolNullEmpty(v bool) { - enc.BoolNullEmpty(v) -} - -// AddBoolKey adds a bool to be encoded, must be used inside an object as it will encode a key. -func (enc *Encoder) AddBoolKey(key string, v bool) { - enc.BoolKey(key, v) -} - -// AddBoolKeyOmitEmpty adds a bool to be encoded and skips if it is zero value. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddBoolKeyOmitEmpty(key string, v bool) { - enc.BoolKeyOmitEmpty(key, v) -} - -// AddBoolKeyNullEmpty adds a bool to be encoded and encodes `null` if it is zero value. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddBoolKeyNullEmpty(key string, v bool) { - enc.BoolKeyNullEmpty(key, v) -} - -// Bool adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Bool(v bool) { - enc.grow(5) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v { - enc.writeString("true") - } else { - enc.writeString("false") - } -} - -// BoolOmitEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) BoolOmitEmpty(v bool) { - if v == false { - return - } - enc.grow(5) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeString("true") -} - -// BoolNullEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) BoolNullEmpty(v bool) { - enc.grow(5) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v == false { - enc.writeBytes(nullBytes) - return - } - enc.writeString("true") -} - -// BoolKey adds a bool to be encoded, must be used inside an object as it will encode a key. -func (enc *Encoder) BoolKey(key string, value bool) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendBool(enc.buf, value) -} - -// BoolKeyOmitEmpty adds a bool to be encoded and skips it if it is zero value. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) BoolKeyOmitEmpty(key string, v bool) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v == false { - return - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendBool(enc.buf, v) -} - -// BoolKeyNullEmpty adds a bool to be encoded and skips it if it is zero value. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) BoolKeyNullEmpty(key string, v bool) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - if v == false { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendBool(enc.buf, v) -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_builder.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_builder.go deleted file mode 100644 index 2895ba34a..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_builder.go +++ /dev/null @@ -1,65 +0,0 @@ -package gojay - -const hex = "0123456789abcdef" - -// grow grows b's capacity, if necessary, to guarantee space for -// another n bytes. After grow(n), at least n bytes can be written to b -// without another allocation. If n is negative, grow panics. -func (enc *Encoder) grow(n int) { - if cap(enc.buf)-len(enc.buf) < n { - Buf := make([]byte, len(enc.buf), 2*cap(enc.buf)+n) - copy(Buf, enc.buf) - enc.buf = Buf - } -} - -// Write appends the contents of p to b's Buffer. -// Write always returns len(p), nil. -func (enc *Encoder) writeBytes(p []byte) { - enc.buf = append(enc.buf, p...) -} - -func (enc *Encoder) writeTwoBytes(b1 byte, b2 byte) { - enc.buf = append(enc.buf, b1, b2) -} - -// WriteByte appends the byte c to b's Buffer. -// The returned error is always nil. -func (enc *Encoder) writeByte(c byte) { - enc.buf = append(enc.buf, c) -} - -// WriteString appends the contents of s to b's Buffer. -// It returns the length of s and a nil error. -func (enc *Encoder) writeString(s string) { - enc.buf = append(enc.buf, s...) -} - -func (enc *Encoder) writeStringEscape(s string) { - l := len(s) - for i := 0; i < l; i++ { - c := s[i] - if c >= 0x20 && c != '\\' && c != '"' { - enc.writeByte(c) - continue - } - switch c { - case '\\', '"': - enc.writeTwoBytes('\\', c) - case '\n': - enc.writeTwoBytes('\\', 'n') - case '\f': - enc.writeTwoBytes('\\', 'f') - case '\b': - enc.writeTwoBytes('\\', 'b') - case '\r': - enc.writeTwoBytes('\\', 'r') - case '\t': - enc.writeTwoBytes('\\', 't') - default: - enc.writeString(`\u00`) - enc.writeTwoBytes(hex[c>>4], hex[c&0xF]) - } - continue - } -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_embedded_json.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_embedded_json.go deleted file mode 100644 index 4c99a0578..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_embedded_json.go +++ /dev/null @@ -1,93 +0,0 @@ -package gojay - -// EncodeEmbeddedJSON encodes an embedded JSON. -// is basically sets the internal buf as the value pointed by v and calls the io.Writer.Write() -func (enc *Encoder) EncodeEmbeddedJSON(v *EmbeddedJSON) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - enc.buf = *v - _, err := enc.Write() - if err != nil { - return err - } - return nil -} - -func (enc *Encoder) encodeEmbeddedJSON(v *EmbeddedJSON) ([]byte, error) { - enc.writeBytes(*v) - return enc.buf, nil -} - -// AddEmbeddedJSON adds an EmbeddedJSON to be encoded. -// -// It basically blindly writes the bytes to the final buffer. Therefore, -// it expects the JSON to be of proper format. -func (enc *Encoder) AddEmbeddedJSON(v *EmbeddedJSON) { - enc.grow(len(*v) + 4) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeBytes(*v) -} - -// AddEmbeddedJSONOmitEmpty adds an EmbeddedJSON to be encoded or skips it if nil pointer or empty. -// -// It basically blindly writes the bytes to the final buffer. Therefore, -// it expects the JSON to be of proper format. -func (enc *Encoder) AddEmbeddedJSONOmitEmpty(v *EmbeddedJSON) { - if v == nil || len(*v) == 0 { - return - } - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeBytes(*v) -} - -// AddEmbeddedJSONKey adds an EmbeddedJSON and a key to be encoded. -// -// It basically blindly writes the bytes to the final buffer. Therefore, -// it expects the JSON to be of proper format. -func (enc *Encoder) AddEmbeddedJSONKey(key string, v *EmbeddedJSON) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(len(key) + len(*v) + 5) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.writeBytes(*v) -} - -// AddEmbeddedJSONKeyOmitEmpty adds an EmbeddedJSON and a key to be encoded or skips it if nil pointer or empty. -// -// It basically blindly writes the bytes to the final buffer. Therefore, -// it expects the JSON to be of proper format. -func (enc *Encoder) AddEmbeddedJSONKeyOmitEmpty(key string, v *EmbeddedJSON) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v == nil || len(*v) == 0 { - return - } - enc.grow(len(key) + len(*v) + 5) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.writeBytes(*v) -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_interface.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_interface.go deleted file mode 100644 index c4692e5fc..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_interface.go +++ /dev/null @@ -1,173 +0,0 @@ -package gojay - -import ( - "fmt" -) - -// Encode encodes a value to JSON. -// -// If Encode cannot find a way to encode the type to JSON -// it will return an InvalidMarshalError. -func (enc *Encoder) Encode(v interface{}) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - switch vt := v.(type) { - case string: - return enc.EncodeString(vt) - case bool: - return enc.EncodeBool(vt) - case MarshalerJSONArray: - return enc.EncodeArray(vt) - case MarshalerJSONObject: - return enc.EncodeObject(vt) - case int: - return enc.EncodeInt(vt) - case int64: - return enc.EncodeInt64(vt) - case int32: - return enc.EncodeInt(int(vt)) - case int8: - return enc.EncodeInt(int(vt)) - case uint64: - return enc.EncodeUint64(vt) - case uint32: - return enc.EncodeInt(int(vt)) - case uint16: - return enc.EncodeInt(int(vt)) - case uint8: - return enc.EncodeInt(int(vt)) - case float64: - return enc.EncodeFloat(vt) - case float32: - return enc.EncodeFloat32(vt) - case *EmbeddedJSON: - return enc.EncodeEmbeddedJSON(vt) - default: - return InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt)) - } -} - -// AddInterface adds an interface{} to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddInterface(value interface{}) { - switch vt := value.(type) { - case string: - enc.AddString(vt) - case bool: - enc.AddBool(vt) - case MarshalerJSONArray: - enc.AddArray(vt) - case MarshalerJSONObject: - enc.AddObject(vt) - case int: - enc.AddInt(vt) - case int64: - enc.AddInt(int(vt)) - case int32: - enc.AddInt(int(vt)) - case int8: - enc.AddInt(int(vt)) - case uint64: - enc.AddUint64(vt) - case uint32: - enc.AddInt(int(vt)) - case uint16: - enc.AddInt(int(vt)) - case uint8: - enc.AddInt(int(vt)) - case float64: - enc.AddFloat(vt) - case float32: - enc.AddFloat32(vt) - default: - if vt != nil { - enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt)) - return - } - return - } -} - -// AddInterfaceKey adds an interface{} to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddInterfaceKey(key string, value interface{}) { - switch vt := value.(type) { - case string: - enc.AddStringKey(key, vt) - case bool: - enc.AddBoolKey(key, vt) - case MarshalerJSONArray: - enc.AddArrayKey(key, vt) - case MarshalerJSONObject: - enc.AddObjectKey(key, vt) - case int: - enc.AddIntKey(key, vt) - case int64: - enc.AddIntKey(key, int(vt)) - case int32: - enc.AddIntKey(key, int(vt)) - case int16: - enc.AddIntKey(key, int(vt)) - case int8: - enc.AddIntKey(key, int(vt)) - case uint64: - enc.AddIntKey(key, int(vt)) - case uint32: - enc.AddIntKey(key, int(vt)) - case uint16: - enc.AddIntKey(key, int(vt)) - case uint8: - enc.AddIntKey(key, int(vt)) - case float64: - enc.AddFloatKey(key, vt) - case float32: - enc.AddFloat32Key(key, vt) - default: - if vt != nil { - enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt)) - return - } - return - } -} - -// AddInterfaceKeyOmitEmpty adds an interface{} to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddInterfaceKeyOmitEmpty(key string, v interface{}) { - switch vt := v.(type) { - case string: - enc.AddStringKeyOmitEmpty(key, vt) - case bool: - enc.AddBoolKeyOmitEmpty(key, vt) - case MarshalerJSONArray: - enc.AddArrayKeyOmitEmpty(key, vt) - case MarshalerJSONObject: - enc.AddObjectKeyOmitEmpty(key, vt) - case int: - enc.AddIntKeyOmitEmpty(key, vt) - case int64: - enc.AddIntKeyOmitEmpty(key, int(vt)) - case int32: - enc.AddIntKeyOmitEmpty(key, int(vt)) - case int16: - enc.AddIntKeyOmitEmpty(key, int(vt)) - case int8: - enc.AddIntKeyOmitEmpty(key, int(vt)) - case uint64: - enc.AddIntKeyOmitEmpty(key, int(vt)) - case uint32: - enc.AddIntKeyOmitEmpty(key, int(vt)) - case uint16: - enc.AddIntKeyOmitEmpty(key, int(vt)) - case uint8: - enc.AddIntKeyOmitEmpty(key, int(vt)) - case float64: - enc.AddFloatKeyOmitEmpty(key, vt) - case float32: - enc.AddFloat32KeyOmitEmpty(key, vt) - default: - if vt != nil { - enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt)) - return - } - return - } -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_null.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_null.go deleted file mode 100644 index cec4e639a..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_null.go +++ /dev/null @@ -1,39 +0,0 @@ -package gojay - -// AddNull adds a `null` to be encoded. Must be used while encoding an array.` -func (enc *Encoder) AddNull() { - enc.Null() -} - -// Null adds a `null` to be encoded. Must be used while encoding an array.` -func (enc *Encoder) Null() { - enc.grow(5) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeBytes(nullBytes) -} - -// AddNullKey adds a `null` to be encoded. Must be used while encoding an array.` -func (enc *Encoder) AddNullKey(key string) { - enc.NullKey(key) -} - -// NullKey adds a `null` to be encoded. Must be used while encoding an array.` -func (enc *Encoder) NullKey(key string) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.writeBytes(nullBytes) -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_number.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_number.go deleted file mode 100644 index 53affb903..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_number.go +++ /dev/null @@ -1 +0,0 @@ -package gojay diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_number_float.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_number_float.go deleted file mode 100644 index b45f8442a..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_number_float.go +++ /dev/null @@ -1,368 +0,0 @@ -package gojay - -import "strconv" - -// EncodeFloat encodes a float64 to JSON -func (enc *Encoder) EncodeFloat(n float64) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeFloat(n) - _, err := enc.Write() - if err != nil { - return err - } - return nil -} - -// encodeFloat encodes a float64 to JSON -func (enc *Encoder) encodeFloat(n float64) ([]byte, error) { - enc.buf = strconv.AppendFloat(enc.buf, n, 'f', -1, 64) - return enc.buf, nil -} - -// EncodeFloat32 encodes a float32 to JSON -func (enc *Encoder) EncodeFloat32(n float32) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeFloat32(n) - _, err := enc.Write() - if err != nil { - return err - } - return nil -} - -func (enc *Encoder) encodeFloat32(n float32) ([]byte, error) { - enc.buf = strconv.AppendFloat(enc.buf, float64(n), 'f', -1, 32) - return enc.buf, nil -} - -// AddFloat adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddFloat(v float64) { - enc.Float64(v) -} - -// AddFloatOmitEmpty adds a float64 to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddFloatOmitEmpty(v float64) { - enc.Float64OmitEmpty(v) -} - -// AddFloatNullEmpty adds a float64 to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddFloatNullEmpty(v float64) { - enc.Float64NullEmpty(v) -} - -// Float adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Float(v float64) { - enc.Float64(v) -} - -// FloatOmitEmpty adds a float64 to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) FloatOmitEmpty(v float64) { - enc.Float64OmitEmpty(v) -} - -// FloatNullEmpty adds a float64 to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) FloatNullEmpty(v float64) { - enc.Float64NullEmpty(v) -} - -// AddFloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddFloatKey(key string, v float64) { - enc.Float64Key(key, v) -} - -// AddFloatKeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddFloatKeyOmitEmpty(key string, v float64) { - enc.Float64KeyOmitEmpty(key, v) -} - -// AddFloatKeyNullEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddFloatKeyNullEmpty(key string, v float64) { - enc.Float64KeyNullEmpty(key, v) -} - -// FloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) FloatKey(key string, v float64) { - enc.Float64Key(key, v) -} - -// FloatKeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) FloatKeyOmitEmpty(key string, v float64) { - enc.Float64KeyOmitEmpty(key, v) -} - -// FloatKeyNullEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) FloatKeyNullEmpty(key string, v float64) { - enc.Float64KeyNullEmpty(key, v) -} - -// AddFloat64 adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddFloat64(v float64) { - enc.Float(v) -} - -// AddFloat64OmitEmpty adds a float64 to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddFloat64OmitEmpty(v float64) { - enc.FloatOmitEmpty(v) -} - -// Float64 adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Float64(v float64) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) -} - -// Float64OmitEmpty adds a float64 to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Float64OmitEmpty(v float64) { - if v == 0 { - return - } - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) -} - -// Float64NullEmpty adds a float64 to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Float64NullEmpty(v float64) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) -} - -// AddFloat64Key adds a float64 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddFloat64Key(key string, v float64) { - enc.FloatKey(key, v) -} - -// AddFloat64KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddFloat64KeyOmitEmpty(key string, v float64) { - enc.FloatKeyOmitEmpty(key, v) -} - -// Float64Key adds a float64 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Float64Key(key string, value float64) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.grow(10) - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendFloat(enc.buf, value, 'f', -1, 64) -} - -// Float64KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) Float64KeyOmitEmpty(key string, v float64) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v == 0 { - return - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) -} - -// Float64KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Float64KeyNullEmpty(key string, v float64) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) -} - -// AddFloat32 adds a float32 to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddFloat32(v float32) { - enc.Float32(v) -} - -// AddFloat32OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddFloat32OmitEmpty(v float32) { - enc.Float32OmitEmpty(v) -} - -// AddFloat32NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddFloat32NullEmpty(v float32) { - enc.Float32NullEmpty(v) -} - -// Float32 adds a float32 to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Float32(v float32) { - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) -} - -// Float32OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Float32OmitEmpty(v float32) { - if v == 0 { - return - } - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) -} - -// Float32NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Float32NullEmpty(v float32) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) -} - -// AddFloat32Key adds a float32 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddFloat32Key(key string, v float32) { - enc.Float32Key(key, v) -} - -// AddFloat32KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddFloat32KeyOmitEmpty(key string, v float32) { - enc.Float32KeyOmitEmpty(key, v) -} - -// AddFloat32KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddFloat32KeyNullEmpty(key string, v float32) { - enc.Float32KeyNullEmpty(key, v) -} - -// Float32Key adds a float32 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Float32Key(key string, v float32) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeByte('"') - enc.writeByte(':') - enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) -} - -// Float32KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) Float32KeyOmitEmpty(key string, v float32) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v == 0 { - return - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) -} - -// Float32KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) Float32KeyNullEmpty(key string, v float32) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_number_int.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_number_int.go deleted file mode 100644 index 2c4bbe343..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_number_int.go +++ /dev/null @@ -1,500 +0,0 @@ -package gojay - -import "strconv" - -// EncodeInt encodes an int to JSON -func (enc *Encoder) EncodeInt(n int) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeInt(n) - _, err := enc.Write() - if err != nil { - return err - } - return nil -} - -// encodeInt encodes an int to JSON -func (enc *Encoder) encodeInt(n int) ([]byte, error) { - enc.buf = strconv.AppendInt(enc.buf, int64(n), 10) - return enc.buf, nil -} - -// EncodeInt64 encodes an int64 to JSON -func (enc *Encoder) EncodeInt64(n int64) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeInt64(n) - _, err := enc.Write() - if err != nil { - return err - } - return nil -} - -// encodeInt64 encodes an int to JSON -func (enc *Encoder) encodeInt64(n int64) ([]byte, error) { - enc.buf = strconv.AppendInt(enc.buf, n, 10) - return enc.buf, nil -} - -// AddInt adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddInt(v int) { - enc.Int(v) -} - -// AddIntOmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddIntOmitEmpty(v int) { - enc.IntOmitEmpty(v) -} - -// AddIntNullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddIntNullEmpty(v int) { - enc.IntNullEmpty(v) -} - -// Int adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Int(v int) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) -} - -// IntOmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) IntOmitEmpty(v int) { - if v == 0 { - return - } - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) -} - -// IntNullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) IntNullEmpty(v int) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) -} - -// AddIntKey adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddIntKey(key string, v int) { - enc.IntKey(key, v) -} - -// AddIntKeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddIntKeyOmitEmpty(key string, v int) { - enc.IntKeyOmitEmpty(key, v) -} - -// AddIntKeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddIntKeyNullEmpty(key string, v int) { - enc.IntKeyNullEmpty(key, v) -} - -// IntKey adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) IntKey(key string, v int) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) -} - -// IntKeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) IntKeyOmitEmpty(key string, v int) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v == 0 { - return - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' && r != '[' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) -} - -// IntKeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) IntKeyNullEmpty(key string, v int) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' && r != '[' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) -} - -// AddInt64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddInt64(v int64) { - enc.Int64(v) -} - -// AddInt64OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddInt64OmitEmpty(v int64) { - enc.Int64OmitEmpty(v) -} - -// AddInt64NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddInt64NullEmpty(v int64) { - enc.Int64NullEmpty(v) -} - -// Int64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Int64(v int64) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendInt(enc.buf, v, 10) -} - -// Int64OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Int64OmitEmpty(v int64) { - if v == 0 { - return - } - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendInt(enc.buf, v, 10) -} - -// Int64NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Int64NullEmpty(v int64) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendInt(enc.buf, v, 10) -} - -// AddInt64Key adds an int64 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddInt64Key(key string, v int64) { - enc.Int64Key(key, v) -} - -// AddInt64KeyOmitEmpty adds an int64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddInt64KeyOmitEmpty(key string, v int64) { - enc.Int64KeyOmitEmpty(key, v) -} - -// AddInt64KeyNullEmpty adds an int64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddInt64KeyNullEmpty(key string, v int64) { - enc.Int64KeyNullEmpty(key, v) -} - -// Int64Key adds an int64 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Int64Key(key string, v int64) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendInt(enc.buf, v, 10) -} - -// Int64KeyOmitEmpty adds an int64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Int64KeyOmitEmpty(key string, v int64) { - if v == 0 { - return - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendInt(enc.buf, v, 10) -} - -// Int64KeyNullEmpty adds an int64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Int64KeyNullEmpty(key string, v int64) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendInt(enc.buf, v, 10) -} - -// AddInt32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddInt32(v int32) { - enc.Int64(int64(v)) -} - -// AddInt32OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddInt32OmitEmpty(v int32) { - enc.Int64OmitEmpty(int64(v)) -} - -// AddInt32NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddInt32NullEmpty(v int32) { - enc.Int64NullEmpty(int64(v)) -} - -// Int32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Int32(v int32) { - enc.Int64(int64(v)) -} - -// Int32OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Int32OmitEmpty(v int32) { - enc.Int64OmitEmpty(int64(v)) -} - -// Int32NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Int32NullEmpty(v int32) { - enc.Int64NullEmpty(int64(v)) -} - -// AddInt32Key adds an int32 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddInt32Key(key string, v int32) { - enc.Int64Key(key, int64(v)) -} - -// AddInt32KeyOmitEmpty adds an int32 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddInt32KeyOmitEmpty(key string, v int32) { - enc.Int64KeyOmitEmpty(key, int64(v)) -} - -// Int32Key adds an int32 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Int32Key(key string, v int32) { - enc.Int64Key(key, int64(v)) -} - -// Int32KeyOmitEmpty adds an int32 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Int32KeyOmitEmpty(key string, v int32) { - enc.Int64KeyOmitEmpty(key, int64(v)) -} - -// Int32KeyNullEmpty adds an int32 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Int32KeyNullEmpty(key string, v int32) { - enc.Int64KeyNullEmpty(key, int64(v)) -} - -// AddInt16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddInt16(v int16) { - enc.Int64(int64(v)) -} - -// AddInt16OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddInt16OmitEmpty(v int16) { - enc.Int64OmitEmpty(int64(v)) -} - -// Int16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Int16(v int16) { - enc.Int64(int64(v)) -} - -// Int16OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Int16OmitEmpty(v int16) { - enc.Int64OmitEmpty(int64(v)) -} - -// Int16NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Int16NullEmpty(v int16) { - enc.Int64NullEmpty(int64(v)) -} - -// AddInt16Key adds an int16 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddInt16Key(key string, v int16) { - enc.Int64Key(key, int64(v)) -} - -// AddInt16KeyOmitEmpty adds an int16 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddInt16KeyOmitEmpty(key string, v int16) { - enc.Int64KeyOmitEmpty(key, int64(v)) -} - -// AddInt16KeyNullEmpty adds an int16 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddInt16KeyNullEmpty(key string, v int16) { - enc.Int64KeyNullEmpty(key, int64(v)) -} - -// Int16Key adds an int16 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Int16Key(key string, v int16) { - enc.Int64Key(key, int64(v)) -} - -// Int16KeyOmitEmpty adds an int16 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Int16KeyOmitEmpty(key string, v int16) { - enc.Int64KeyOmitEmpty(key, int64(v)) -} - -// Int16KeyNullEmpty adds an int16 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Int16KeyNullEmpty(key string, v int16) { - enc.Int64KeyNullEmpty(key, int64(v)) -} - -// AddInt8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddInt8(v int8) { - enc.Int64(int64(v)) -} - -// AddInt8OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddInt8OmitEmpty(v int8) { - enc.Int64OmitEmpty(int64(v)) -} - -// AddInt8NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddInt8NullEmpty(v int8) { - enc.Int64NullEmpty(int64(v)) -} - -// Int8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Int8(v int8) { - enc.Int64(int64(v)) -} - -// Int8OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Int8OmitEmpty(v int8) { - enc.Int64OmitEmpty(int64(v)) -} - -// Int8NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Int8NullEmpty(v int8) { - enc.Int64NullEmpty(int64(v)) -} - -// AddInt8Key adds an int8 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddInt8Key(key string, v int8) { - enc.Int64Key(key, int64(v)) -} - -// AddInt8KeyOmitEmpty adds an int8 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddInt8KeyOmitEmpty(key string, v int8) { - enc.Int64KeyOmitEmpty(key, int64(v)) -} - -// AddInt8KeyNullEmpty adds an int8 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddInt8KeyNullEmpty(key string, v int8) { - enc.Int64KeyNullEmpty(key, int64(v)) -} - -// Int8Key adds an int8 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Int8Key(key string, v int8) { - enc.Int64Key(key, int64(v)) -} - -// Int8KeyOmitEmpty adds an int8 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Int8KeyOmitEmpty(key string, v int8) { - enc.Int64KeyOmitEmpty(key, int64(v)) -} - -// Int8KeyNullEmpty adds an int8 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Int8KeyNullEmpty(key string, v int8) { - enc.Int64KeyNullEmpty(key, int64(v)) -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_number_uint.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_number_uint.go deleted file mode 100644 index cd69b13fd..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_number_uint.go +++ /dev/null @@ -1,362 +0,0 @@ -package gojay - -import "strconv" - -// EncodeUint64 encodes an int64 to JSON -func (enc *Encoder) EncodeUint64(n uint64) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeUint64(n) - _, err := enc.Write() - if err != nil { - return err - } - return nil -} - -// encodeUint64 encodes an int to JSON -func (enc *Encoder) encodeUint64(n uint64) ([]byte, error) { - enc.buf = strconv.AppendUint(enc.buf, n, 10) - return enc.buf, nil -} - -// AddUint64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddUint64(v uint64) { - enc.Uint64(v) -} - -// AddUint64OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddUint64OmitEmpty(v uint64) { - enc.Uint64OmitEmpty(v) -} - -// AddUint64NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddUint64NullEmpty(v uint64) { - enc.Uint64NullEmpty(v) -} - -// Uint64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Uint64(v uint64) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendUint(enc.buf, v, 10) -} - -// Uint64OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Uint64OmitEmpty(v uint64) { - if v == 0 { - return - } - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendUint(enc.buf, v, 10) -} - -// Uint64NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Uint64NullEmpty(v uint64) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendUint(enc.buf, v, 10) -} - -// AddUint64Key adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddUint64Key(key string, v uint64) { - enc.Uint64Key(key, v) -} - -// AddUint64KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddUint64KeyOmitEmpty(key string, v uint64) { - enc.Uint64KeyOmitEmpty(key, v) -} - -// AddUint64KeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddUint64KeyNullEmpty(key string, v uint64) { - enc.Uint64KeyNullEmpty(key, v) -} - -// Uint64Key adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Uint64Key(key string, v uint64) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendUint(enc.buf, v, 10) -} - -// Uint64KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Uint64KeyOmitEmpty(key string, v uint64) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v == 0 { - return - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' && r != '[' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendUint(enc.buf, v, 10) -} - -// Uint64KeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Uint64KeyNullEmpty(key string, v uint64) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' && r != '[' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendUint(enc.buf, v, 10) -} - -// AddUint32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddUint32(v uint32) { - enc.Uint64(uint64(v)) -} - -// AddUint32OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddUint32OmitEmpty(v uint32) { - enc.Uint64OmitEmpty(uint64(v)) -} - -// AddUint32NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddUint32NullEmpty(v uint32) { - enc.Uint64NullEmpty(uint64(v)) -} - -// Uint32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Uint32(v uint32) { - enc.Uint64(uint64(v)) -} - -// Uint32OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Uint32OmitEmpty(v uint32) { - enc.Uint64OmitEmpty(uint64(v)) -} - -// Uint32NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Uint32NullEmpty(v uint32) { - enc.Uint64NullEmpty(uint64(v)) -} - -// AddUint32Key adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddUint32Key(key string, v uint32) { - enc.Uint64Key(key, uint64(v)) -} - -// AddUint32KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddUint32KeyOmitEmpty(key string, v uint32) { - enc.Uint64KeyOmitEmpty(key, uint64(v)) -} - -// AddUint32KeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddUint32KeyNullEmpty(key string, v uint32) { - enc.Uint64KeyNullEmpty(key, uint64(v)) -} - -// Uint32Key adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Uint32Key(key string, v uint32) { - enc.Uint64Key(key, uint64(v)) -} - -// Uint32KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Uint32KeyOmitEmpty(key string, v uint32) { - enc.Uint64KeyOmitEmpty(key, uint64(v)) -} - -// Uint32KeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Uint32KeyNullEmpty(key string, v uint32) { - enc.Uint64KeyNullEmpty(key, uint64(v)) -} - -// AddUint16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddUint16(v uint16) { - enc.Uint64(uint64(v)) -} - -// AddUint16OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddUint16OmitEmpty(v uint16) { - enc.Uint64OmitEmpty(uint64(v)) -} - -// AddUint16NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddUint16NullEmpty(v uint16) { - enc.Uint64NullEmpty(uint64(v)) -} - -// Uint16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Uint16(v uint16) { - enc.Uint64(uint64(v)) -} - -// Uint16OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Uint16OmitEmpty(v uint16) { - enc.Uint64OmitEmpty(uint64(v)) -} - -// Uint16NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Uint16NullEmpty(v uint16) { - enc.Uint64NullEmpty(uint64(v)) -} - -// AddUint16Key adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddUint16Key(key string, v uint16) { - enc.Uint64Key(key, uint64(v)) -} - -// AddUint16KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddUint16KeyOmitEmpty(key string, v uint16) { - enc.Uint64KeyOmitEmpty(key, uint64(v)) -} - -// AddUint16KeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddUint16KeyNullEmpty(key string, v uint16) { - enc.Uint64KeyNullEmpty(key, uint64(v)) -} - -// Uint16Key adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Uint16Key(key string, v uint16) { - enc.Uint64Key(key, uint64(v)) -} - -// Uint16KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Uint16KeyOmitEmpty(key string, v uint16) { - enc.Uint64KeyOmitEmpty(key, uint64(v)) -} - -// Uint16KeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Uint16KeyNullEmpty(key string, v uint16) { - enc.Uint64KeyNullEmpty(key, uint64(v)) -} - -// AddUint8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddUint8(v uint8) { - enc.Uint64(uint64(v)) -} - -// AddUint8OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddUint8OmitEmpty(v uint8) { - enc.Uint64OmitEmpty(uint64(v)) -} - -// AddUint8NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddUint8NullEmpty(v uint8) { - enc.Uint64NullEmpty(uint64(v)) -} - -// Uint8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Uint8(v uint8) { - enc.Uint64(uint64(v)) -} - -// Uint8OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Uint8OmitEmpty(v uint8) { - enc.Uint64OmitEmpty(uint64(v)) -} - -// Uint8NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Uint8NullEmpty(v uint8) { - enc.Uint64NullEmpty(uint64(v)) -} - -// AddUint8Key adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddUint8Key(key string, v uint8) { - enc.Uint64Key(key, uint64(v)) -} - -// AddUint8KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddUint8KeyOmitEmpty(key string, v uint8) { - enc.Uint64KeyOmitEmpty(key, uint64(v)) -} - -// AddUint8KeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddUint8KeyNullEmpty(key string, v uint8) { - enc.Uint64KeyNullEmpty(key, uint64(v)) -} - -// Uint8Key adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Uint8Key(key string, v uint8) { - enc.Uint64Key(key, uint64(v)) -} - -// Uint8KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Uint8KeyOmitEmpty(key string, v uint8) { - enc.Uint64KeyOmitEmpty(key, uint64(v)) -} - -// Uint8KeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Uint8KeyNullEmpty(key string, v uint8) { - enc.Uint64KeyNullEmpty(key, uint64(v)) -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_object.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_object.go deleted file mode 100644 index 5f2c8cf3f..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_object.go +++ /dev/null @@ -1,400 +0,0 @@ -package gojay - -var objKeyStr = []byte(`":"`) -var objKeyObj = []byte(`":{`) -var objKeyArr = []byte(`":[`) -var objKey = []byte(`":`) - -// EncodeObject encodes an object to JSON -func (enc *Encoder) EncodeObject(v MarshalerJSONObject) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, err := enc.encodeObject(v) - if err != nil { - enc.err = err - return err - } - _, err = enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} - -// EncodeObjectKeys encodes an object to JSON -func (enc *Encoder) EncodeObjectKeys(v MarshalerJSONObject, keys []string) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - enc.hasKeys = true - enc.keys = keys - _, err := enc.encodeObject(v) - if err != nil { - enc.err = err - return err - } - _, err = enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} - -func (enc *Encoder) encodeObject(v MarshalerJSONObject) ([]byte, error) { - enc.grow(512) - enc.writeByte('{') - if !v.IsNil() { - v.MarshalJSONObject(enc) - } - if enc.hasKeys { - enc.hasKeys = false - enc.keys = nil - } - enc.writeByte('}') - return enc.buf, enc.err -} - -// AddObject adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) AddObject(v MarshalerJSONObject) { - enc.Object(v) -} - -// AddObjectOmitEmpty adds an object to be encoded or skips it if IsNil returns true. -// Must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) AddObjectOmitEmpty(v MarshalerJSONObject) { - enc.ObjectOmitEmpty(v) -} - -// AddObjectNullEmpty adds an object to be encoded or skips it if IsNil returns true. -// Must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) AddObjectNullEmpty(v MarshalerJSONObject) { - enc.ObjectNullEmpty(v) -} - -// AddObjectKey adds a struct to be encoded, must be used inside an object as it will encode a key -// value must implement MarshalerJSONObject -func (enc *Encoder) AddObjectKey(key string, v MarshalerJSONObject) { - enc.ObjectKey(key, v) -} - -// AddObjectKeyOmitEmpty adds an object to be encoded or skips it if IsNil returns true. -// Must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) AddObjectKeyOmitEmpty(key string, v MarshalerJSONObject) { - enc.ObjectKeyOmitEmpty(key, v) -} - -// AddObjectKeyNullEmpty adds an object to be encoded or skips it if IsNil returns true. -// Must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) AddObjectKeyNullEmpty(key string, v MarshalerJSONObject) { - enc.ObjectKeyNullEmpty(key, v) -} - -// Object adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) Object(v MarshalerJSONObject) { - if v.IsNil() { - enc.grow(2) - r := enc.getPreviousRune() - if r != '{' && r != '[' { - enc.writeByte(',') - } - enc.writeByte('{') - enc.writeByte('}') - return - } - enc.grow(4) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeByte('{') - - var origHasKeys = enc.hasKeys - var origKeys = enc.keys - enc.hasKeys = false - enc.keys = nil - - v.MarshalJSONObject(enc) - - enc.hasKeys = origHasKeys - enc.keys = origKeys - - enc.writeByte('}') -} - -// ObjectWithKeys adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject. It will only encode the keys in keys. -func (enc *Encoder) ObjectWithKeys(v MarshalerJSONObject, keys []string) { - if v.IsNil() { - enc.grow(2) - r := enc.getPreviousRune() - if r != '{' && r != '[' { - enc.writeByte(',') - } - enc.writeByte('{') - enc.writeByte('}') - return - } - enc.grow(4) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeByte('{') - - var origKeys = enc.keys - var origHasKeys = enc.hasKeys - enc.hasKeys = true - enc.keys = keys - - v.MarshalJSONObject(enc) - - enc.hasKeys = origHasKeys - enc.keys = origKeys - - enc.writeByte('}') -} - -// ObjectOmitEmpty adds an object to be encoded or skips it if IsNil returns true. -// Must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) ObjectOmitEmpty(v MarshalerJSONObject) { - if v.IsNil() { - return - } - enc.grow(2) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeByte('{') - - var origHasKeys = enc.hasKeys - var origKeys = enc.keys - enc.hasKeys = false - enc.keys = nil - - v.MarshalJSONObject(enc) - - enc.hasKeys = origHasKeys - enc.keys = origKeys - - enc.writeByte('}') -} - -// ObjectNullEmpty adds an object to be encoded or skips it if IsNil returns true. -// Must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) ObjectNullEmpty(v MarshalerJSONObject) { - enc.grow(2) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v.IsNil() { - enc.writeBytes(nullBytes) - return - } - enc.writeByte('{') - - var origHasKeys = enc.hasKeys - var origKeys = enc.keys - enc.hasKeys = false - enc.keys = nil - - v.MarshalJSONObject(enc) - - enc.hasKeys = origHasKeys - enc.keys = origKeys - - enc.writeByte('}') -} - -// ObjectKey adds a struct to be encoded, must be used inside an object as it will encode a key -// value must implement MarshalerJSONObject -func (enc *Encoder) ObjectKey(key string, v MarshalerJSONObject) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v.IsNil() { - enc.grow(2 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyObj) - enc.writeByte('}') - return - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyObj) - - var origHasKeys = enc.hasKeys - var origKeys = enc.keys - enc.hasKeys = false - enc.keys = nil - - v.MarshalJSONObject(enc) - - enc.hasKeys = origHasKeys - enc.keys = origKeys - - enc.writeByte('}') -} - -// ObjectKeyWithKeys adds a struct to be encoded, must be used inside an object as it will encode a key. -// Value must implement MarshalerJSONObject. It will only encode the keys in keys. -func (enc *Encoder) ObjectKeyWithKeys(key string, value MarshalerJSONObject, keys []string) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if value.IsNil() { - enc.grow(2 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyObj) - enc.writeByte('}') - return - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyObj) - var origKeys = enc.keys - var origHasKeys = enc.hasKeys - enc.hasKeys = true - enc.keys = keys - value.MarshalJSONObject(enc) - enc.hasKeys = origHasKeys - enc.keys = origKeys - enc.writeByte('}') -} - -// ObjectKeyOmitEmpty adds an object to be encoded or skips it if IsNil returns true. -// Must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) ObjectKeyOmitEmpty(key string, v MarshalerJSONObject) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v.IsNil() { - return - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyObj) - - var origHasKeys = enc.hasKeys - var origKeys = enc.keys - enc.hasKeys = false - enc.keys = nil - - v.MarshalJSONObject(enc) - - enc.hasKeys = origHasKeys - enc.keys = origKeys - - enc.writeByte('}') -} - -// ObjectKeyNullEmpty adds an object to be encoded or skips it if IsNil returns true. -// Must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) ObjectKeyNullEmpty(key string, v MarshalerJSONObject) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - if v.IsNil() { - enc.writeBytes(nullBytes) - return - } - enc.writeByte('{') - - var origHasKeys = enc.hasKeys - var origKeys = enc.keys - enc.hasKeys = false - enc.keys = nil - - v.MarshalJSONObject(enc) - - enc.hasKeys = origHasKeys - enc.keys = origKeys - - enc.writeByte('}') -} - -// EncodeObjectFunc is a custom func type implementing MarshaleObject. -// Use it to cast a func(*Encoder) to Marshal an object. -// -// enc := gojay.NewEncoder(io.Writer) -// enc.EncodeObject(gojay.EncodeObjectFunc(func(enc *gojay.Encoder) { -// enc.AddStringKey("hello", "world") -// })) -type EncodeObjectFunc func(*Encoder) - -// MarshalJSONObject implements MarshalerJSONObject. -func (f EncodeObjectFunc) MarshalJSONObject(enc *Encoder) { - f(enc) -} - -// IsNil implements MarshalerJSONObject. -func (f EncodeObjectFunc) IsNil() bool { - return f == nil -} - -func (enc *Encoder) keyExists(k string) bool { - if enc.keys == nil { - return false - } - for _, key := range enc.keys { - if key == k { - return true - } - } - return false -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_pool.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_pool.go deleted file mode 100644 index 3b2632253..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_pool.go +++ /dev/null @@ -1,50 +0,0 @@ -package gojay - -import ( - "io" - "sync" -) - -var encPool = sync.Pool{ - New: func() interface{} { - return NewEncoder(nil) - }, -} - -var streamEncPool = sync.Pool{ - New: func() interface{} { - return Stream.NewEncoder(nil) - }, -} - -func init() { - for i := 0; i < 32; i++ { - encPool.Put(NewEncoder(nil)) - } - for i := 0; i < 32; i++ { - streamEncPool.Put(Stream.NewEncoder(nil)) - } -} - -// NewEncoder returns a new encoder or borrows one from the pool -func NewEncoder(w io.Writer) *Encoder { - return &Encoder{w: w} -} - -// BorrowEncoder borrows an Encoder from the pool. -func BorrowEncoder(w io.Writer) *Encoder { - enc := encPool.Get().(*Encoder) - enc.w = w - enc.buf = enc.buf[:0] - enc.isPooled = 0 - enc.err = nil - enc.hasKeys = false - enc.keys = nil - return enc -} - -// Release sends back a Encoder to the pool. -func (enc *Encoder) Release() { - enc.isPooled = 1 - encPool.Put(enc) -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_slice.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_slice.go deleted file mode 100644 index 7d964df97..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_slice.go +++ /dev/null @@ -1,113 +0,0 @@ -package gojay - -// AddSliceString marshals the given []string s -func (enc *Encoder) AddSliceString(s []string) { - enc.SliceString(s) -} - -// SliceString marshals the given []string s -func (enc *Encoder) SliceString(s []string) { - enc.Array(EncodeArrayFunc(func(enc *Encoder) { - for _, str := range s { - enc.String(str) - } - })) -} - -// AddSliceStringKey marshals the given []string s -func (enc *Encoder) AddSliceStringKey(k string, s []string) { - enc.SliceStringKey(k, s) -} - -// SliceStringKey marshals the given []string s -func (enc *Encoder) SliceStringKey(k string, s []string) { - enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) { - for _, str := range s { - enc.String(str) - } - })) -} - -// AddSliceInt marshals the given []int s -func (enc *Encoder) AddSliceInt(s []int) { - enc.SliceInt(s) -} - -// SliceInt marshals the given []int s -func (enc *Encoder) SliceInt(s []int) { - enc.Array(EncodeArrayFunc(func(enc *Encoder) { - for _, i := range s { - enc.Int(i) - } - })) -} - -// AddSliceIntKey marshals the given []int s -func (enc *Encoder) AddSliceIntKey(k string, s []int) { - enc.SliceIntKey(k, s) -} - -// SliceIntKey marshals the given []int s -func (enc *Encoder) SliceIntKey(k string, s []int) { - enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) { - for _, i := range s { - enc.Int(i) - } - })) -} - -// AddSliceFloat64 marshals the given []float64 s -func (enc *Encoder) AddSliceFloat64(s []float64) { - enc.SliceFloat64(s) -} - -// SliceFloat64 marshals the given []float64 s -func (enc *Encoder) SliceFloat64(s []float64) { - enc.Array(EncodeArrayFunc(func(enc *Encoder) { - for _, i := range s { - enc.Float64(i) - } - })) -} - -// AddSliceFloat64Key marshals the given []float64 s -func (enc *Encoder) AddSliceFloat64Key(k string, s []float64) { - enc.SliceFloat64Key(k, s) -} - -// SliceFloat64Key marshals the given []float64 s -func (enc *Encoder) SliceFloat64Key(k string, s []float64) { - enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) { - for _, i := range s { - enc.Float64(i) - } - })) -} - -// AddSliceBool marshals the given []bool s -func (enc *Encoder) AddSliceBool(s []bool) { - enc.SliceBool(s) -} - -// SliceBool marshals the given []bool s -func (enc *Encoder) SliceBool(s []bool) { - enc.Array(EncodeArrayFunc(func(enc *Encoder) { - for _, i := range s { - enc.Bool(i) - } - })) -} - -// AddSliceBoolKey marshals the given []bool s -func (enc *Encoder) AddSliceBoolKey(k string, s []bool) { - enc.SliceBoolKey(k, s) -} - -// SliceBoolKey marshals the given []bool s -func (enc *Encoder) SliceBoolKey(k string, s []bool) { - enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) { - for _, i := range s { - enc.Bool(i) - } - })) -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_sqlnull.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_sqlnull.go deleted file mode 100644 index 04ff5962a..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_sqlnull.go +++ /dev/null @@ -1,377 +0,0 @@ -package gojay - -import "database/sql" - -// EncodeSQLNullString encodes a string to -func (enc *Encoder) EncodeSQLNullString(v *sql.NullString) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeString(v.String) - _, err := enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} - -// AddSQLNullString adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullString(v *sql.NullString) { - enc.String(v.String) -} - -// AddSQLNullStringOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullStringOmitEmpty(v *sql.NullString) { - if v != nil && v.Valid && v.String != "" { - enc.StringOmitEmpty(v.String) - } -} - -// AddSQLNullStringNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullStringNullEmpty(v *sql.NullString) { - if v != nil && v.Valid { - enc.StringNullEmpty(v.String) - } -} - -// AddSQLNullStringKey adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullStringKey(key string, v *sql.NullString) { - enc.StringKey(key, v.String) -} - -// AddSQLNullStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullStringKeyOmitEmpty(key string, v *sql.NullString) { - if v != nil && v.Valid && v.String != "" { - enc.StringKeyOmitEmpty(key, v.String) - } -} - -// SQLNullString adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullString(v *sql.NullString) { - enc.String(v.String) -} - -// SQLNullStringOmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullStringOmitEmpty(v *sql.NullString) { - if v != nil && v.Valid && v.String != "" { - enc.String(v.String) - } -} - -// SQLNullStringNullEmpty adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullStringNullEmpty(v *sql.NullString) { - if v != nil && v.Valid { - enc.StringNullEmpty(v.String) - } -} - -// SQLNullStringKey adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullStringKey(key string, v *sql.NullString) { - enc.StringKey(key, v.String) -} - -// SQLNullStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullStringKeyOmitEmpty(key string, v *sql.NullString) { - if v != nil && v.Valid && v.String != "" { - enc.StringKeyOmitEmpty(key, v.String) - } -} - -// SQLNullStringKeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullStringKeyNullEmpty(key string, v *sql.NullString) { - if v != nil && v.Valid { - enc.StringKeyNullEmpty(key, v.String) - } -} - -// NullInt64 - -// EncodeSQLNullInt64 encodes a string to -func (enc *Encoder) EncodeSQLNullInt64(v *sql.NullInt64) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeInt64(v.Int64) - _, err := enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} - -// AddSQLNullInt64 adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullInt64(v *sql.NullInt64) { - enc.Int64(v.Int64) -} - -// AddSQLNullInt64OmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullInt64OmitEmpty(v *sql.NullInt64) { - if v != nil && v.Valid && v.Int64 != 0 { - enc.Int64OmitEmpty(v.Int64) - } -} - -// AddSQLNullInt64NullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullInt64NullEmpty(v *sql.NullInt64) { - if v != nil && v.Valid { - enc.Int64NullEmpty(v.Int64) - } -} - -// AddSQLNullInt64Key adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullInt64Key(key string, v *sql.NullInt64) { - enc.Int64Key(key, v.Int64) -} - -// AddSQLNullInt64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullInt64KeyOmitEmpty(key string, v *sql.NullInt64) { - if v != nil && v.Valid && v.Int64 != 0 { - enc.Int64KeyOmitEmpty(key, v.Int64) - } -} - -// AddSQLNullInt64KeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullInt64KeyNullEmpty(key string, v *sql.NullInt64) { - if v != nil && v.Valid { - enc.Int64KeyNullEmpty(key, v.Int64) - } -} - -// SQLNullInt64 adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullInt64(v *sql.NullInt64) { - enc.Int64(v.Int64) -} - -// SQLNullInt64OmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullInt64OmitEmpty(v *sql.NullInt64) { - if v != nil && v.Valid && v.Int64 != 0 { - enc.Int64(v.Int64) - } -} - -// SQLNullInt64NullEmpty adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullInt64NullEmpty(v *sql.NullInt64) { - if v != nil && v.Valid { - enc.Int64NullEmpty(v.Int64) - } -} - -// SQLNullInt64Key adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullInt64Key(key string, v *sql.NullInt64) { - enc.Int64Key(key, v.Int64) -} - -// SQLNullInt64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullInt64KeyOmitEmpty(key string, v *sql.NullInt64) { - if v != nil && v.Valid && v.Int64 != 0 { - enc.Int64KeyOmitEmpty(key, v.Int64) - } -} - -// SQLNullInt64KeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullInt64KeyNullEmpty(key string, v *sql.NullInt64) { - if v != nil && v.Valid { - enc.Int64KeyNullEmpty(key, v.Int64) - } -} - -// NullFloat64 - -// EncodeSQLNullFloat64 encodes a string to -func (enc *Encoder) EncodeSQLNullFloat64(v *sql.NullFloat64) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeFloat(v.Float64) - _, err := enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} - -// AddSQLNullFloat64 adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullFloat64(v *sql.NullFloat64) { - enc.Float64(v.Float64) -} - -// AddSQLNullFloat64OmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullFloat64OmitEmpty(v *sql.NullFloat64) { - if v != nil && v.Valid && v.Float64 != 0 { - enc.Float64OmitEmpty(v.Float64) - } -} - -// AddSQLNullFloat64NullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullFloat64NullEmpty(v *sql.NullFloat64) { - if v != nil && v.Valid { - enc.Float64NullEmpty(v.Float64) - } -} - -// AddSQLNullFloat64Key adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullFloat64Key(key string, v *sql.NullFloat64) { - enc.Float64Key(key, v.Float64) -} - -// AddSQLNullFloat64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullFloat64KeyOmitEmpty(key string, v *sql.NullFloat64) { - if v != nil && v.Valid && v.Float64 != 0 { - enc.Float64KeyOmitEmpty(key, v.Float64) - } -} - -// AddSQLNullFloat64KeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullFloat64KeyNullEmpty(key string, v *sql.NullFloat64) { - if v != nil && v.Valid { - enc.Float64KeyNullEmpty(key, v.Float64) - } -} - -// SQLNullFloat64 adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullFloat64(v *sql.NullFloat64) { - enc.Float64(v.Float64) -} - -// SQLNullFloat64OmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullFloat64OmitEmpty(v *sql.NullFloat64) { - if v != nil && v.Valid && v.Float64 != 0 { - enc.Float64(v.Float64) - } -} - -// SQLNullFloat64NullEmpty adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullFloat64NullEmpty(v *sql.NullFloat64) { - if v != nil && v.Valid { - enc.Float64NullEmpty(v.Float64) - } -} - -// SQLNullFloat64Key adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullFloat64Key(key string, v *sql.NullFloat64) { - enc.Float64Key(key, v.Float64) -} - -// SQLNullFloat64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullFloat64KeyOmitEmpty(key string, v *sql.NullFloat64) { - if v != nil && v.Valid && v.Float64 != 0 { - enc.Float64KeyOmitEmpty(key, v.Float64) - } -} - -// SQLNullFloat64KeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullFloat64KeyNullEmpty(key string, v *sql.NullFloat64) { - if v != nil && v.Valid { - enc.Float64KeyNullEmpty(key, v.Float64) - } -} - -// NullBool - -// EncodeSQLNullBool encodes a string to -func (enc *Encoder) EncodeSQLNullBool(v *sql.NullBool) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeBool(v.Bool) - _, err := enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} - -// AddSQLNullBool adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullBool(v *sql.NullBool) { - enc.Bool(v.Bool) -} - -// AddSQLNullBoolOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullBoolOmitEmpty(v *sql.NullBool) { - if v != nil && v.Valid && v.Bool != false { - enc.BoolOmitEmpty(v.Bool) - } -} - -// AddSQLNullBoolKey adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullBoolKey(key string, v *sql.NullBool) { - enc.BoolKey(key, v.Bool) -} - -// AddSQLNullBoolKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullBoolKeyOmitEmpty(key string, v *sql.NullBool) { - if v != nil && v.Valid && v.Bool != false { - enc.BoolKeyOmitEmpty(key, v.Bool) - } -} - -// AddSQLNullBoolKeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullBoolKeyNullEmpty(key string, v *sql.NullBool) { - if v != nil && v.Valid { - enc.BoolKeyNullEmpty(key, v.Bool) - } -} - -// SQLNullBool adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullBool(v *sql.NullBool) { - enc.Bool(v.Bool) -} - -// SQLNullBoolOmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullBoolOmitEmpty(v *sql.NullBool) { - if v != nil && v.Valid && v.Bool != false { - enc.Bool(v.Bool) - } -} - -// SQLNullBoolNullEmpty adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullBoolNullEmpty(v *sql.NullBool) { - if v != nil && v.Valid { - enc.BoolNullEmpty(v.Bool) - } -} - -// SQLNullBoolKey adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullBoolKey(key string, v *sql.NullBool) { - enc.BoolKey(key, v.Bool) -} - -// SQLNullBoolKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullBoolKeyOmitEmpty(key string, v *sql.NullBool) { - if v != nil && v.Valid && v.Bool != false { - enc.BoolKeyOmitEmpty(key, v.Bool) - } -} - -// SQLNullBoolKeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullBoolKeyNullEmpty(key string, v *sql.NullBool) { - if v != nil && v.Valid { - enc.BoolKeyNullEmpty(key, v.Bool) - } -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_stream.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_stream.go deleted file mode 100644 index fae8a17cf..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_stream.go +++ /dev/null @@ -1,205 +0,0 @@ -package gojay - -import ( - "strconv" - "sync" - "time" -) - -// MarshalerStream is the interface to implement -// to continuously encode of stream of data. -type MarshalerStream interface { - MarshalStream(enc *StreamEncoder) -} - -// A StreamEncoder reads and encodes values to JSON from an input stream. -// -// It implements conext.Context and provide a channel to notify interruption. -type StreamEncoder struct { - mux *sync.RWMutex - *Encoder - nConsumer int - delimiter byte - deadline *time.Time - done chan struct{} -} - -// EncodeStream spins up a defined number of non blocking consumers of the MarshalerStream m. -// -// m must implement MarshalerStream. Ideally m is a channel. See example for implementation. -// -// See the documentation for Marshal for details about the conversion of Go value to JSON. -func (s *StreamEncoder) EncodeStream(m MarshalerStream) { - // if a single consumer, just use this encoder - if s.nConsumer == 1 { - go consume(s, s, m) - return - } - // else use this Encoder only for first consumer - // and use new encoders for other consumers - // this is to avoid concurrent writing to same buffer - // resulting in a weird JSON - go consume(s, s, m) - for i := 1; i < s.nConsumer; i++ { - s.mux.RLock() - select { - case <-s.done: - default: - ss := Stream.borrowEncoder(s.w) - ss.mux.Lock() - ss.done = s.done - ss.buf = make([]byte, 0, 512) - ss.delimiter = s.delimiter - go consume(s, ss, m) - ss.mux.Unlock() - } - s.mux.RUnlock() - } - return -} - -// LineDelimited sets the delimiter to a new line character. -// -// It will add a new line after each JSON marshaled by the MarshalerStream -func (s *StreamEncoder) LineDelimited() *StreamEncoder { - s.delimiter = '\n' - return s -} - -// CommaDelimited sets the delimiter to a comma. -// -// It will add a new line after each JSON marshaled by the MarshalerStream -func (s *StreamEncoder) CommaDelimited() *StreamEncoder { - s.delimiter = ',' - return s -} - -// NConsumer sets the number of non blocking go routine to consume the stream. -func (s *StreamEncoder) NConsumer(n int) *StreamEncoder { - s.nConsumer = n - return s -} - -// Release sends back a Decoder to the pool. -// If a decoder is used after calling Release -// a panic will be raised with an InvalidUsagePooledDecoderError error. -func (s *StreamEncoder) Release() { - s.isPooled = 1 - streamEncPool.Put(s) -} - -// Done returns a channel that's closed when work is done. -// It implements context.Context -func (s *StreamEncoder) Done() <-chan struct{} { - return s.done -} - -// Err returns nil if Done is not yet closed. -// If Done is closed, Err returns a non-nil error explaining why. -// It implements context.Context -func (s *StreamEncoder) Err() error { - return s.err -} - -// Deadline returns the time when work done on behalf of this context -// should be canceled. Deadline returns ok==false when no deadline is -// set. Successive calls to Deadline return the same results. -func (s *StreamEncoder) Deadline() (time.Time, bool) { - if s.deadline != nil { - return *s.deadline, true - } - return time.Time{}, false -} - -// SetDeadline sets the deadline -func (s *StreamEncoder) SetDeadline(t time.Time) { - s.deadline = &t -} - -// Value implements context.Context -func (s *StreamEncoder) Value(key interface{}) interface{} { - return nil -} - -// Cancel cancels the consumers of the stream, interrupting the stream encoding. -// -// After calling cancel, Done() will return a closed channel. -func (s *StreamEncoder) Cancel(err error) { - s.mux.Lock() - defer s.mux.Unlock() - - select { - case <-s.done: - default: - s.err = err - close(s.done) - } -} - -// AddObject adds an object to be encoded. -// value must implement MarshalerJSONObject. -func (s *StreamEncoder) AddObject(v MarshalerJSONObject) { - if v.IsNil() { - return - } - s.Encoder.writeByte('{') - v.MarshalJSONObject(s.Encoder) - s.Encoder.writeByte('}') - s.Encoder.writeByte(s.delimiter) -} - -// AddString adds a string to be encoded. -func (s *StreamEncoder) AddString(v string) { - s.Encoder.writeByte('"') - s.Encoder.writeString(v) - s.Encoder.writeByte('"') - s.Encoder.writeByte(s.delimiter) -} - -// AddArray adds an implementation of MarshalerJSONArray to be encoded. -func (s *StreamEncoder) AddArray(v MarshalerJSONArray) { - s.Encoder.writeByte('[') - v.MarshalJSONArray(s.Encoder) - s.Encoder.writeByte(']') - s.Encoder.writeByte(s.delimiter) -} - -// AddInt adds an int to be encoded. -func (s *StreamEncoder) AddInt(value int) { - s.buf = strconv.AppendInt(s.buf, int64(value), 10) - s.Encoder.writeByte(s.delimiter) -} - -// AddFloat64 adds a float64 to be encoded. -func (s *StreamEncoder) AddFloat64(value float64) { - s.buf = strconv.AppendFloat(s.buf, value, 'f', -1, 64) - s.Encoder.writeByte(s.delimiter) -} - -// AddFloat adds a float64 to be encoded. -func (s *StreamEncoder) AddFloat(value float64) { - s.AddFloat64(value) -} - -// Non exposed - -func consume(init *StreamEncoder, s *StreamEncoder, m MarshalerStream) { - defer s.Release() - for { - select { - case <-init.Done(): - return - default: - m.MarshalStream(s) - if s.Encoder.err != nil { - init.Cancel(s.Encoder.err) - return - } - i, err := s.Encoder.Write() - if err != nil || i == 0 { - init.Cancel(err) - return - } - } - } -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_stream_pool.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_stream_pool.go deleted file mode 100644 index 3bb8b1af0..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_stream_pool.go +++ /dev/null @@ -1,38 +0,0 @@ -package gojay - -import ( - "io" - "sync" -) - -// NewEncoder returns a new StreamEncoder. -// It takes an io.Writer implementation to output data. -// It initiates the done channel returned by Done(). -func (s stream) NewEncoder(w io.Writer) *StreamEncoder { - enc := BorrowEncoder(w) - return &StreamEncoder{Encoder: enc, nConsumer: 1, done: make(chan struct{}, 1), mux: &sync.RWMutex{}} -} - -// BorrowEncoder borrows a StreamEncoder from the pool. -// It takes an io.Writer implementation to output data. -// It initiates the done channel returned by Done(). -// -// If no StreamEncoder is available in the pool, it returns a fresh one -func (s stream) BorrowEncoder(w io.Writer) *StreamEncoder { - streamEnc := streamEncPool.Get().(*StreamEncoder) - streamEnc.w = w - streamEnc.Encoder.err = nil - streamEnc.done = make(chan struct{}, 1) - streamEnc.Encoder.buf = streamEnc.buf[:0] - streamEnc.nConsumer = 1 - streamEnc.isPooled = 0 - return streamEnc -} - -func (s stream) borrowEncoder(w io.Writer) *StreamEncoder { - streamEnc := streamEncPool.Get().(*StreamEncoder) - streamEnc.isPooled = 0 - streamEnc.w = w - streamEnc.Encoder.err = nil - return streamEnc -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_string.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_string.go deleted file mode 100644 index 438c773fc..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_string.go +++ /dev/null @@ -1,186 +0,0 @@ -package gojay - -// EncodeString encodes a string to -func (enc *Encoder) EncodeString(s string) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeString(s) - _, err := enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} - -// encodeString encodes a string to -func (enc *Encoder) encodeString(v string) ([]byte, error) { - enc.writeByte('"') - enc.writeStringEscape(v) - enc.writeByte('"') - return enc.buf, nil -} - -// AppendString appends a string to the buffer -func (enc *Encoder) AppendString(v string) { - enc.grow(len(v) + 2) - enc.writeByte('"') - enc.writeStringEscape(v) - enc.writeByte('"') -} - -// AddString adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddString(v string) { - enc.String(v) -} - -// AddStringOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddStringOmitEmpty(v string) { - enc.StringOmitEmpty(v) -} - -// AddStringNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddStringNullEmpty(v string) { - enc.StringNullEmpty(v) -} - -// AddStringKey adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddStringKey(key, v string) { - enc.StringKey(key, v) -} - -// AddStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddStringKeyOmitEmpty(key, v string) { - enc.StringKeyOmitEmpty(key, v) -} - -// AddStringKeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddStringKeyNullEmpty(key, v string) { - enc.StringKeyNullEmpty(key, v) -} - -// String adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) String(v string) { - enc.grow(len(v) + 4) - r := enc.getPreviousRune() - if r != '[' { - enc.writeTwoBytes(',', '"') - } else { - enc.writeByte('"') - } - enc.writeStringEscape(v) - enc.writeByte('"') -} - -// StringOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) StringOmitEmpty(v string) { - if v == "" { - return - } - r := enc.getPreviousRune() - if r != '[' { - enc.writeTwoBytes(',', '"') - } else { - enc.writeByte('"') - } - enc.writeStringEscape(v) - enc.writeByte('"') -} - -// StringNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) StringNullEmpty(v string) { - r := enc.getPreviousRune() - if v == "" { - if r != '[' { - enc.writeByte(',') - enc.writeBytes(nullBytes) - } else { - enc.writeBytes(nullBytes) - } - return - } - if r != '[' { - enc.writeTwoBytes(',', '"') - } else { - enc.writeByte('"') - } - enc.writeStringEscape(v) - enc.writeByte('"') -} - -// StringKey adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) StringKey(key, v string) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(len(key) + len(v) + 5) - r := enc.getPreviousRune() - if r != '{' { - enc.writeTwoBytes(',', '"') - } else { - enc.writeByte('"') - } - enc.writeStringEscape(key) - enc.writeBytes(objKeyStr) - enc.writeStringEscape(v) - enc.writeByte('"') -} - -// StringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) StringKeyOmitEmpty(key, v string) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v == "" { - return - } - enc.grow(len(key) + len(v) + 5) - r := enc.getPreviousRune() - if r != '{' { - enc.writeTwoBytes(',', '"') - } else { - enc.writeByte('"') - } - enc.writeStringEscape(key) - enc.writeBytes(objKeyStr) - enc.writeStringEscape(v) - enc.writeByte('"') -} - -// StringKeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) StringKeyNullEmpty(key, v string) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(len(key) + len(v) + 5) - r := enc.getPreviousRune() - if r != '{' { - enc.writeTwoBytes(',', '"') - } else { - enc.writeByte('"') - } - enc.writeStringEscape(key) - enc.writeBytes(objKey) - if v == "" { - enc.writeBytes(nullBytes) - return - } - enc.writeByte('"') - enc.writeStringEscape(v) - enc.writeByte('"') -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_time.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_time.go deleted file mode 100644 index 6f99e3426..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/encode_time.go +++ /dev/null @@ -1,68 +0,0 @@ -package gojay - -import ( - "time" -) - -// EncodeTime encodes a *time.Time to JSON with the given format -func (enc *Encoder) EncodeTime(t *time.Time, format string) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeTime(t, format) - _, err := enc.Write() - if err != nil { - return err - } - return nil -} - -// encodeInt encodes an int to JSON -func (enc *Encoder) encodeTime(t *time.Time, format string) ([]byte, error) { - enc.writeByte('"') - enc.buf = t.AppendFormat(enc.buf, format) - enc.writeByte('"') - return enc.buf, nil -} - -// AddTimeKey adds an *time.Time to be encoded with the given format, must be used inside an object as it will encode a key -func (enc *Encoder) AddTimeKey(key string, t *time.Time, format string) { - enc.TimeKey(key, t, format) -} - -// TimeKey adds an *time.Time to be encoded with the given format, must be used inside an object as it will encode a key -func (enc *Encoder) TimeKey(key string, t *time.Time, format string) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeTwoBytes(',', '"') - } else { - enc.writeByte('"') - } - enc.writeStringEscape(key) - enc.writeBytes(objKeyStr) - enc.buf = t.AppendFormat(enc.buf, format) - enc.writeByte('"') -} - -// AddTime adds an *time.Time to be encoded with the given format, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddTime(t *time.Time, format string) { - enc.Time(t, format) -} - -// Time adds an *time.Time to be encoded with the given format, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Time(t *time.Time, format string) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.buf = t.AppendFormat(enc.buf, format) - enc.writeByte('"') -} diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/errors.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/errors.go deleted file mode 100644 index 0fd52e663..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/errors.go +++ /dev/null @@ -1,88 +0,0 @@ -package gojay - -import ( - "errors" - "fmt" -) - -const invalidJSONCharErrorMsg = "Invalid JSON, wrong char '%c' found at position %d" - -// InvalidJSONError is a type representing an error returned when -// Decoding encounters invalid JSON. -type InvalidJSONError string - -func (err InvalidJSONError) Error() string { - return string(err) -} - -func (dec *Decoder) raiseInvalidJSONErr(pos int) error { - var c byte - if len(dec.data) > pos { - c = dec.data[pos] - } - dec.err = InvalidJSONError( - fmt.Sprintf( - invalidJSONCharErrorMsg, - c, - pos, - ), - ) - return dec.err -} - -const invalidUnmarshalErrorMsg = "Cannot unmarshal JSON to type '%T'" - -// InvalidUnmarshalError is a type representing an error returned when -// Decoding cannot unmarshal JSON to the receiver type for various reasons. -type InvalidUnmarshalError string - -func (err InvalidUnmarshalError) Error() string { - return string(err) -} - -func (dec *Decoder) makeInvalidUnmarshalErr(v interface{}) error { - return InvalidUnmarshalError( - fmt.Sprintf( - invalidUnmarshalErrorMsg, - v, - ), - ) -} - -const invalidMarshalErrorMsg = "Invalid type %T provided to Marshal" - -// InvalidMarshalError is a type representing an error returned when -// Encoding did not find the proper way to encode -type InvalidMarshalError string - -func (err InvalidMarshalError) Error() string { - return string(err) -} - -// NoReaderError is a type representing an error returned when -// decoding requires a reader and none was given -type NoReaderError string - -func (err NoReaderError) Error() string { - return string(err) -} - -// InvalidUsagePooledDecoderError is a type representing an error returned -// when decoding is called on a still pooled Decoder -type InvalidUsagePooledDecoderError string - -func (err InvalidUsagePooledDecoderError) Error() string { - return string(err) -} - -// InvalidUsagePooledEncoderError is a type representing an error returned -// when decoding is called on a still pooled Encoder -type InvalidUsagePooledEncoderError string - -func (err InvalidUsagePooledEncoderError) Error() string { - return string(err) -} - -// ErrUnmarshalPtrExpected is the error returned when unmarshal expects a pointer value, -// When using `dec.ObjectNull` or `dec.ArrayNull` for example. -var ErrUnmarshalPtrExpected = errors.New("Cannot unmarshal to given value, a pointer is expected") diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/gojay.go b/plugins/traefik/vendor/github.com/francoispqt/gojay/gojay.go deleted file mode 100644 index d0c542f6b..000000000 --- a/plugins/traefik/vendor/github.com/francoispqt/gojay/gojay.go +++ /dev/null @@ -1,10 +0,0 @@ -// Package gojay implements encoding and decoding of JSON as defined in RFC 7159. -// The mapping between JSON and Go values is described -// in the documentation for the Marshal and Unmarshal functions. -// -// It aims at performance and usability by relying on simple interfaces -// to decode and encode structures, slices, arrays and even channels. -// -// On top of the simple interfaces to implement, gojay provides lots of helpers to decode and encode -// multiple of different types natively such as bit.Int, sql.NullString or time.Time -package gojay diff --git a/plugins/traefik/vendor/github.com/francoispqt/gojay/gojay.png b/plugins/traefik/vendor/github.com/francoispqt/gojay/gojay.png deleted file mode 100644 index 21090bdd20877f745003b24a4e8667f57b67a1a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44163 zcmeEthd6l??>n)c?&Eu7 z!-#n95jDI@a%%7|H6hC~Q+-SO$%^j!8=_AS6xRJWt4tdT z^@(||c)umOL2(}cj;I2Wy6zVevR-n_8Z|tnxgDGl5rG|_!oDABUXu0bJ0W<*M)rTd z|8EB_cL0KJ8xDx}j4bUif5S~%2qzPil3B~e zd${#dTkH&s4GuY#4)O8wj;da{j9EoO5luv!@YNpStJf}e{G0f$w$cCDIl4yL8gtYy z9DS$EE+|Yn?K>UO1M1gLBQ5C(Dci{EiT0nAj|Dupj`9_M5Yi*-D}2@ZP5kT2J20I2 zUz>1s{cG_Wp0LLHtbnHJ>q~VHhQvM89rMYgB?bB)OS_?{S4Ro#sxLN z?%DNjO9siOIK0?z+9a6$=PQGe4y6P;4mPwf z49&7EWy;!HsUfJ!OX4O3342hr3A@B!zYZSq0i7cZQR9>D66@{nuJ+y|*)PoFIIDk2 zT3=1|fByR~0~k*Nz1*mgYgC+=Y0SWIK*gv(tbHL1JU&_dC!fmEKXVJUwYABI=WVhNZe*_?pdSR=HGD35(<(~dC?sis0~|33T>JNPmm}W za4-|K4cjxsmtu?+v_f_*d72QZ3xN)0{i{-J7&I<`tNKOicmEAojI6UZz1tBW4 zEmOpU0A5Ax#)QZuo7IP$i>IbZwyvU7tDRlrCuvg{MgOpz2*ZPVaon)`Jj!7ffBTb$;Ux(93=-i1FJ}Z?bMkzJhJs-M_SgT~8kNNjl!wXLJXh(SJTEV= zTmr}CwN?JQwr8eWw{E%kU(J*9_=zjfR|~g@&V_MH!%PH&&Gt6~BE2i~2l70?m5ZHd`&N)|pG}eZe0v zWPHWx%{AKjtZqMkJ^h6OT!F`5*w=TC>YYkKEWXFqNu%_U!K8{%D;+d?eX!GL?Iu#J zs_fM@2m}KwbRWM$#!W&1yHdkDoNy0B9Fbe2$N72e6OX=_{|F|)&nKUKXuGjuVzj#F zFT}&ghv(|x1c?C30#|-0a+!cbid8}AHSNMMxvg#9@HN}%jgnOXSXN|V`yMkfTgR;% zl`8gd9clfC&c#t(0k9j_2k*RBe|K+WKLQ-(c`q)6lzQ0cpscX)8RtWxVgGn1dJni! z770Y*QH5M+ z0(9^C;1bkK@)(JHs8*EYFvKlkzjxSG^0^2%NvvSEH1iMAg4~`71UF}(&90Gz3Tcjy zRA2Pb+9QkE*9!dD9jVu-&wUqkJ+&0~eCRfS1A9E}d7kG?Baj+zU5NJBID>L8|e%@Vd( z(~XZFJ-T08QL!D&QQu4w6?g@qohs=xQCuqGS|QpQjkM<#t(s7g#`@{9QTx)*CoBE> zl10Z+x$!%Oxp>iJ7(Kcxf(tCtT}769q0pnedFKtze-8h$p9ZCT=ENFnsHTqolP zP=F-}H9*c!LAwyYU9Rtg%*KpnyvmIjRiTJM91LzC5XG2-bVc`7FlF?ekk2m)Gsptw2 zugLt@jjx@-a0CNIB~wNRsgbC#1p=34b@EnKmWP6XoOR# z9buu|Sq;)j)^T)wW&W&$_CK50TV!lh3MylN)F8~S>LAA7Q`W_uV!c;wGbm7(%bg7_ zGVQnAWUrj6xTk^t?AW(_ELR*& zBDV8ocqH|Glx<_u+i@S@?%a%v>ZN;LH*ek~ey3Dg-=xWw69JvhfaG`0zN-l&zhEvz zh<-$-K5w4X=s~(a$ORYOxH-A(7H7PxdL;Rvf-3E4TyAdewQ$Qu4P<|9y9v$U??$X1 z)pe0_dGK}`Y&q7jT50@3f^KF?zMsT-W7lJQfka}R+EVdfI}8YOlg`X`5s!;wIoF(^0R}8st4ErGh&Js0R9#^3!-pM7`3`0dhNCLb+vMch5H@Q1kMUA*Sd5pH zlaFUTgt`t%!%n+!sj%m^i({aqlHVyvjR`aB!zuK#kgcf&+4?j5Hz~X$#i7QX!-~5vrR`IrwfNPR9bcKe*f2Sc=Ot0e6oWY< zW$pk|YU`;r!DguqRD=zu6I3uNw(>7_C1y%Oa#Ota*ij$S-Fd5>rM+AawvGZXMvTlC z>nlN$JEu0^7Kw34u@l{ih`sOaY~MBPEgK>5@UG&loaJHya+e3CZ##`aUvnd&jokC~ z9Ju9su8fq2-6g-^%;9L6xp+_33=$OjFtAwK~;y9af_PWUgqueu`H^;?pXo zzvJ-nx_CbDYf2s+6}977iWl`T=1{X-tX34~T*`nUzDJiw7fW6PZ<`gza8V&qYO@)~P|G6k@jaS;I-TA}7a6lhVcn zVqORw)jnK3tD8-t1bWumQKm zP~b~!IngISkQ$lS*yrhBwbZRgQ2qLx(*sCeWQPNCMR*L#Pn-EG{$!8%BQY}bM5j66 z>w9ii`L>0BX{|VJw%_DOzEHrMgR)3_N|aCJGIPsJOT|5}r_@F7UA``&_JFtTUdj^^fkTaC8h*FT8`Ov*8&dXg!Rt2*4f* zf-G$ZcAIV8dar>mo||Tn*Y+OVzaV89w~@QfxpEJ6!IPJNbat+td)w*GvQn}nYhjc8 z0SHXfF5CrMPSn9;t~(Gd!LyurBT3)C-*0~2J|H1{*`d8JWqTc-KF^Q)OUYIgUcPZV z=b&n|9H8Es|FZ5@IzEuNY7L0m zPL=O+y?%vaQb*dcSC&sVWghQ=CULI8XY>4_nn!shcyB(GFGot_*$}s*N!A1?VL_Fn zV^A$qwkh0&UV$CnL^zwyPW&(`RgVO>G9jYL2mpxbu(4CKT(}>~S3CjOX64ruHR7bwaHR|Cmk(V()pa-tR1kw}B3mXetSzV9UiF za71>xDFM9)60lv;Ej%b&!E|e9+I1_fKYsjJgI$HWX;6LT z6a8U|8~>dw^v5l{g5=n8?D%{9Z&k!95jEYpcwtt4;LBk|l2z)l62Kv)w4W?ttaHI< zQm%=%r$JQjY-JrVgem>tftyYv^sRXEmxsyWa5Y8L}n1a(((2ug} zJtI32xjg<*Y=P9uhn3u2<&dEfx>8FW%dI{_z;&W?}sR8 zRzhOuJZ$CCv0!)R3d#2gICx)NXgQ^?A3ryzn4qSks~OolBV>HH*nLMewQ7`3`l*e) zT}{}Q-P);roA5TowER2Z8LkJC$&Z)1^WoFsa`xIi*@2V}CMf*Ijhi+%XqRkn(E57DzExtpPuTexxr?==2dy;In;_S8ns!BustiCVl(QyFlUz9-cE2?ZIXb4?K?y!~ z;i$)ZtS#w*&N59pGgo)_g(%BfjWKZMbE##^mxQ?w2ruy zCUfU7<7B8>KU6!@scJ=LNl*ZM25#K%k6pI_R{E{3QzHYQSJI;zw)F7uFyylT8k4P}Y!l{%e9_C(V9R{OUu7MTZqcGp&iIoLaH|jr>v#Zl z$Sj!P>8tfDw{wzUI2PTY z9exh~Rxcjy_QpS=oUT9Qq1Q^e3`T1qbV3vxgWDrU7w^&E0(?&l^!0C~<{yEk!1?(n{z>{X*W^w zDAu04{91Zy=>RjbSN_ptue3TBXcN9iA1*?oAl)qh-%aKKw2p;sF#O5qw=+IgSPFqU z0zPqS_Ljep+b`=gAG^(!20ia8)4Zw;Lbk@ocjK_GdTOQjt}f8E37Z}GH`QSC{jMoW zbABuS$j89fgskk7l=+x&^GTyaiqh4#XZ<+sZ{z;TNI+>cyM%@pfFPQQ*Ll{c
  • @ zdYQ4oTFdr*Wtf>~$Xg^?y-0*Rgrfz*Eq`P4$93F`{f2b&C%dowfbain8d><~nR)J} z1^7f^|KY<>UOv9}RimnaZY>>^f@eFM81X#0ew9Q$(mttkfyYT83i=}2xvOGdtmlhL zNzhA4tErj!k2@(Aa-;c}KdTcOrWLy^_9I>zidN_KOGKB!-rC{!yPiIlxnfo>@gA+r z`Fo2SH-;2*c=TY9fDCTuQln)6(LY2M)CxL5t~_}xH#WmLmUY?ynMOvu4JmHU_C4Q- znpMUyggk!u@S%L(@Czu`YLOGh6q#bGv=Ip@e&re^JWNF2voB5ROB4UQmiROd^zKcW z*iACyVWYL7f_2A+3R;2y5Ko=B737Wm&!$to&p+_LAw+?MeSp|uM()0|F*K+qU}q&X zi1x5Oe~o7OahE^Xw0>q8QQ*|cf)sm)F8ju6&BaeDJpQlst}*|^0Zy((Qz$T;mo8Fo z+g6JiuOZO;lB|P3-cBgd+IouIe+(_an2rNflqyrOn|)wO zO7&B}IOPTQ=pjRhq~TbXdXTqt?>As2$^V0;llkiv%m_zQL9eC1SAops?P|**heDUx zEJppaYv~o&A)l47V^q6^(&ebYjuWzB(GyZpwYt!09__&pSr|i*KY)2hy>+6XgOeoB zem3OH) z%P)psCL|<$fcrE5$)KE6Kpq$;?lHAffW^`5u}Q#u+a;@#C7UEQxb19WR|wc>TIKgS z>S~Yf(?YBe?~6z+q$$wl%GT{i$j>cuGETJ z$9sACJUf_D{c!UwsVWn;L*#%}s>fHA7Qz5NWJ&9R6xUz$NekkBSsdK~&dihTyv zjBGXY;_Q&S_fTPpHsXdV^sKBi@6mniIXl#EmVXz`IjN(Un*k_KF^x!*wAxkUSpAz8 z{8ld|(7PnpSrKnq0GeqM2w#nd{TkOu&D!>FGPuw3W0G&b<-f&%@G|`FJI``~P*5{L z2dgs@jp>I?TH>oq!l)m(-dH<@zs?VXEP|$SMJk=2TA9k@Q@U_T3;o8#H3Yt9PEA=k zIXS{V9d?caBS=EOI-%{UQ41eKHu$aTrCk??eNyAHeD36oxY5>#>~sK_SpFRy7JLIm z31!7gRoyD%dI~zm9$d}0n|smdQ7D;>*00CL%zx#Ti<2BRuYB?B}Qb{H^u*ZJAms-Ar|eq4|<$nloN6MZEz5vUlm;*+{k2*(0P6 zCC}aKq*|pazakJjrfvfML|f!2V}QPeE*E03c4`VONfx*+_NGa(k%XPna?}@3oUE$Z z2D2!*@OO6X`!}iZ<(1hk#ubvwQoLp=k(n@I+6Pg6bo0rkSLb3pSLbVATkfO{&+R=~ zz~Q-)5H4@16*0Z`ws_5BxtMlYpxLJE*Zu>^3AFwTra>l9>s5U9rqbQ4ShiQq+9L8) z3cklGW_hF`4WjYnpIt;r*3W z)$9&f5@HdTJSyu&NlTdj)>!Vh`=Q6*QCUKyZVad&0MoX4Z%#A6|J+DJLqk`)*n+ZRb87ND0+K{7@HXyrSI8IjA58my+A)J+T$-f0Jy zJ-|v~0JK2_5K?TsqU6eF+jz$CxGS@QudC$=AeupS5Er#}H$Ng;0+KkBkJxRSf8_D? zBp-cW{I6ZiCnZ71r%m{L_4g~zkr(}d;HaW(AgYN-gkLnfJ+;~yO zP`q@~0@_{|5MSCI?h~fd3kt}E>H04mt5MtuxUR$ zC}DL|H*alG;H$MsK1sh(b+n^b7E~Nsn+vw2Kw#no>=jM1_|hS9q!{HBjSS|j;s8#rSOvU7YysT-QT>-D{jAf{;Oz>K1$xn` zDtR4+3Bj1P!_m)?(xfWVn&75V347f4KDiQZ2|!o$_pE8L-no1ST3DA`dmtUk7{CxJ z8WFnHwgIoyjNZHa=M->F<*qL#+|Z_9({#b(8s8g=>XieOZN7w028VObH%{ZmwV9fN z&%*6Yo7-R(AD!{mM(Te+I2gbO-$PfIewG`pMcMXdG>eY@Oi7Sjbb>mi#($c)|F0LI z-~orfo4e}I^g)gHU&2ZSP@npb9#%V>zao=Nbr{O;H3!gvm2^vD><=Ip0UF`QV_qv; z1|J9(IFz!Iy1nvPVJzL-17uzCZ6BlTFcknDL8bTzJ+tlH6k zX8@Y23DLx`IY{I39+7@L@Ts>t&jJ(Cbpy#DP+s^cTmHME#lNoxb>7WO1kx&g05|#q z0NHC2wLg!aR-RQEJbfCBdVE(8`h-0a#aDY6Srb?AJFz1&31&M#&2{2jV{85NG0)WO zz}5PKwr@ai$RqRkXWkYID+6^pnlC|1wK1#CC`gP^FWyz5|bCv{PO;{)*5`5=(XZ;7#7~!4pNq3G%sCI z0xs|gpq}f*4RSzYAy(8<`F2wDK3=yRaOMQ>()|D;1>{G1V#QN<45}>e;_jh8R zNhgj5KEX2j5<4TIxsw@l6b&Fa3($!ETWpa|`hmX73A&N(2gMT4!`Q?`l*XKo1HqD1 z$^Pb8*lu*-fRbJN*uCH;Sv?^4h@MRUXzm4!e$(AIM^4bUSk+{7c$K#Zl{J1}!f1^G z7JonKn(d-b&GVzVW_qs}=3&RGqDMO{T2`S>Fi)2*`!#)39U=5Md1n zTScWU$xzaf%V7ZY>3G?s7$$w4-H&>1-*~3E$w#rvcGoa&c zNiyO?MuGgoO4$_>ObsL%Y6QlIR=y@-(&6Q+jO1ir2=ygx$il?LM4!m>ztLY`u#$K$ z2XM8%h0+h}xY`MxvVl_fkCAt~$^rs5)sBD3+ty5iKMyzzN2p6acBB#O_|5xs| zkwaP~;ziMDJuAu_J`uV7X>6_3gM}e{5J2}1NYGpGtT0|m8l0GAQIR6Q+pKESO#fZ} z=GI$oq9_W=wrBqVt*1NPf7kVM8c^0;NT|&Qn}tE%^WaDiH&%|7)^{>R@lv^M0z&?! zetOfwp8qCCPC&XIi1>VlshCf@6 z3M|2y%|U%$T72=kN$b;1Cw4{mszR}@3M1atN>&}4(uj+t1Cp%a8OP(k>n)P-RvV-k zyL8>I7xV94&Ga%5N>tQ?cRA{nc*F@58ao&KlzF`5A$i5k*36lxO&?Ls9Ww|4^M&FO zJ2v2grQEL4vFFNZo;$|Kn-kA+LL>+w;Qgrlt%0EP0MhEO(dPrzf^4@L5W20nWl0y$)VJhNVxyVksRKH;@u3ioCkc{ z5H65gvHWK+XlrFXr}7Q*xX61WFAo$HntspKIs)Mlv0lO$!ImQ)i^&5s$&lElcTlg`)depDjhC+I!X|M|ZO0CsOjV_-xOEU}>XLIZ;K*lQJK<6?F=}MJIgJ4ME$>D8l5S z*+bALaDGVVvh^17mUcCtF?T$wK-<%!NGkvbQscApN?GgY4ECrlMebQ8{zMPJwQ({@ zF<$SV@)N)agL%BZZ3lZz=SbTQ7){CCEc^GvGTFrFViTb_;-jOsDMYJ)7+|O+5&trt?hwgyV&SlNzXY>dqF8wZg!R7jvdz>I^?P6-j)z zR{c&^#ZV{L#`-k9L${WoEP3lofJQnpns)}v4olUFim{T!*~?GNIQ)SjBndKE{k>M8 zG#ZR|8Ca#P=u@&60b#GrnZrmuA>a`@ww4}hOMBe#grZ(|)ze^!bjQ|-jCxigETJUs*a|%6;hwLp4u-G{>q6z z{?A0W`$>#&=nHz(cE_LZX3S0ef)Y=u2ay~Tb0$)xeJr43_I>m~M@Tm#Ru})8*1kx7 z4)@l9gJJ&JD#vQ?gMoAl*0}|AGpWAX)QbPUUZTxSRdELAd0^_H#KI+DIkp zl9 z?eKT~d2r-RBYCSQgt>L_^kNMHKf-~8uOwdMvsDC?gr}DZ^$j{Hiw-oXh zE=2C;7pGuqdz*N893vo?>@;L#BNCSy>a|w@WVMfQ$I6+zbeog z{-)}@3)JJaHOWthR}|DrhyG^AB0X=SSV_vq-B9{G*$I)yG7x84HNtGasPj`+YW{fd zNOv%N4*QbNH2v&G5lakaM^N^3^xsO- z|5?iN>YNaDMv=5#=jFR-j5{?8_D{m(KD|;J19IVb?1c|-BkcK?$C+I5%Xq_+d~|S4 z$LZ`AiY3n1(9I+BRsC36HTzn5CdxcRAmHlR@%(UFYvcXRV6U!QRC+E& zF2Ch5&yyv$T!2u`W7T=b6aC8az*3AZTvY2eOPZZa4*gcr`vGOQ`%=I7SOgHRya-mx zS$oJ7Iz->aV&A7NAPeLxA`6*XwQTokbR-e3uum+l;ox9P-%myt=E+%t{&Ok$$dSXJ zLr(4-56o)X#vU2%9Tq8C=&XiwWMN_AKpu&k0IFmEdJD1R7?V|vR>5ZP=nqoAiL*S7Yp(np&8g=3O31KCIdrKK2@ve-lY2QZ1uEq zI1f6$n`fr_=+Ux{nc1{sjq~@MtcDt@pj(loF5}y6L4`(Mr3t$IQCSkq!7UfoZNhJ0 zxl+cz&J%$M?4+aCXHe+=!~H)cmr9)0VGd>`lYF+U{`{%a89BW7BX(voP`G7VrsFI69_r3b!^$s0O@QI=I1ZuvM;~Ke)-`?Kdge5~0V~f+(&O%yh zYCpJgI1_YLVE5l)G2A(%Rip8liALlWB8tu3<^Vh-dP&CM_Et4+-dcRhC|2kFLyOS*v=-*s0=m&d$YNP}zxBtEQH|H)_UROfC7G={o z=^a}tNEKA*)f;zsYxw^>grvc$ojTV@0T`b70v^vq^U&*p)-jp0*+ zo(eR&?@o?N}LROjs zfJU2VBHgjoI=gqBYQ!j$S;=wM%Fr9*L%X8CiLgBJ9 zd82uWD6FKplk8{lv7*bT_*0O#_`4wQ9ElWyEnl&agYQ-GuTl3@gyXBver`-PguFre zp9Wu?1h<4V&TPtGX&uDLz}cxn-j53S=D2~Nrd!POkyG~%4M(hh=_N*bMh(@N*rhD) zm5Q)hsSRij$q+q(k8Oc) zIx0%C!TMfQ4uS0^0i4EX{$dJ5rUKFrH;KW@U+(8~Gk?3tCo>A5+RA>#?WEtkj~9;q zfE@VXRK@&z^+pJ~9RWCCPncV>NBx(o@^{g~>ZYAKpY;JKSu`67wy>V`0QFuHwYjEH5v;>gb-i^QvgON*{p@@q1qAQll6-vi zUm>uKzC9J!kavHW8QYq@1z{@;;-@FixKJM8D2A)Ej8G(GMl$Q984{v#rGC_3-k@{e z_%#XKMaZ}(PZb&8QC6`@)8YB6?;^5*@ekFm<|m9j5#Jdn$H$!+vAWw1A}FlUTGBDs zuZYn2SM{X@CX33$nMY_$dRI<%(Y5-N=NS^Ie>%~v#N*;O+I4F z8-4>0UF8|6gu1qlM0ilOA0<4@hbKl@b7y^YyQXuvTHJPHc9ptDW%+YweQZnOBK4rw z^FmAdxe9r%H>%^|A3Ktj5_IKC_o$&dfCpTm44cKeK7D^@;xk5Dxw7Ufs}esY0tc^N ztSwbm@g*Mi)>meCMivSKTKYREHJwIkrdSYq^A)1-J#Ljkpl^%g-D zi(c~X(r{CW+^l7X6%oh&x36*{JsP}>RFO?sKS2}?Y<&Xn3hw^ZEDAVtcGX5!Jkb2T zwF0KU;*7C=QMw;nD@x{{oipEvc|Zhg<+CEV& z?s}-;e1E%iUqi+fB_X;^6$ zo?9kILBh&_*1AXe7fioaDlg%hIf#@!W zDuB`B0HTwbKT06XXDdsIpmeMxP;S_fo-sCtGY{7` zW5mbGr&FZH^S>?ps~j?lklr>2`B(U1?(nD^q4navj#{|sP?ozkV0jwn(a%*NCG1hG zTz-fI3a}vEb44#~!FB15iE9M{s{sIMb;)S=etf?aq!6pS8lts4(uO?>hDd=)!}OYk z6Bq*sn45{!oE#5DD@##y2jT+lKnL#wIuFwFDS8iSX^^Y?pe-&`4DQqiUoG$Y_iuak z-*Q;%(oRzsj%LYwZFe2ekhi}*6JX0>$DP7IR)2az;G2ofJbKYxvNLfzITZ3N?``rf z%Qy)zovij4YfA60%X^3ZE={;Nl`a!O}=8g62Ya8nSNoWhI)^iq!9a-425M`x~`YW*x$)8=#^kPOS#p`y*By<59 z+rUHZJ?6hfOXM(sxiYAcOXd0v1zHpdiah!Xu`{hgu~HgecI|l+U~hepB{5SbGKOV3;%YffqOAb z+MqvZ6u#p>nC(1o3jWb#z~jY)df4}eP?n`%I@}W3QRP}fwXExKkp@+6Ah#g04u@S@%3(7 z6EViO?e?wec*WJ6rdSmeh3TIUk7?qc%AGSqm1~mgaOlF_mSM)NarYy44Zn>kh!!^vsYEo(V>N-ODw%!(bu5tun_}_FO_U0V zlUPTSV*r*iIdeh?l6kF~U7zEFZ?@(^y6ME~e{k|J224D1n=@fb zhWEM_`VQ_2YMeHeb#!!`jizjefK;x?ah&9FoKzETCM%=cpOG|uBnL@A$ySC>%PPZ< zE%EzO9Q8yBO9k3%{w2S=%u091p?lJ2y|SABqCw6HL+<8y_%n0RvvG~`?>d+DH7OCb z-=>|OsrH^cVi9<7(NqS?6>zQ=i_++2;+~4{Y6yFJ z(8F+YFnq_!g4$r7z%=gE64K(~s{xF)5%&fW--3CrQ?@OS@e)rh{5yU6eq{JHaf1`p zHxA@iMY~1&EB=bL=Fdz22-n?SafIY0ZA9~UGR1+8q?hbRi=qOMPd@jiSW2NhF&krR z$0g+9l+;Cl#Jc$H-{Wqa=DeB@7(Gb}$J21oGf06)x$sE8cSL}(5MHKzd!uMI4)|Z{ z+hylpWh6-EN3HFSJ(a7ipOVx_2NKO;zOV8)##pp+^aA$yx#6hfPXY$Io(gwh*IJNS zCg_d}GFpiMF&X%nMwXL_N2K%#dA&1!Ci_SC*_`HCHM&;(-VrtV&92`h8-V#K>v+0QK?oLNv-M|!nvY}7I4w0hm$2hf?_6}RfIkOsg&Dr zD%a#nD;Y^>zv|QV7s3}=9==v!DnYbr)Cqg!sNeKT`l^B!c)_f^AS)vMY<4Z@WC7C_y((t{aYM0qX&1c9QJEA?OsY&KLE)0p##E0jEx~i zt;*Gizh^=LiG_|qe;50Xsc29wll2~3`ZF@p^|@j~FzF&!(l`H6-&ZsFC7E;7Q<3EU z4#Af(?X=OZpt1qhN_OxG=?5$32Bm7=yf?Y}5aic)Vamv7!VT{wd3=m_ZHXi-Ae%cl zp@V#hTbp(od?fYVbHDFjeA1KG13s2xqHmGD_eYXxJUQI8JG`P=IVF&Xeu}+EP3#j6 z14bcS4+&%rod76$*2lJ#N!c_5a=j9iri!$_PD4YgdQBx3(9t0WH!*kZ8h%I_$@RCfdk_LXWZR5R zA?-m>o`Z)F=WCtWs>Puz2rIk#u34kxJgYQ21) z-nQ!dXr(m(ss&wHv|Mvh<^ce&82GjwB~+B}p!PPjOZ;|CRtWvd(u}D4WOrc-EuQU; zZrG?4)8OAvplit=JCGBiT*jxPCmc&v)XS7fL1?EP%WArRP*(SM7iiwMl69H7^D{^t zcf}t}c4-$Htz^6dPK7IQ-x9o(jNV7zA!~YNI(=u&Nl7q|kf1|ds%H{Y|5z>W&1L<8 z#Y+>GpYPRmf=w$TtMd+$p(eZWs7yH_4vGaGW0PUUJO*%(A#Jz-ik!;JfdQ{zo3A)- z=?J*-da;!lCaIyMc8F6L_pQzr%puMKc^S5J#gG^Qox$?HT6Q%q-f@Sd|&JgeX z{QO2jNpyX}`90Jf$R%O2FplZq*-X&^NJH6R-eOpt23=_2735I*>O8Vo@4f)RtLA|x zWQ2Jx60*4USkFpfweI^<2^oKMc|&+V3QRJvUO}Q-%p3GA50!%ANSJ}0Y(cHyt7l_ zP0#YXy>~76zw~bk;FMqkz;V#s{i}8JpoPjp5)qYPwu7>3_VE2WYOc;uiF%KXcpZ~q z+Ti^Vmsj~_@J$T^pe7t0pDreU;IN31S>ye+mg_IU3`AuHgQc z3-k)rg!ZBG%}F<`vaPHB8o&ZIkrJvdr*WV}yTSt3ch$#+59Ey2wmu%Yf0E@BeCF_$R|C zdV8d|LOFXrDIv~(9L1b4?g_0C?dj7s; zECXy01=Q>*RdEdju6NOVjAMExjP1DqNxUxALx;z6!6Ao^2%4Rq$mLs5@$}(fpiAV; zLdQK{66zripZnA(c&OadShK5;qk7bIb(i$0s~AAf((++J+a3h~l)&YuEx>6{JT z6#W=k1!n&g00E^W_OmQS>AMuNz5h76k%t0$4(DqcEZu2_`Y6}#qId=$sQ|t_33}wI z6_vm4h<`4V%#)X4y9l9L#`bE!O58fPyBbP^j$T%snSx>fdtEc-Iga;P0`JX$0^Wrv z-34n#@J6>$5aL49eF5Xy6#PrH07HPFDqN9f_MdU{1yxzOdQI|q)Ph1Jbea<{RRCrc zo(vzOpY=V%S4&(UWLO#o`31qDh{^cPpg*~|a}@j5aoS=nFi~&i0a6GUqmDJ>Ng_i{ zJA0LVzu!qP`GJ#-*QEhuP48Y~uO{yKQ+@qmFX5XR-tz9Q=Hw)yOAuLNQj&He!wml+ z{gd)mq6n4`TqWE&BYi8OU=$ZWS=~8s6a2;a7nuJ0bNNbE)j3P+RVYGoCcv0y1kke~ z*GC?NAgEu}l?^d+{=vLed0_K>(Fdm6pj)&3wKi`~f-UT3I#wv`^KBBa6 z(UWrp4!M|n>8i*B=+~!q*S?}nSwMWp;AF7qG9YcZ2Qp7s+^FCW z*Ax2!=|$qH>c2~m|NOW3`cfFbbG@IEBvj&qW6xKGHsRa57H3kkO0e3Qy=mFY`GpU0 zi59~5yPho3flSKlq8R0$G3xKlNqJR^#Ek>4>A2i9J+rt*)XI@lPqaeTPqwaJsO^9& z^b5CSa;5+7obMnAti&}ADHe-4z}8=y@9;D^|ESa<<%`Bp<6|SEDWH6^5^Ui%{dU{H z@)_1^n+%#G-j=@!gJ`c0?m#XCZn?nM4jAT^XR0-hB!0#}a6%1If6yf?X+VYIwKbb< zCbD>`zbA$9WK|lGUMmN7)O428O8r!qkculEr`I_c2xhyLf3)#Iztm3`8%d}>U3Z{H z$WvvFMlHna&UHk+08o4E?ke~*ld#o|KQWyA=n7c&?Hr-Vnw1J{z;S#-o=zD%6=1$_SPM7!T`rN)z}RQ_!*y(LpMFNlO0v3-LwWL(!Nxou|qKZ zXNh}SgdL|097bXFEqK{?Xfx5PO8YHiF&H?zJZtY;H@BSN-aB7=6$j3OFWmLReu=&M zYVq`TMgS1rr&W;#t>Sxv#nVm^4k=652bBcbYeRr5_MuMkvRaH!WdFq%pFeY&H`h+i zxSab`lj8r@3q8JJlFb%HVWacnfsRQquL~LRr2T^|Vw18$gFJz(>REyt9XAhd>b!uF z^F|GT`HJj^O?MC8C=A_M%Iywpkr$17hW}aVkXpSIlp)itscche^lk@B#(1S0 z%KlJ>-H*kZV@emQD<-z)f)b`+Oqdomb_ud#-pWAekJlb9`KVvuzFg*JA%n8K7U`!C z4?d7y>kM_2PWS-(AS~gKvTp-MGu}L*So^QM7t;F7uOmS4Bf9_y);<+vve6ccouP6V zr;lrLaflNS2t)b=g}oNhwmLZa>esJdz~eWbwOzFznKpj$yzEpXC0P!ICH$GE?CH5X z6!Ri|_we1)thv}ul1?5;oNMfh5?-IVSdtoAg>*96o9+embMU)ykB%~W@ z1Qmp#B&E?0AuS_azsql}@1L@k@ZP)kp0m$B`|K<0;9_Vg$LlwckiD4f#J^O%y1F`N z9ACkx1x67Zp*Q;7K|l;!?y~&qx?s@pDffwOCr6fC*B`TcNE1H#W8d-TpDK}_cFQBs zjTHu=QXeBK*Qj6E#G;F_n-_t$mBkhqdquX6ESS)Tgfjrfu`UC<{_sHB^J`zZ;+rH- zt$5|!Dc0UTBJ2Dl)O-i#$uE~NM!zd)_BJF0`A<(zRVpu<;=Es)uX6FKN{i#>g9|wc z9XUp28?6Ky<=&ZY@dF8y&|3vM3N)m|?%G^mx?V3}m1q{s;4IWP$U6{^1^qIhhqrh} zUWq!MI70d((~0bc-S~szf#LbcnMA)r$qv;pvpO{gq_@6LbcVFf2zzD=vw)-#j zG4t3Hp4eBf0&h|d&Z}Gl&6L*$8Ipaf6&%ECt$tbyTEm^O>xR>&1g~))cvnQ6s$WQZ zoZGR6(=z>9Xghqr;d`QxT2Oa%#H%whM@q|vyRixOB1J0Vf%8X0VeVY?rx>nqva}bE zB1gtT0#6JGm6=eAeVe{vYRCqdDa-Qo5G>_!#i0t3{1&y(2DD+gJ>U{bjl*vj587Qh zwy+y*25r*|lCfsJmVY%HzClVc$u4!n!fX%aYj#YguNKGY>Wsh@XWX>7y_qBbUO{h< zK5i~EZ^)Ng0-aO%i5A2S^B*&bhITi7g9h(tFPFfGD7NjF{u_uU@@Z|no0XO6b3<5A zQ}@qb9w~xJvW13z3OA)C);DFo&1W~Em-z{U;YS=dPwCP-H*=v=>xZRn)#wa;v#;6HN3u!$xJo+F=R&DsDB8^^X z=$j&3lS)?CSzzxI^z7M6OE)fWAuf#q(x4xH1HIH@B>rorV6vA%O3$ebJn#+H5j-H5gVgk(VSS1+G7XhnXU@6&%~uNT`+5i?_poi& zCwTmMHHS&R$KT@LbOMn^_gBou#EygV501`%qJ!8``i5nERNlRUr`PN#Vj7610=T|7 zGv-LWNp7Q8R(4GeRpshC}{a5*b+SY@QAofLF) zeXCMeNX$zR@^77!rE5wMU-&*#nNq3y4Gz^=>hU%-3gx%MzlGrXS1cC zOL3i`X}FafK@}xDG~>C`XZ6f0*#?r7js^L;jWr(;Z{N$UFrXAENE2&+r36G5IC~H} z)xl`IEyI+_{yONXI*a0s8u7~mufk97^REyGyi>AoOy6UChn*|mNO3057a{cL>{vgF z%$qL0vbnzC_0GqS1TwKJ8YJf;7$I7;Dtt;0mAf9Aav^Odvx<*o6pQRwuM|%c_`OIF zx1QLeN-ZcGlGtQIWg2(+tY2}JZrp#9sJ2>XSGabFJk>=rYONMHKe1c35#bNAof`d8 zFrcy3(m4NFWJsU+-`=&<0`}|d?6ZZEiEEb;T~fz<0Czwd;L^@8yfYKvHUBDHzCbrc zp1#9jZHPCV*>b-2$8W6yE&c|A)m!1rZ#|@4UeQb4jfkR>6W97>(xnupAdO>>;3#sS zGn2Hk(AEjpMLylT`7d?(OEmVRL*#;mF{1oB0%cnA$@P|ZGI-8h=^{h+;q>cc@5(m* z`io2IH0uViis5DipDTe~x&fi1oedF*QZO|FR&d-YzyQf@tV~6I(2`Y!A1^v3yTasy zi(nQ^p!LY93R>|4MBsFHW(EiqR#6Es5Oai}2o- zr!-qPQYB^7cqQjHvkJ!CQ_uBmw|11j|4!5ia$v(57fg&MBt43F@gkwk zPZB0o({r_*k$23jEq#yevGeW8>-HrB@#(`!x~rbev+D~q?kpXpqvY??geH~2Ch$Pu zf0CuDIxbV=PGbw4OC)t%>8jE|tfFb2J%5+*OcKpubmbub!GE|mrFXk>V(x4H5EE$t zYeUpRe3rCOj`v4eTW<6f)D{*zY@>kUBR}ga*&2!Z&ns2cG+rj`?pHnxB^omOzOPWgoP_$(8x{=Cgnr+Qm=H^e5X zT;im6IA=er06X_A^_h!*%VGV4kaXpHgUQHUK38S)U48Df^3yn_b7q%egR)ApR>qm# z{lh%?#i+ zUkVt7L1)=&9hrsoqEoOAPkA@co55Sc674HdUK#c**mzwDxXs= zT)_7Nz^*%eaLo)c}!ruDK>e+mJJ}WGicn5}PCtCO?iNdunGLaO=CCjmO zG#m7C?DRoWI@{)$W?;CY23eI2UWlyPdlL-Z)s!LbD4@Mp<)9Fl2Ct}rvL_8Y@QBt4PPV8d-bS@BdkLyBaL=AqsG|j(5ZR0O`MM}DhB)BYo%W( zR#~M9xXZi<6H9Kb_oeB0(-DI`xWcus#jh3JqlUbNm`?f0AMVtR(>2=Bg*@SiEVOD* zlsMi5`Og-t;E~WGS0UI8>>Y-8tsNX2)5F$|9i=Uk>4*7HZItJ&yC7d>T0DAkQI|8J zJ3b5a7eY|W7XIb%WW*T2o%pr%8my z#qiz8S_x1p#0f1O>4II5f>nxCB9VfEpQJ96>5L2*+-V^>SF_lLnWlS4ch+M>9{Cl* z{2!7Hhkz>KLZbWZffiqDG&YMj_cboqux+u(r|wueswx3nLAc|c)E8imu@9w3mlcmR z*tGGNy&i2b)OP96+;LigR#Rs750?qae z4Iq+8r!P<+P(w~*WjH$=FT)@ts@!U(javmlKj;dVF-cE4Ce#5HSKu53AqfxZmB(o~ z;#@1Vd%CPuo9?9ZpPu}6lw`sZDuYu;0oLs+u7#-M)>4Q_vE~LL`!|17CTxKPm6>Uo zs>kU(x8NLoZO3)oB#Iqs8av3GdE%$<=C&~ms6#4vv0o(*d}&LXHb21m{G;I4ohSUKr$zOzFtEEJQE`w|Vd971C6EpR( zgW30xsVY&lhC&%TrCCeS5qBreefqLss{OZum3_SX5m|dBERk@Q%H}#EtoB>1^TVT` z_Ols;3)OXWo;6pvs+#)r0FDzU?Aq|L?T@Y& zAA2Xxx~Pj7YIHx zgc-l928z89oVZJW=-)_PP{??r|^{Rm(mcV%6* z_&WsuROfq9%HJ}mHo4W3&!B2+tL$}(xq%Jx^&4g;(?@o9wk?R;zjeMLwsu<6`S&A=(#ltK zuRu{jn0ojeeLElN{oSQu&c2D@r7aEyZ4r37Mh(C#$|f^0&y2X2$r;^biP{J}%Z;9y zQw|X8VK2qa4zEe+QwIw&-oi_Mfv70)-!tbsj4@+Rp@ie|}@~H^HL# zM%?LZhiKWK0>-W8;T-qDH&lbO#JyDI)gb3m)X~a#_T0#goA4vUt@%FO;I9RN6$OLd z93WRsOBLt*-V}5lJsEYuoWNHh zf-_Kk2_iusjbFqtPRo+M@Ta_w6LS&cnfs;YkS@CaFEr-;m1tWTbh$#xdSWq zzA#FNVF!stk(x5{nt6BKzn+7rAapH+0(tcZLC0{KZz=Ik3=44$?Pq|gf5Rq26zA8I zBvp2Y4$Q(iupiE45Ltv5R_@RZRAy3=?AO!>E*ur7wE!?H5q!G2x=yo8rLpbVQy2LH zX7<$w+JfiS{&d*9(~#&0V9qm6y0ecXH}WZED8=Yn$rf@!_m;cVfPI+AhXL);?f$cF zxLQ4<*C+e^NtD_u-Icg*d@HRjgdnY0Xq5nOk_3X~gN9d6-^U;Ms9=I2UXqYA@3uv! zXRYYx)OI)Mgl^^HaPJP)+?TG~OIyB=<}(%ZJelnLwXLe7p?O4r<^!{auZ^m?$K@Un zi7FEdRC|)iPoIR_dFrL(c89pHKWPYR=>C0O8Tq+uQvu_I2SCyILxH-w*;+ya#>rXl zzea#R0i=H7y4FJJ&Ah_z@VN#2y+7_fh;|ZX-;Myo?Y${2#rn#_Ao!FGRkMchhlB~j zxBB|}BBtX-3~x@}i$)w?t`!Y1tQd)Q5`-F#gc1V3)2n5IB>KM_^dc}$s+9=d@JV@4KdryNRlwXKkG zJ{{0BNYqxguB&H98i!O|5F?uW#%wW+PIWmMw)!1c1JV&YF8zbzIrDdW5PN)H&OT`X z!#~Zi)w}b_qWE-BOL5j}KUDZ(P?+9>J18bL_?*!_i%XxHT-0^#l-b|4Qo-c!R3v}8 z)wdK&M)S;N=K1-Zw9SWVrHY3yd1N()yX?Dxf__dQ22)cW?8O8kURb(FvS0dnHNa=(mDeXR}t(N8a zrS;%`9y**naqKuB258BM{3$5dLbly!IFZIAOy+ZIXNg8 ztkuUT76=!a(V0e}&tN^=x#_&mP)rSK)0c#|vZl=S4vYYY459yf%ny_1JycbHHZxwD ze`y2?MBv?j=96zr6qbF6^V4x%F)Nc*;1tE<@rpj5-o0~HOQi0(fodb^pn@!L!6pDM zr*Q=?2S5bJYk+>K1BW5YM5bFWBZ?P1IjHLZ(#6AOZsl+M{_B6oTF7djiQbR|M@zs+~c?Y_IZ{lYi9qko9{1Z+FF)sgkF;rf!72kW3JyA8QU2LPWrVp zXLOK3AQ@jb9vS{OB;gK#3()hjRtMz>!A1C=a-p9B`y=ODkf@-FM=R!s&l&0xWrn+i z&QsgygH|LS)Mjo*PtyQJy>%C92Q2Px=%?5O>mm_p;-k)zR!3M|dKZp;k<-?S5oSO4 z5~E1EqaO0bYoIEbmz;}{5aY0XyIPB(*vKm_N$(igq7q&tM8#pZzYFW67^!A%866+3 z%D4m{DEjHlGY$kJ;v=Mevv{45A6geE}` z;)~xsAr%@1P^UorOA?25JtZ~k2grZ2h<3mUI%;UObOR9aK0o1t^y2OD)3NklB$c$v zPe3NAKTCaSr=S65@gISSFN7m1`4;laapWaGv3nG}4z&RGr$EAUbkPCDF@Vdf(PEe* z3vTyF*2a$Ixe=ik(P6Ji5%UJ-hZ7(^s6t)P2H3l6Q}?KTGeNr3!1pW)G%!2d= z*J^@c3Qz$b0PgRU2Tg-L>I%SfPG9|gkl)!$ZCG*2qI(;)%yTDA$ipaNp8{RG+_V1n zQ!h7|sv$HVN!$3J7GQ^TCSVLzlfhhm?bjl(y)-AV@lyTH27xMn9WZeqHabU+B*rg2 zL+NPoDF6-z@~nm9sdg%UDUCKo)WFc#2?D39Kp-d=?c`Ocn_&yUElX^{Q*m{eDZzqp z3~t>B6?O6Fh`tV!ZJ87`=Z_ioIdeg^UGK1g>RAS+32ms*bq2k4APv;F$sjV4WS4-e zv4&c%9L0I!#8^%OcK(`*eDhRU*!avhV*)V-=<4nGl*IUl%mq9NZyc6+!)bXdiibj} zO_~U6U!lF%<90kWxS@Bz1V}Kt{X*~J0XQqKF>QuX4TV>)N~XRrwKDj%31t$zeZs6> zKZcax0A#^+6-*tZsLRjSG*f-AwRim)#6-v^sZ+xM#i^|_0+TR5Q_!<9g35_Svl*X7 z2}sN%@a?c*3Y**L3_u)*vJS}We8QK9B1etLo(dqc3VO0BT$oq|cynn!Bvjm);Mgs` zH$M9whJ$XvaJ|oDIAgwugA95`ddVvPE*G5-lY$3kVVg@TLV0u7L8@CH{cgbiS`E69 zug%inV3JpSLwN1y5)Zs72{Yi+JX=yWp7RtGXVlPeL+N}|AF6?-TF^j5g=XW_A6T{C z7NnW&oTugFoc{1NX((O(3UvA-^aW~j5}^RxNtqgi_0AzPDDQ!!Z?nSh8ffp8}3sNX&)4z6%! z>A;aU6=0{A#9C3`sNDMR&WMD0_`461QFCATgoI`(7Kry=y?XOzi$7dC20M5&7omT` zwmggr1|BCT*T@LBkzt!@#BCL?Qrk?5h<4b~6_`~pMr zXaISUDw)OMJ1Ebte1}fKCO+V$sCsJ}pcUr~#N7i{YAyFXfwKD$LvC4RGfmn>^gDjK_Ov#bdSoeuXV~T>A zNBx&Ie={gFxxrVY(K_~)>s;SE*N|!PNGk|%(E8rIl%v6+37p2Lve!;?Ek|kxR4{K0 zdjH>Bbqe9dc3M?>L8D9$F{;Re%ML<~=Y~LH4~5I#$e-c7c5TnPj<2LS(30(ABs1W! zf#)y*A4w%38^NPdz-pPr+nZIt(&1X7u>W1bS9-7zX^5fcZ<}O1l-=(}=;9<_8(O za_F#8$yU%_HpHw3B^tjk{`4B4G%NsNV9;#LCnV=F^Y7AP>oBKOUrD;wD;IF-7#u4E zyd=asUb{@(i+QfLDn!aLBGE8mkTVZeQbAxzv&K`zutIz4mkk2CVRqz0vV{vY!DWA@w_M1SE)dkN{haB`cC072jfjy{ivseTh^H{;(nPrr!snqYgs+zl{$^ zjR9zEKzKcOne=dXKH^*zm8J1!rr}xNQc%GFe=SucFRC#;dBW$b@68t&`vp5yH|~53 z@^;&648t#tB4N#Z9`Fxj&MFw?(YoHJ=Dw0K9Gxlsa)n~}7+f$QeMZVs*;s6CZ3jqp zsL`_^-kUsPbY^DUU!7q+kJp9#uL;?dqykiS6?ygTm^K?I1!`&GoQb8)N{-8L6o(3O z1VoQ%!2S6-Bz@U-`!jeGwDGT?^AiS+Ps7m0Pp=@t%I-D4!+MBQ{?0Per6}5uK(69~ ztB)s3eK|n-LQ-9o#Cq`?36k0jy`H0 zoRLAGV6Or~#Hv?OzE@9Url9su+I(aimh5RyZOjZ2`rJ{v|Ig>L2nnN-2?gU|EH>8OCeKIk!^D@tu&?#w06qMx9n4X1v;a>2K zeU&F%IuZy=&6L&^i4@lDV|;yK2R0NUvFJm-}xNef?5}|6t6!?MZK=C znch609qxi=9tjt6ho4eLh4nD1V7$~lrs(vkCObJY25D7Vie)87D7*YygeLx^q@;L# zNYH|L-^`uvcAIM?V(h%E*Sw*hz5FbI{h6yHn^pNhd&~|kcU9c*t)}hV#2=yPhp8Z6 zQG6eu8~2K@2(`oJ4x(|v0%#7TH#NEj)6x)no#pwSXEGJk&xafdQ?7K} z>clSNT2+XYA&7q=7QN4L@AOhOI<7LBk#D2lsyfTh%So7Xr>(b z9X3d9f{y@_=e*jl5Wwq)V6cS{glNy{Y!0P!kp-L|*gScQac><-MYrSUf1erDoMsQ^y@ZB#vLS3?;yt#fbHX56A(&jxva`r!2R5}xOyTy3=SQW?* zIs~+$t?#2|AF2&+ZxkpjBVqnASSh;UvmK2$C*Rli-s}Nmz*v=n^*~3|T)wQxM7X#b zAkcPs=rX8m900em!1$le?3D$5o`BQCxz-?>xR@B$F|*#Pu0`grxVX;t=EXl^X`yWD zo#Q`U23SZzff`4cLImvLzKF&q3LS-(8f}#^DZ-lw&jS|UgLeE;*`9hKPeP^^@(4;t zdOvPkyhlNbB}7~1-93ur)**|hRKCKc*yq_K?0zp5)W6N(P-sQaIwES~FdPQWDDE*j@Yn=u_}KNpOcrY%KG*x0SzQsKluf;@9VdbvpW1qA zrs8Y@c@P{XVvfsv*R$??^D#DZvi|8-ABwEK)=>gPJ5+PJlIzMd6UcuwhZEdr%q`#( z-AVh7jIY&*PpwJM^Clog&PRS^9??1evlas(l@X;2&s~BF0$b-mVD+-IwSD7y{~&ig zu(*6(2?4yp^Krp@AY%_QH#kfLmJqvI6t$kpLL!IM#E#BOK;lZ{nmW$Z;R#O# zKu!VFPLX9h#l%j9bi>02y>{WW)X+O@*sWYn&RM30dy_?8+{V2V(U|DfA7f8ii^SX; zt7qDR?pZ9w1N{U8UF-G8fgVC=%jBR6^v@hsw13%;(H&BsM+EzHwpof3D`z%sWhR-` zcQZ0F2|Or4ARds2>S_iQman@;uLd+spK0G*21xIZ)_-+ZvOEDtE&le0QB0ub6!LJ72zeYg04n$2pmM*sf|5Fwqfm5M zegH&@-@di9buF$EED%5|2>gx1h6jPL;4iqrI3dB%vbsAGfDB;o!o?)wJHDoSCN99+9XRmvD!pv9H{MU?64p^m;a>V5s>j`l)XilT|%$ zoMLB_O9^;Q`%4Qb6Z9TEI`V+784XmaX=r5KMfwTcWu@vk1!x_6S)#B8BEZQhq#tgN z=3@voYAV=MNdb-2zJi9^tfJsBrP5wBBYgsukBACmnGpiB zisV8{L9oyHa4=%Hko@e9?2!*!)Qbmg@Gs;)d}#nh@I!IBHoM1xP)jr9EDV`DVCjA(=`iXXidCD)cL=CPeJAD27p*R5ZHh#B=2$ zUMN~~JPt4?XfH?dpv@OeMrr{B>Qi%e{$m9l!2Iux)Z(LyvJ$Fe%!Z7~7aaMQs*v6a zFA5y4E-+6M6xsJD@l2+h8;p`Xh>0Tl?gU% z11W&?L*Ak*;Cj6FNg62FHg!3JikWLQza`sR#nPKOeI>2^bib|s;XZ(7M^azg0s6ir z@Hl^KXV60q)AJqocg>5*0k}g8K&QR9C-7pu>GOKnWI5v>AN`8Q?w_J%#8$m#epy+1 zTxI}#f5t}u?f4-Au>mi6j=_eq{vF1g26un$kyF}pNMP*oivs5mY@-O>9^nwv`B(L@ z-C|iq&3HGXUq#WezrlK3#*a7_WhY^{ii3kg(2b_=R8pYOz6Z4v7x1qsFL%LO@~PXU zbZYL#SBKNy?`<=O*qCxR*`7OIV`q>nE;G3dA&if7?)vYzZ8N+f-0@1>9p7L%jS2Jx zL=nN~+g-pkXgn7Kvlwz@rDI@$at>t^;;J)Hw3TVbj6fS>OIh^?G zpP#^A>|PD_!n8Wj@Q1-pnUq_X%Qt;bd?76un^_5Ez0QD!8w*A?Ra*$vZ)Pc`QzK%HC)Lxc z`Wk0ctXtZ@f}fqV2)O)hg<)C19$l8 zmKMIgAtOMTR|5spEacx6TmGvPe^7`zY@*!1`e)$Z|K+oYP(Ujv zI(gXw$S4~Scus)P;`W@p<%%v&N`E+UrSAl`ex3$S9DE&Ovss%VD$fj;9iCQ{MCzy;% zT!7?}FW_v-j*h}=00v>AE0G4JTU_Cn$zd)28e3r)_1D$fAvigd|8Mbeah}g4OhR9g}aJ>dn`Jf+dq;K?em$E0faV zp*Cv2P{4~QU{)2N3@8@&OlH2~InHY#&W`Hvt8cln9C@d6GXc02_MaXQRD}4{4j&2F zuirP4CH1>ZHD3hc-D$u9Xr%7&Cy1usKJR;%YK`}ULcGWSbai{Q23+=Cs$eRg(4?F@5h;A`F&`wp5I9(F~ z@y0P&pK;bEWcfufhwNhTK|k^#)Lv}54Yq5LWcbfVnnwb3#62xn&t=ZPG5?Z#6#th? z;a^Czy(VgKmm=lp!I2Jpst1<=c{8e5IA8l(Pn;icafeRGXo>G|#S6nHDZ)Qo_99tc z0bumE-aFU;1vCZWnA~ci6TgEC!`pz$xh{bbiIJ(^nmT!JivB0uEcQ~&JFa_ULz$q z!^X;KA^{@~lxKdI$}^T{VSwW-Ma#}DM8DbU;&?S@Jkoesh3se;QLOe~=psiB%BRKYD#$s5|_2KVV~=uj`sf@h>R=1FMAH0sGn3G|adtK3N6h0i}Bd148Q=YCv8Zzl>*8zkb96nVN*?r;GjXe^D%f@14mw0#xI;>E0!r`W$aUuo4 zO0wvOHtAb-TVr8*6^pmY2`PyNdI?X9-bc`S01@}Xf?hM_?&xn|Cd~PSGKfE8U$S_v zF+U->fk(XIa{V`R+DUDs5n%wQdivT~*n#R-NMOrbX;eqwT?$QwtUVnLd5Ul`J4^y{ z1rfS_vRg>FK$3y3Lgmxptg0V!83EJ!2EEfM0g<~!0v{4yCJu!34Ecn7G#lMl2l_G} zD*HCJj<0Jw?U4HReC3V^{*e9nb=X_%1v$h|oTO()^zH}_<-tl}J0yrOUz^{#vJsqH zLmt77{0^YA$%)z`TmU8^q=0~KC0M8pWQYuFT0RDK(PmW7MKdH^(P%AjatX0iKW#X$ zOnzr5tmob1NSGJa?Y&g3)2ZJ4aj8*EEiU+d#W3icX&w z^~O*Y55!LA8c1_TpvI;-6`Ru-9lFarh&!MgZZAIl_qA=0A`~%;oB56{3!nzomiY z^Y=Gl|A3fR=;)RQgG%hJPabD4EREV-Cmc7Z;bx>A!*5SNQga=uoh4NnYd~J{81Sf_oJM zeZ0A6NbkR#>zPN#w3#FQI5skactA+qQ_Z>rsi35rAC-)*W#&wSrEX9^_`BC(f0FgPdsHUW7bd*3GvNjIu2Rl@YKOe-M*7T!Jz;L3^ zwA-DDvOZgS2GYs%>Q^(ay8<a$fAfpiu&**^JJ?3LYt zWZ0*0?1gmYQ~mC#AAA*}No;b`o8mmDkEt3olA^41k;z-;?F%vAn$(*W?Xuj7Ece9u z9iHh-PciOoh#UB@xfah;E?kNnyHzX4-WL-RLCxW`gi|l*(*YVayOQ2ieqg_x2?dJd z{~*2TUFN1|O?Mzrw4{}53)C7@*TU}|l zSa$~*Z{%)x8&l!%@KJf%Cco%Ua?{HFO~BWbOL$A;E9x=+vs}jI$~cF z9#}&6PFRzlPqQ=v4YVZ$qz%@g{5yju99l>Xar`huT~YR2ny6*RLw`EQYMz2?JZ`QwZ>qAABFKB|cDO0& zMbmo+@TXfov>l9sT#4R^oRH|3)d~8!xer8#a?o3Y{Zz~GCtkEU7x#Z4C0yr^iAP+W zaMti#*3kehP^1K8774jKBSMhWv+?^}f$PSOHM3P2!E1A)(q}qzv1eF3>q&1PU%{euvnE zT_-MNCLHk1tM2>`*TEcpZaRh%QUWi+ee-KQh-7VmQWT(R>prKVXc(6o0kK~x8LMRS zB^4OQajj_kSaJ^HD4<%hz&CQkLOi5<*dXvMdIXRT-OkA4GPH{S+9>{( ztX8W@vx0E5#NsrdWTLSZBs(1F;*yX9FwMn?)sSejT~_CmkvU)04Zxk6a`Vi4=?@A| zu&v#cln9eSPpKE`zB4Ev0OFU25x6Kb`zLfw5#uy+2h)-OEbxx`ES6u=(>S^p|eOfUI&y*L!40aw*E-xsRF9)N*L#Na#9iyImZJI)B#I8qrV<{n1pp4SJ{AQx$}diWo4U^ zjm!i=G1JSHzri|c8p8fY!kQ)c+f;VoJI-4)%kaIb>^Cc<_R4<7vvy$^5;@xSVAq{} zIg0q`OBoKyLIA&muc5^@c+Gh+JWhR0FqUj7IBizZ34|dWsIYy(b*_u^5eDv#ped_A zv6r7(;8Uad`toi02{I)R7f*|v^tf&eIrW6BGf7OYZ%p$ z*YLc+9l*ysIo!z2Tx-1%>+Ea5c|m4a_}h5Qw5lIfQX;AomY6Kd&@{NhN>?*{=XxU$_9ow;{pXk}olhjUr{-hD=JHHx zNGVPJ_Hmz(*79{bSS=_{UqmF)P=#Ixguzt?;mNU}0Ol6FT}nW%tOY2uX&Ra4o-|}# zscW~;yxuF(nZI^c2(WyJ-L*JIwKYH3WQ;(LKPz@rgrpK~@rO>oS#Mw--`|wuA{kVD zTr3Kbo<&@D4X|;SDR9m#>iw4n&5U^`oQUI9bVl}_Qui~#Q#_n?He_?W+{vGpQ+?&p zPDYJ>5w|V*^R+@^sZ`A#Ko~=+Ih?GK9v1<{2LV8aQmE10rd2(0FY&IG7sZd^f(=gE zey3`u`dvgusKNv(4O>5YeW~FIX<}~_boh^r&7$9mnVmi4?xROPFJ8Qt(DA}Lrd133@IrL0G4jlhb?fO*h%Aa;YfES7@h7ew-5R*SN`EFWvj zz8zQUy&W4E<+}->?-gp7qA(|B^^E-R>#CQNl~C4oris5 z>=@b)=?)nR|DXt3IBI)d6FH;Z*6Yu#z!LdYOq@ubevXG07)B&WA9CS~`M`CsT2x)1 zzKO;HusdKJb7Bm68p%wM+uWdg4bR0Q|K*miKX?@W1i)2Q&47n*O-@{y-=Dv~7snx; zWA)!;&ivtL-026G)qNm2;5t>PtaJ|{yJ*yF?JNE1uaA1t{Mh0 zmrMNab6LH)?Of*Y4em`*_El9$7Bh1YHEehc(?sV{8zhFQc3$T{!yZ{4t{j3JfWX(X zWbL)o3mV7DT)mq{VH9d&%9H0{2U13sIQ}K*`CWwvmZ`S?{hfvxHQAi6tVE!Hl7&Yz@5m@{9)I*%ccfO1&?f=2CK0A6 zlv8^%dwq1r#Z&3g|F0S-~7rpgR>Hv0o*^hiauDh&e`J5ttxPi zlXu1X~vL_B0I{A=b9^FH@ApJ1#d5W6V80cuJ#ZSZXgKj6Syu!0oJ_{ioDN%G5GuMC`JA5>W~ee;Kiu zj^}HD@?S7F6VhdrwX8zeAdJ(;gz9#n+BdQm_mgIGB;NX1{;mQc+sMc#?g+1bQB^B1 z9!ApP3=AIYRl%T4LUQu<9ZEYv1TX9`8mj~X&}j+#$$X$T!u~=P)z%n!lKMfmK_3!` zUVRNz-JE5-03i@lNOCn*i6|p!I+QF{d2)ea6*_Et{RCnZ$AjA@R(!ie%S%d2E#}|4 z0>RSrwY=c}4i5jx8lkv^O^#+H05xS>a<7(8e2~yH;W4wS+a2>2y>r*p<%Y<|7DNjM zs4brS(;RTD7*fSAaHj(3yMiIj#WNH3P~2xFmJNc+SnMD^ur&gxncNztgG?XHUmnWy z@d63duS>X_fvwPwiUF5KO-l6tEpVom)baY{ocbF8@;+QTQ^7!&-rnDNeHN->yF6J+ z=tDx1Z%`i|7YA*{J?lOyfqEH7 zyy(VPxC2f8sEzqb&o2Pp(tyfdKb6vYdUM~k)WGKQ8|kR8?vP?=pKPhv?SiZ)gCu##+HACioQPf-Ph^ zQ{sAyPN69D_%)UkV6g*7?dR`2cH4e7gnssc#4)S+7arSlzw`J870?%%KkXgwEWU#@ zU={bFb>eprxs||L&X`NdSf-g-N$$<(#wGQIM$;T3V^iZ=t)tT)-+ETn+q#Y!$JN(c z?L;cyT+FT0B&f?RyFz{w{HC7=GGQCdmEWMdcT)P#+p-=gB7)NT73JjGLmVCFq}Zs# z;#~;|Gl~WYL#zV6e}?b+1pO_4^X84;@ovPY@*r_&u)Zlsj^u<=&eBee`Wqt>6WujyM>m!SH5DtuZ1ib~uEBS8pa!avbRQi~(qk`ZAMq`C-fFJ2xVvtA zqI~AhakJZi5j4(hx7LBEU|vO50!#JbNP(ZS?NThS+gh2dv~~~hVRMxMJHj+innP8B zVDx#~KFk08H`8sDb5SBBbgYOjYuhf|qsUze$YO{mg+`CslqjIw;Z-(>sc3DiOTaN> z!ldHkpNa+@;XBzTgRU9P)-G{p*)qCFjgiR&GR+fEY<29Svz?v~g zBEI=v@!Hz#Zz@&kf`~G(qbcaoLGdLE0$XxV?Ev5z6`}M902vLG3lZ4}GKfU&wx*H@(nf6ixPhwn? z#>CT$lKe^uJBQUsi`!qkOB>Rdtg&}BuRB@QN#b^y@EoVN>>K&WPca*+!-!Ft0(T=? z_roHlCGpc+{^uB#?&m%1&^kjTcJVVqnKysGY6wR7`T1=N_s#Mfu6?u^+h56aB0(`E z+PMQSl~@8#!2a_j7ow_oi^9Rw}dusjZcCm>O(PM6YQCZSo@XRH zl-asx)fiq>RCM8VT(-8z@nL%=*^fpRx$wa7!1nF64zGAu@!$K?H~2jusE3Cne6mxz zMfq{l`M8{;f+{=kcjXq^F6P;wY_OhSd9S?9w#!5zy!A$EdM!cGE zUp@xy?&nh}-s>6i&L>tHF*W1Au8iy%V7UgPL-HLQi+i$6VO$y(+L1 zsXGpc&ULBf9b-*ucC*}&{UGauVdKKp47$M{%er7~TCHPSk`(N(%0X;vijkUGBKGLW zBU7gRzR&nbcg4%Jh#@71-O`R>l$4U@+Eu{|vdc+u3*t^<%fR}1)MoD=qB{H6sJ38+ z7x_KE{~#*kX=iP^?9XTX2tf`e=QAUTgBmyVSc2@Xe36C%Yr9-%&HE=Ku`0y=#Wk3( z>PYn?Ch^r{Sld@)ZC(fkk{xBszhU)N4qUn^bvk_1Vxnf@ZoOa3Y>hFWO*(QZonYwg z=~-8ovmXJzif*fg%$Uy_TUF!$!ZH7a)+e+i!nqmooF z;)DI2g|~9OIkiOrx=Q;qJ3BkKT2icprCR()c?{~qB#VtG=M244>Zur9?0+E>wdsAC z!7wZr-Db7AvZ7M|cH1xA=lP6ZzUCA!%6=^zxqA{mu{QLKYo7jsS38~mAI#jhI zfW7U-_Diu+Ul3%sw1}EwepM|l-jd`DgrYY9xXuDn=OS66rHRDw?AY|Sy{p4z6>x<=#%>8SkvQ}MrX4G%Moi|>GaRo#;}*loHp?hMrX-bgyZ8o}meQFWZ`8$gi;zU*$lvVH*7 zvt`2O>C!EA@8HCBY6y=_9C4`Eu+t}CB4w!8^DI6Zk#;x6&lH!6wdKTHK|B}HtB4P< zf6?OhjqFK9r;J5e^h)8Bm!hI#Ba`b6KsmwLTpEaJ48k{Va&C#XA#~_lR9haTVj)?* z^&VVs%6V^bY4W8vB$9F`$oH=1fAR?WQjC6wTSNDEPXiN0&qp17iNInDVN4ubHZ8D}vX@UCya! zs2tesVgp4$A#915_$zcsV7_^k8{w;lYejw#Jqy1!Z$+Y?^gt{W(Z)-dKqAYGoqPU%CxU^5#g+dty1t92fRYY{r0Ry#?% zcb;OW&W_VU$UYgN`lh2gJ{Iy0=Jm!##dm0v4Za+*%WGc0Y!*-nEG6})QViWtLuK=O zk=^$GUqn+7OW_0=9vgu|%jyOBK)t-8f(nSPjE{%J(jfc_h*>F-G6&W{q=hyb$R{VBI zbrD7jcMif&Du}MznjZ1YLVWTX(}xt%?qh(k=+@SzgI;vQkqH*GI~w0t`yl|Du*dCI z5ay6-n(s7m@``b*A2&t6H8lA=^FQ>r6(O_P5Yi`5+0}!iJcYO3QWIw@gA`39?dN{` zD(`V|a$wtI?N*cjC&5UV_0v*3x^ ztRRe9Q3iK1lY)(n2NcoIkr1qO?pL<27*_y0IV_L`6p$HD3ZZbafNP~ZKn0q7Qbzy$ zXtGiAMP(`>46F_KiE)9$Gq+DAv{j>5_#izyIT<2l*L#F6f>6gn|M7xR8Yp+I%14s4 zg!R;s>duyXM^M$hdr9@?083D04M-xbYR>D+?)q?l*5UQzih~WTBA=I)J$WCBdlK3t zlO8ih!7bCZkfWyst>OEqs;X*;YR_sq!u14x9Ke+zA9F4#(A_H7_#K;eM%Bn zVpN;%wqJDy<^G#vYoc(?RV;)T_vC#B8yD3U6&3i3;a#HJ&cI3?;qEN>bGWqzAdWVo zt|VHM=$sR}xE)_s27D=46Dv1t%BXa7DrqbA@*Rsq)-}7cu~UCu+`!k4Fdf+o*GIxn zogZ9r_&IBe$~qamNp#GKFXaVK?p0mFbThSYZoX)_Zlg(1=~#D7u|e+MC^!VBHsew) zr+unV3B<2{mCZ^wpUJ2og^Yh3{MDp(ZmY@sck1owO^nj5p6ua2(qaq;_gD4e5_5`B zYjXzc(krL+KB}FgCcC@4S2ENPj+daZf^x-mwio6^Qj2C)TB9U;Be&jz2@?%!8qzpN zeFf{6llX>*hvmx92aLfik;|`z(Qi#zv*PLfpAvtyIUIa_9s9k4bjtK-R(zleD~VM6 zKPkS``HkoUDqFc|zPWr;W=5r3A+)$LYpLEy6&QkkE^qKA9n74@&40IBENqwYVn+^? ze|83g!K9j^!0r2%nI~tQFtb~Q;sa%9EQsI|;u@m7#xpjJ^OwAoLvEeyQ24_QyRI;==%zpl8Hz(*wfUjvrnl$UL99l={4dw>1ZB z&L2Gg^Liza2JMh@#1q`pqT~I#lub|l<+GI$MIo>>l>%PhHexw)&zQmqB@tXm9!g&{XTfqGM{Gv_3%!vqK8a%=AYGp6eQQp6T z2c{P>IIaCc>E(MNI>WWXsX%smrqX|kC}oEDTkoStd{X-*8YA91=kYAj(}^vcME*{ zpBA{F**Psoj!t#@T&ly!ZHWi}bY^=--V!?!+2+7E_Zgkg7W_Zn+Z4`Fs&{K`}y%TTX$$xg$YHM#deePJ$BPyYo&Y9`x^Ig=%8Cp_- z2yM1TPk`D`N=i!KLr?1BaOZ&CA-q54^7Hox6rVqs^Sq#Uz)z>Q_rA{QPpo#p3@1p- zW=A0OC$)3Bfm}&vW+s;v%>Vq8jrl3ph_#@Rxmi;fUSyWI-A^t4yQ0it{3aD?B|zf7)uw?y=_RjxJ%kSElNuvL7GV^gjJKZ2+mW&P&rYdQvt9 zVCEu%9m2yaWCg&3mft7+8~4u>D&xI`(EPE_fbi;~HIqAi-NF7prrh3|0LI|gqw~Rs z@EZS94VbpyMh(%EBU>lWkiSjg81ClB^!(Tnc3)g&xkfC#|Vz?EA{ znp5hvi2S>XA|rX!N8am_(A|FwNMUUgh(x)NpDAI0IV$9ub{4}2Hn(m5pV~GW8hOZO zkT9;$PE#cHgw0<1%KhZfZlEC%7-pI4x|S4Hw)1((uA>?KkDRG!{p2VU85%4uDoB}m zXYdVncHFY)Xb$_Ir+|r^Ahh>gS8u??t+M)MB_^o4QyS{eG z@dNRX(SMxOi6JND!^z+a3q*Ur;;ed6vmhm{Ez@ib@22}we zZm07629IAGV4YxvZc_B9F7F`rvJEx?x$0OMmwTza$WTnS!FvwM;Z3=TxyS_Xi$IM%JU0= zC#*Zq53d7cfB2@n9~`jxJ$&n^6W?C@%^bw_>w%Z&HfP|DgOESUa|^dyNfcZrpEroN zx%yDZTypan$>8Fp-V?vENNq8$Z6sbBQp0u^ii+;hGhYC>T5gTFkz8`@uP82ZTuK^WCF>-L+ zvJ3gF2L`~+7tm+aSA^9l}m=Le!cCnIr%%G7}Y5RgH@Z-JTFp z=ZNjHP`<`17}S=lXm=Ti$0nH0`aC4?w;ebvYv04ano#mc^dyLm1sj~ml`*@Vo~myH zD)bY=IAUDW(npYWc{d&lec)6(3BYRzRP@HA3ooyNS|b>;cFb~f-s1zC#=0+XCuII` z-Hv6Dn%x5@ea-*UgLzX!K9=1Kkq7Z)6C>3_*T&##(795beAY#@AlL7R zS}?-jNDP8lrmtev4cY1U+jUDpx)T5S!_}7&U$MUO-`pZ`3j^wC&|Gv%k$)dv?R5@+ z?9-1WdZTFIx3UMxJF4xf4*zC4aw2r|SSx>L$ITE~m!E?;NEf1yn@k(qT~GSuu}?p7 z6jhC<+ymjlIZjJO@H0)Egf?4J&dqj2-LSo%+n5JBiL&@FkQy#u?qdCe_P7P+2{9Wx3)f5GA zi6+1Dy~*Dv(DC1YM1JO{dj;_4XFT3(f8iFp_1Nnfo+;x;g{oP0(*bJ_7O7I8iz7}a z`Hi;QULXW&LDL)(iS(VvgAN}ulC)*xb-#H>{JBcIWQg*au9@|-T?jSYOg{B=&nl}O zCn!NW%z*X{&@0@Ii>GQU;nM2YiJA59E6X3&5q22K8&f#YZd~}LWMXn_RR#YjOz6W4 z&igDx5z*#*r30FvcR4VJqX66Lab4rZyryK<_g>g9B8XMp{r(=7Kq}bzB)^6CljnGo z03w)n(ssSn+4&Dopdj{yw*X;azh7r&N|nB97p|P5c0^?$U8Ny5G85YUwip+NLG4{h zR<^S|_ogxKmkU54G+i%f2Y?f)qnhv+PZ@ftgE%q(^4XXQ=q3Y7@&p|uKCfNUoBzRc zFcC&RbmO1Nhq^)YK(QjHmD}j+9V3%lq_b;Es5~Y~Mm2d$-a+MjBP%$rtD`lU}+pj=}h>&TX>TERGmGICsu1*_qzmd>_vv9acG*pw7YpM z`3q>!#X{i+^po7dd)C*Soi8>9UIOPudWG@Qf5g__aze7B5@rvm0Yqtd#+fyCWX0uc zB4;B50M)m16gEtp$3u2{A7t-BeCfbIyz=y8W1o^iLFC5Ws#!UuIFf{Dx~cC=OrPcR zrN`B? zsd3|j4A-{dno8iSc?Xs%xlIoNTR#A-t2)G(=D?~thgY?5ySnW3FZ@qTL%kQ!(%7lW z9|m@Uy`Sf!4cDUESzul+RK}u)UHjd`g!Lj&Yci4*(`PL5;BllRk}L#$G4wLCFm+=B z2OjPS;u{TR<%;i9%CcpH3#6t+q3mcKhglPZ@S_+R@2JG?VgCwKQOqpOmI>F z^;q1bBZk)!8=glU3DmyC;!}MrXl3Pc&-QaTJ+>*-Bh$I6@f125x5j`AD)RqTLlGMy z-vYgFO(=uGRjJZP_vLYhC9u>f%N1Rp9-iX?z<~~Rf&wi1;JGm?(Ga(wpnnBm4v$iv zL)4_g4F+{dezllIb|jIY5)N8LeV~#Mh4dL*#W=JjLpH96mR-de3zg1RAk=I?*SEOL z|J7a4EHQ*Kq*?;Cuoss5F2A>c_Aa2K1II(?$10KjzJ)~*1zZUf>7>1R z?M&AVg{0*dB-fP>2^JO>iRjyTumQyXw5dmQtQk>v$oiH7|DJ`p`3O@`{X9*1(E(R~ zoE-n`x5STtmJ|svWtYKA`%KdO0MOlX0q?>s$4x5d!!)GtZESWf^*By^*88i+jXis0 zFvy6UcpNCA3-98#-~9?IfrFXJzNowsL~t+bLJ?%iuv8AKoQ!VfLOvWD+4nfjp?FCk ze%y|Byd2qvEIEDX@g&64kPl*7SGs34&X{rz3akQ4N43uf*fK6{LpELXIO2qYejcn3 zNwQ*8CuA8XaH!2xDZAK7g~fa-33M4<%KGb>7dUX2ui@B`uDO#65hFr^b1_C`ClKJeTkSfwPu(qt? zappuAY)Kq}R5(HouJw%)!8d{mPj1Nyx#S=xa9;(BX z3UuO;mG1*QNdNeRyQp=o4As|dMjc1GK9!o@_^y}O$^VMLMFv;~`!puuZ(`rWVB*Vkzuj5rPg!Z{bb z3;GD{-j#jdqrd;YBj_U9K&)(!-+T=($D$LEhF%3@^Jzym-6~~gvzO0!Me2;Of;W>C z2iIJ_2JDQ1(geB=y++97IgnsEFK=%-Z z3^g9x+rF}}LCHvGTC!%RujN-b3mPWlCSr1+m1#lfVZp^~Jfb zCtXAhWx<6y{OS!ud-a9zzB~Vn6K=N(ZkVrGXk7o4cMl7kl6q^p|U3W%^PI4{Da!W9GW2sRvr9a4xh7#uMwm&(3^m5bJ~ zjKNd<^07OCVwRI6R1g+Yo$L$nV>aux58yg~qE#mVRQ%f{x0nJ}_ zYFG?mB{!^-D=jz@QV3la3vxGgXz8g*|Cx%RiwSMAqgw2syYO()q5Z%AE~Cf@u#hX^ zJio-f@V6VRCq_0+NMu|9RaHYo?G6W-r)M?0tQ`9)v@!G8QL!r)13^4wosrG1E#jCT zXwftA+E{WWGy~$BW>hCY=_I^0Vj||P(y80T1-P+D4HHs0L5_xN%7I)SnA+8r_q4kE zF^bo5H$drDT|V-k9(IaF>^$ngZBD4O)61gW8SoLzq7KgA|H$KwsZ_)r#tC>|ftq^C zz}bw&y6KI2*!tb8k-!=L9$pS=TCM}ys1;1QhU-IkObFn|EBkz#%;a?Ef|7m@~{au1Xu$Dnepw|z$oPNPt_ zR8f)Gbq@>LMX+E0fR`0O$ziah;MocX$Zmq>+IE7@)V-e71s@CJ%RRN>iKjY_D0dJ= zI;Uo4;$vDTx$~$)5Gyt-`fHWuI}2PZU7L26;9v8gaWGX2$DKL=R@J1`R7{^+nl2PU ww}d@?P9R=lS$Pq~YNUbv|DXSt24wKNsu%-8D+%F1g}~>krarbv!}{_60UN9I^Z)<= diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/.editorconfig b/plugins/traefik/vendor/github.com/go-task/slim-sprig/.editorconfig deleted file mode 100644 index b0c95367e..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/.editorconfig +++ /dev/null @@ -1,14 +0,0 @@ -# editorconfig.org - -root = true - -[*] -insert_final_newline = true -charset = utf-8 -trim_trailing_whitespace = true -indent_style = tab -indent_size = 8 - -[*.{md,yml,yaml,json}] -indent_style = space -indent_size = 2 diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/.gitattributes b/plugins/traefik/vendor/github.com/go-task/slim-sprig/.gitattributes deleted file mode 100644 index 176a458f9..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -* text=auto diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/.gitignore b/plugins/traefik/vendor/github.com/go-task/slim-sprig/.gitignore deleted file mode 100644 index 5e3002f88..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -vendor/ -/.glide diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/CHANGELOG.md b/plugins/traefik/vendor/github.com/go-task/slim-sprig/CHANGELOG.md deleted file mode 100644 index 61d8ebffc..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/CHANGELOG.md +++ /dev/null @@ -1,364 +0,0 @@ -# Changelog - -## Release 3.2.0 (2020-12-14) - -### Added - -- #211: Added randInt function (thanks @kochurovro) -- #223: Added fromJson and mustFromJson functions (thanks @mholt) -- #242: Added a bcrypt function (thanks @robbiet480) -- #253: Added randBytes function (thanks @MikaelSmith) -- #254: Added dig function for dicts (thanks @nyarly) -- #257: Added regexQuoteMeta for quoting regex metadata (thanks @rheaton) -- #261: Added filepath functions osBase, osDir, osExt, osClean, osIsAbs (thanks @zugl) -- #268: Added and and all functions for testing conditions (thanks @phuslu) -- #181: Added float64 arithmetic addf, add1f, subf, divf, mulf, maxf, and minf - (thanks @andrewmostello) -- #265: Added chunk function to split array into smaller arrays (thanks @karelbilek) -- #270: Extend certificate functions to handle non-RSA keys + add support for - ed25519 keys (thanks @misberner) - -### Changed - -- Removed testing and support for Go 1.12. ed25519 support requires Go 1.13 or newer -- Using semver 3.1.1 and mergo 0.3.11 - -### Fixed - -- #249: Fix htmlDateInZone example (thanks @spawnia) - -NOTE: The dependency github.com/imdario/mergo reverted the breaking change in -0.3.9 via 0.3.10 release. - -## Release 3.1.0 (2020-04-16) - -NOTE: The dependency github.com/imdario/mergo made a behavior change in 0.3.9 -that impacts sprig functionality. Do not use sprig with a version newer than 0.3.8. - -### Added - -- #225: Added support for generating htpasswd hash (thanks @rustycl0ck) -- #224: Added duration filter (thanks @frebib) -- #205: Added `seq` function (thanks @thadc23) - -### Changed - -- #203: Unlambda functions with correct signature (thanks @muesli) -- #236: Updated the license formatting for GitHub display purposes -- #238: Updated package dependency versions. Note, mergo not updated to 0.3.9 - as it causes a breaking change for sprig. That issue is tracked at - https://github.com/imdario/mergo/issues/139 - -### Fixed - -- #229: Fix `seq` example in docs (thanks @kalmant) - -## Release 3.0.2 (2019-12-13) - -### Fixed - -- #220: Updating to semver v3.0.3 to fix issue with <= ranges -- #218: fix typo elyptical->elliptic in ecdsa key description (thanks @laverya) - -## Release 3.0.1 (2019-12-08) - -### Fixed - -- #212: Updated semver fixing broken constraint checking with ^0.0 - -## Release 3.0.0 (2019-10-02) - -### Added - -- #187: Added durationRound function (thanks @yjp20) -- #189: Added numerous template functions that return errors rather than panic (thanks @nrvnrvn) -- #193: Added toRawJson support (thanks @Dean-Coakley) -- #197: Added get support to dicts (thanks @Dean-Coakley) - -### Changed - -- #186: Moving dependency management to Go modules -- #186: Updated semver to v3. This has changes in the way ^ is handled -- #194: Updated documentation on merging and how it copies. Added example using deepCopy -- #196: trunc now supports negative values (thanks @Dean-Coakley) - -## Release 2.22.0 (2019-10-02) - -### Added - -- #173: Added getHostByName function to resolve dns names to ips (thanks @fcgravalos) -- #195: Added deepCopy function for use with dicts - -### Changed - -- Updated merge and mergeOverwrite documentation to explain copying and how to - use deepCopy with it - -## Release 2.21.0 (2019-09-18) - -### Added - -- #122: Added encryptAES/decryptAES functions (thanks @n0madic) -- #128: Added toDecimal support (thanks @Dean-Coakley) -- #169: Added list contcat (thanks @astorath) -- #174: Added deepEqual function (thanks @bonifaido) -- #170: Added url parse and join functions (thanks @astorath) - -### Changed - -- #171: Updated glide config for Google UUID to v1 and to add ranges to semver and testify - -### Fixed - -- #172: Fix semver wildcard example (thanks @piepmatz) -- #175: Fix dateInZone doc example (thanks @s3than) - -## Release 2.20.0 (2019-06-18) - -### Added - -- #164: Adding function to get unix epoch for a time (@mattfarina) -- #166: Adding tests for date_in_zone (@mattfarina) - -### Changed - -- #144: Fix function comments based on best practices from Effective Go (@CodeLingoTeam) -- #150: Handles pointer type for time.Time in "htmlDate" (@mapreal19) -- #161, #157, #160, #153, #158, #156, #155, #159, #152 documentation updates (@badeadan) - -### Fixed - -## Release 2.19.0 (2019-03-02) - -IMPORTANT: This release reverts a change from 2.18.0 - -In the previous release (2.18), we prematurely merged a partial change to the crypto functions that led to creating two sets of crypto functions (I blame @technosophos -- since that's me). This release rolls back that change, and does what was originally intended: It alters the existing crypto functions to use secure random. - -We debated whether this classifies as a change worthy of major revision, but given the proximity to the last release, we have decided that treating 2.18 as a faulty release is the correct course of action. We apologize for any inconvenience. - -### Changed - -- Fix substr panic 35fb796 (Alexey igrychev) -- Remove extra period 1eb7729 (Matthew Lorimor) -- Make random string functions use crypto by default 6ceff26 (Matthew Lorimor) -- README edits/fixes/suggestions 08fe136 (Lauri Apple) - - -## Release 2.18.0 (2019-02-12) - -### Added - -- Added mergeOverwrite function -- cryptographic functions that use secure random (see fe1de12) - -### Changed - -- Improve documentation of regexMatch function, resolves #139 90b89ce (Jan Tagscherer) -- Handle has for nil list 9c10885 (Daniel Cohen) -- Document behaviour of mergeOverwrite fe0dbe9 (Lukas Rieder) -- doc: adds missing documentation. 4b871e6 (Fernandez Ludovic) -- Replace outdated goutils imports 01893d2 (Matthew Lorimor) -- Surface crypto secure random strings from goutils fe1de12 (Matthew Lorimor) -- Handle untyped nil values as paramters to string functions 2b2ec8f (Morten Torkildsen) - -### Fixed - -- Fix dict merge issue and provide mergeOverwrite .dst .src1 to overwrite from src -> dst 4c59c12 (Lukas Rieder) -- Fix substr var names and comments d581f80 (Dean Coakley) -- Fix substr documentation 2737203 (Dean Coakley) - -## Release 2.17.1 (2019-01-03) - -### Fixed - -The 2.17.0 release did not have a version pinned for xstrings, which caused compilation failures when xstrings < 1.2 was used. This adds the correct version string to glide.yaml. - -## Release 2.17.0 (2019-01-03) - -### Added - -- adds alder32sum function and test 6908fc2 (marshallford) -- Added kebabcase function ca331a1 (Ilyes512) - -### Changed - -- Update goutils to 1.1.0 4e1125d (Matt Butcher) - -### Fixed - -- Fix 'has' documentation e3f2a85 (dean-coakley) -- docs(dict): fix typo in pick example dc424f9 (Dustin Specker) -- fixes spelling errors... not sure how that happened 4cf188a (marshallford) - -## Release 2.16.0 (2018-08-13) - -### Added - -- add splitn function fccb0b0 (Helgi Þorbjörnsson) -- Add slice func df28ca7 (gongdo) -- Generate serial number a3bdffd (Cody Coons) -- Extract values of dict with values function df39312 (Lawrence Jones) - -### Changed - -- Modify panic message for list.slice ae38335 (gongdo) -- Minor improvement in code quality - Removed an unreachable piece of code at defaults.go#L26:6 - Resolve formatting issues. 5834241 (Abhishek Kashyap) -- Remove duplicated documentation 1d97af1 (Matthew Fisher) -- Test on go 1.11 49df809 (Helgi Þormar Þorbjörnsson) - -### Fixed - -- Fix file permissions c5f40b5 (gongdo) -- Fix example for buildCustomCert 7779e0d (Tin Lam) - -## Release 2.15.0 (2018-04-02) - -### Added - -- #68 and #69: Add json helpers to docs (thanks @arunvelsriram) -- #66: Add ternary function (thanks @binoculars) -- #67: Allow keys function to take multiple dicts (thanks @binoculars) -- #89: Added sha1sum to crypto function (thanks @benkeil) -- #81: Allow customizing Root CA that used by genSignedCert (thanks @chenzhiwei) -- #92: Add travis testing for go 1.10 -- #93: Adding appveyor config for windows testing - -### Changed - -- #90: Updating to more recent dependencies -- #73: replace satori/go.uuid with google/uuid (thanks @petterw) - -### Fixed - -- #76: Fixed documentation typos (thanks @Thiht) -- Fixed rounding issue on the `ago` function. Note, the removes support for Go 1.8 and older - -## Release 2.14.1 (2017-12-01) - -### Fixed - -- #60: Fix typo in function name documentation (thanks @neil-ca-moore) -- #61: Removing line with {{ due to blocking github pages genertion -- #64: Update the list functions to handle int, string, and other slices for compatibility - -## Release 2.14.0 (2017-10-06) - -This new version of Sprig adds a set of functions for generating and working with SSL certificates. - -- `genCA` generates an SSL Certificate Authority -- `genSelfSignedCert` generates an SSL self-signed certificate -- `genSignedCert` generates an SSL certificate and key based on a given CA - -## Release 2.13.0 (2017-09-18) - -This release adds new functions, including: - -- `regexMatch`, `regexFindAll`, `regexFind`, `regexReplaceAll`, `regexReplaceAllLiteral`, and `regexSplit` to work with regular expressions -- `floor`, `ceil`, and `round` math functions -- `toDate` converts a string to a date -- `nindent` is just like `indent` but also prepends a new line -- `ago` returns the time from `time.Now` - -### Added - -- #40: Added basic regex functionality (thanks @alanquillin) -- #41: Added ceil floor and round functions (thanks @alanquillin) -- #48: Added toDate function (thanks @andreynering) -- #50: Added nindent function (thanks @binoculars) -- #46: Added ago function (thanks @slayer) - -### Changed - -- #51: Updated godocs to include new string functions (thanks @curtisallen) -- #49: Added ability to merge multiple dicts (thanks @binoculars) - -## Release 2.12.0 (2017-05-17) - -- `snakecase`, `camelcase`, and `shuffle` are three new string functions -- `fail` allows you to bail out of a template render when conditions are not met - -## Release 2.11.0 (2017-05-02) - -- Added `toJson` and `toPrettyJson` -- Added `merge` -- Refactored documentation - -## Release 2.10.0 (2017-03-15) - -- Added `semver` and `semverCompare` for Semantic Versions -- `list` replaces `tuple` -- Fixed issue with `join` -- Added `first`, `last`, `intial`, `rest`, `prepend`, `append`, `toString`, `toStrings`, `sortAlpha`, `reverse`, `coalesce`, `pluck`, `pick`, `compact`, `keys`, `omit`, `uniq`, `has`, `without` - -## Release 2.9.0 (2017-02-23) - -- Added `splitList` to split a list -- Added crypto functions of `genPrivateKey` and `derivePassword` - -## Release 2.8.0 (2016-12-21) - -- Added access to several path functions (`base`, `dir`, `clean`, `ext`, and `abs`) -- Added functions for _mutating_ dictionaries (`set`, `unset`, `hasKey`) - -## Release 2.7.0 (2016-12-01) - -- Added `sha256sum` to generate a hash of an input -- Added functions to convert a numeric or string to `int`, `int64`, `float64` - -## Release 2.6.0 (2016-10-03) - -- Added a `uuidv4` template function for generating UUIDs inside of a template. - -## Release 2.5.0 (2016-08-19) - -- New `trimSuffix`, `trimPrefix`, `hasSuffix`, and `hasPrefix` functions -- New aliases have been added for a few functions that didn't follow the naming conventions (`trimAll` and `abbrevBoth`) -- `trimall` and `abbrevboth` (notice the case) are deprecated and will be removed in 3.0.0 - -## Release 2.4.0 (2016-08-16) - -- Adds two functions: `until` and `untilStep` - -## Release 2.3.0 (2016-06-21) - -- cat: Concatenate strings with whitespace separators. -- replace: Replace parts of a string: `replace " " "-" "Me First"` renders "Me-First" -- plural: Format plurals: `len "foo" | plural "one foo" "many foos"` renders "many foos" -- indent: Indent blocks of text in a way that is sensitive to "\n" characters. - -## Release 2.2.0 (2016-04-21) - -- Added a `genPrivateKey` function (Thanks @bacongobbler) - -## Release 2.1.0 (2016-03-30) - -- `default` now prints the default value when it does not receive a value down the pipeline. It is much safer now to do `{{.Foo | default "bar"}}`. -- Added accessors for "hermetic" functions. These return only functions that, when given the same input, produce the same output. - -## Release 2.0.0 (2016-03-29) - -Because we switched from `int` to `int64` as the return value for all integer math functions, the library's major version number has been incremented. - -- `min` complements `max` (formerly `biggest`) -- `empty` indicates that a value is the empty value for its type -- `tuple` creates a tuple inside of a template: `{{$t := tuple "a", "b" "c"}}` -- `dict` creates a dictionary inside of a template `{{$d := dict "key1" "val1" "key2" "val2"}}` -- Date formatters have been added for HTML dates (as used in `date` input fields) -- Integer math functions can convert from a number of types, including `string` (via `strconv.ParseInt`). - -## Release 1.2.0 (2016-02-01) - -- Added quote and squote -- Added b32enc and b32dec -- add now takes varargs -- biggest now takes varargs - -## Release 1.1.0 (2015-12-29) - -- Added #4: Added contains function. strings.Contains, but with the arguments - switched to simplify common pipelines. (thanks krancour) -- Added Travis-CI testing support - -## Release 1.0.0 (2015-12-23) - -- Initial release diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/LICENSE.txt b/plugins/traefik/vendor/github.com/go-task/slim-sprig/LICENSE.txt deleted file mode 100644 index f311b1eaa..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/LICENSE.txt +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (C) 2013-2020 Masterminds - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/README.md b/plugins/traefik/vendor/github.com/go-task/slim-sprig/README.md deleted file mode 100644 index 72579471f..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# Slim-Sprig: Template functions for Go templates [![GoDoc](https://godoc.org/github.com/go-task/slim-sprig?status.svg)](https://godoc.org/github.com/go-task/slim-sprig) [![Go Report Card](https://goreportcard.com/badge/github.com/go-task/slim-sprig)](https://goreportcard.com/report/github.com/go-task/slim-sprig) - -Slim-Sprig is a fork of [Sprig](https://github.com/Masterminds/sprig), but with -all functions that depend on external (non standard library) or crypto packages -removed. -The reason for this is to make this library more lightweight. Most of these -functions (specially crypto ones) are not needed on most apps, but costs a lot -in terms of binary size and compilation time. - -## Usage - -**Template developers**: Please use Slim-Sprig's [function documentation](https://go-task.github.io/slim-sprig/) for -detailed instructions and code snippets for the >100 template functions available. - -**Go developers**: If you'd like to include Slim-Sprig as a library in your program, -our API documentation is available [at GoDoc.org](http://godoc.org/github.com/go-task/slim-sprig). - -For standard usage, read on. - -### Load the Slim-Sprig library - -To load the Slim-Sprig `FuncMap`: - -```go - -import ( - "html/template" - - "github.com/go-task/slim-sprig" -) - -// This example illustrates that the FuncMap *must* be set before the -// templates themselves are loaded. -tpl := template.Must( - template.New("base").Funcs(sprig.FuncMap()).ParseGlob("*.html") -) -``` - -### Calling the functions inside of templates - -By convention, all functions are lowercase. This seems to follow the Go -idiom for template functions (as opposed to template methods, which are -TitleCase). For example, this: - -``` -{{ "hello!" | upper | repeat 5 }} -``` - -produces this: - -``` -HELLO!HELLO!HELLO!HELLO!HELLO! -``` - -## Principles Driving Our Function Selection - -We followed these principles to decide which functions to add and how to implement them: - -- Use template functions to build layout. The following - types of operations are within the domain of template functions: - - Formatting - - Layout - - Simple type conversions - - Utilities that assist in handling common formatting and layout needs (e.g. arithmetic) -- Template functions should not return errors unless there is no way to print - a sensible value. For example, converting a string to an integer should not - produce an error if conversion fails. Instead, it should display a default - value. -- Simple math is necessary for grid layouts, pagers, and so on. Complex math - (anything other than arithmetic) should be done outside of templates. -- Template functions only deal with the data passed into them. They never retrieve - data from a source. -- Finally, do not override core Go template functions. diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/Taskfile.yml b/plugins/traefik/vendor/github.com/go-task/slim-sprig/Taskfile.yml deleted file mode 100644 index cdcfd223b..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/Taskfile.yml +++ /dev/null @@ -1,12 +0,0 @@ -# https://taskfile.dev - -version: '2' - -tasks: - default: - cmds: - - task: test - - test: - cmds: - - go test -v . diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/crypto.go b/plugins/traefik/vendor/github.com/go-task/slim-sprig/crypto.go deleted file mode 100644 index d06e516d4..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/crypto.go +++ /dev/null @@ -1,24 +0,0 @@ -package sprig - -import ( - "crypto/sha1" - "crypto/sha256" - "encoding/hex" - "fmt" - "hash/adler32" -) - -func sha256sum(input string) string { - hash := sha256.Sum256([]byte(input)) - return hex.EncodeToString(hash[:]) -} - -func sha1sum(input string) string { - hash := sha1.Sum([]byte(input)) - return hex.EncodeToString(hash[:]) -} - -func adler32sum(input string) string { - hash := adler32.Checksum([]byte(input)) - return fmt.Sprintf("%d", hash) -} diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/date.go b/plugins/traefik/vendor/github.com/go-task/slim-sprig/date.go deleted file mode 100644 index ed022ddac..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/date.go +++ /dev/null @@ -1,152 +0,0 @@ -package sprig - -import ( - "strconv" - "time" -) - -// Given a format and a date, format the date string. -// -// Date can be a `time.Time` or an `int, int32, int64`. -// In the later case, it is treated as seconds since UNIX -// epoch. -func date(fmt string, date interface{}) string { - return dateInZone(fmt, date, "Local") -} - -func htmlDate(date interface{}) string { - return dateInZone("2006-01-02", date, "Local") -} - -func htmlDateInZone(date interface{}, zone string) string { - return dateInZone("2006-01-02", date, zone) -} - -func dateInZone(fmt string, date interface{}, zone string) string { - var t time.Time - switch date := date.(type) { - default: - t = time.Now() - case time.Time: - t = date - case *time.Time: - t = *date - case int64: - t = time.Unix(date, 0) - case int: - t = time.Unix(int64(date), 0) - case int32: - t = time.Unix(int64(date), 0) - } - - loc, err := time.LoadLocation(zone) - if err != nil { - loc, _ = time.LoadLocation("UTC") - } - - return t.In(loc).Format(fmt) -} - -func dateModify(fmt string, date time.Time) time.Time { - d, err := time.ParseDuration(fmt) - if err != nil { - return date - } - return date.Add(d) -} - -func mustDateModify(fmt string, date time.Time) (time.Time, error) { - d, err := time.ParseDuration(fmt) - if err != nil { - return time.Time{}, err - } - return date.Add(d), nil -} - -func dateAgo(date interface{}) string { - var t time.Time - - switch date := date.(type) { - default: - t = time.Now() - case time.Time: - t = date - case int64: - t = time.Unix(date, 0) - case int: - t = time.Unix(int64(date), 0) - } - // Drop resolution to seconds - duration := time.Since(t).Round(time.Second) - return duration.String() -} - -func duration(sec interface{}) string { - var n int64 - switch value := sec.(type) { - default: - n = 0 - case string: - n, _ = strconv.ParseInt(value, 10, 64) - case int64: - n = value - } - return (time.Duration(n) * time.Second).String() -} - -func durationRound(duration interface{}) string { - var d time.Duration - switch duration := duration.(type) { - default: - d = 0 - case string: - d, _ = time.ParseDuration(duration) - case int64: - d = time.Duration(duration) - case time.Time: - d = time.Since(duration) - } - - u := uint64(d) - neg := d < 0 - if neg { - u = -u - } - - var ( - year = uint64(time.Hour) * 24 * 365 - month = uint64(time.Hour) * 24 * 30 - day = uint64(time.Hour) * 24 - hour = uint64(time.Hour) - minute = uint64(time.Minute) - second = uint64(time.Second) - ) - switch { - case u > year: - return strconv.FormatUint(u/year, 10) + "y" - case u > month: - return strconv.FormatUint(u/month, 10) + "mo" - case u > day: - return strconv.FormatUint(u/day, 10) + "d" - case u > hour: - return strconv.FormatUint(u/hour, 10) + "h" - case u > minute: - return strconv.FormatUint(u/minute, 10) + "m" - case u > second: - return strconv.FormatUint(u/second, 10) + "s" - } - return "0s" -} - -func toDate(fmt, str string) time.Time { - t, _ := time.ParseInLocation(fmt, str, time.Local) - return t -} - -func mustToDate(fmt, str string) (time.Time, error) { - return time.ParseInLocation(fmt, str, time.Local) -} - -func unixEpoch(date time.Time) string { - return strconv.FormatInt(date.Unix(), 10) -} diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/defaults.go b/plugins/traefik/vendor/github.com/go-task/slim-sprig/defaults.go deleted file mode 100644 index b9f979666..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/defaults.go +++ /dev/null @@ -1,163 +0,0 @@ -package sprig - -import ( - "bytes" - "encoding/json" - "math/rand" - "reflect" - "strings" - "time" -) - -func init() { - rand.Seed(time.Now().UnixNano()) -} - -// dfault checks whether `given` is set, and returns default if not set. -// -// This returns `d` if `given` appears not to be set, and `given` otherwise. -// -// For numeric types 0 is unset. -// For strings, maps, arrays, and slices, len() = 0 is considered unset. -// For bool, false is unset. -// Structs are never considered unset. -// -// For everything else, including pointers, a nil value is unset. -func dfault(d interface{}, given ...interface{}) interface{} { - - if empty(given) || empty(given[0]) { - return d - } - return given[0] -} - -// empty returns true if the given value has the zero value for its type. -func empty(given interface{}) bool { - g := reflect.ValueOf(given) - if !g.IsValid() { - return true - } - - // Basically adapted from text/template.isTrue - switch g.Kind() { - default: - return g.IsNil() - case reflect.Array, reflect.Slice, reflect.Map, reflect.String: - return g.Len() == 0 - case reflect.Bool: - return !g.Bool() - case reflect.Complex64, reflect.Complex128: - return g.Complex() == 0 - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return g.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return g.Uint() == 0 - case reflect.Float32, reflect.Float64: - return g.Float() == 0 - case reflect.Struct: - return false - } -} - -// coalesce returns the first non-empty value. -func coalesce(v ...interface{}) interface{} { - for _, val := range v { - if !empty(val) { - return val - } - } - return nil -} - -// all returns true if empty(x) is false for all values x in the list. -// If the list is empty, return true. -func all(v ...interface{}) bool { - for _, val := range v { - if empty(val) { - return false - } - } - return true -} - -// any returns true if empty(x) is false for any x in the list. -// If the list is empty, return false. -func any(v ...interface{}) bool { - for _, val := range v { - if !empty(val) { - return true - } - } - return false -} - -// fromJson decodes JSON into a structured value, ignoring errors. -func fromJson(v string) interface{} { - output, _ := mustFromJson(v) - return output -} - -// mustFromJson decodes JSON into a structured value, returning errors. -func mustFromJson(v string) (interface{}, error) { - var output interface{} - err := json.Unmarshal([]byte(v), &output) - return output, err -} - -// toJson encodes an item into a JSON string -func toJson(v interface{}) string { - output, _ := json.Marshal(v) - return string(output) -} - -func mustToJson(v interface{}) (string, error) { - output, err := json.Marshal(v) - if err != nil { - return "", err - } - return string(output), nil -} - -// toPrettyJson encodes an item into a pretty (indented) JSON string -func toPrettyJson(v interface{}) string { - output, _ := json.MarshalIndent(v, "", " ") - return string(output) -} - -func mustToPrettyJson(v interface{}) (string, error) { - output, err := json.MarshalIndent(v, "", " ") - if err != nil { - return "", err - } - return string(output), nil -} - -// toRawJson encodes an item into a JSON string with no escaping of HTML characters. -func toRawJson(v interface{}) string { - output, err := mustToRawJson(v) - if err != nil { - panic(err) - } - return string(output) -} - -// mustToRawJson encodes an item into a JSON string with no escaping of HTML characters. -func mustToRawJson(v interface{}) (string, error) { - buf := new(bytes.Buffer) - enc := json.NewEncoder(buf) - enc.SetEscapeHTML(false) - err := enc.Encode(&v) - if err != nil { - return "", err - } - return strings.TrimSuffix(buf.String(), "\n"), nil -} - -// ternary returns the first value if the last value is true, otherwise returns the second value. -func ternary(vt interface{}, vf interface{}, v bool) interface{} { - if v { - return vt - } - - return vf -} diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/dict.go b/plugins/traefik/vendor/github.com/go-task/slim-sprig/dict.go deleted file mode 100644 index 77ebc61b1..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/dict.go +++ /dev/null @@ -1,118 +0,0 @@ -package sprig - -func get(d map[string]interface{}, key string) interface{} { - if val, ok := d[key]; ok { - return val - } - return "" -} - -func set(d map[string]interface{}, key string, value interface{}) map[string]interface{} { - d[key] = value - return d -} - -func unset(d map[string]interface{}, key string) map[string]interface{} { - delete(d, key) - return d -} - -func hasKey(d map[string]interface{}, key string) bool { - _, ok := d[key] - return ok -} - -func pluck(key string, d ...map[string]interface{}) []interface{} { - res := []interface{}{} - for _, dict := range d { - if val, ok := dict[key]; ok { - res = append(res, val) - } - } - return res -} - -func keys(dicts ...map[string]interface{}) []string { - k := []string{} - for _, dict := range dicts { - for key := range dict { - k = append(k, key) - } - } - return k -} - -func pick(dict map[string]interface{}, keys ...string) map[string]interface{} { - res := map[string]interface{}{} - for _, k := range keys { - if v, ok := dict[k]; ok { - res[k] = v - } - } - return res -} - -func omit(dict map[string]interface{}, keys ...string) map[string]interface{} { - res := map[string]interface{}{} - - omit := make(map[string]bool, len(keys)) - for _, k := range keys { - omit[k] = true - } - - for k, v := range dict { - if _, ok := omit[k]; !ok { - res[k] = v - } - } - return res -} - -func dict(v ...interface{}) map[string]interface{} { - dict := map[string]interface{}{} - lenv := len(v) - for i := 0; i < lenv; i += 2 { - key := strval(v[i]) - if i+1 >= lenv { - dict[key] = "" - continue - } - dict[key] = v[i+1] - } - return dict -} - -func values(dict map[string]interface{}) []interface{} { - values := []interface{}{} - for _, value := range dict { - values = append(values, value) - } - - return values -} - -func dig(ps ...interface{}) (interface{}, error) { - if len(ps) < 3 { - panic("dig needs at least three arguments") - } - dict := ps[len(ps)-1].(map[string]interface{}) - def := ps[len(ps)-2] - ks := make([]string, len(ps)-2) - for i := 0; i < len(ks); i++ { - ks[i] = ps[i].(string) - } - - return digFromDict(dict, def, ks) -} - -func digFromDict(dict map[string]interface{}, d interface{}, ks []string) (interface{}, error) { - k, ns := ks[0], ks[1:len(ks)] - step, has := dict[k] - if !has { - return d, nil - } - if len(ns) == 0 { - return step, nil - } - return digFromDict(step.(map[string]interface{}), d, ns) -} diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/doc.go b/plugins/traefik/vendor/github.com/go-task/slim-sprig/doc.go deleted file mode 100644 index aabb9d448..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Package sprig provides template functions for Go. - -This package contains a number of utility functions for working with data -inside of Go `html/template` and `text/template` files. - -To add these functions, use the `template.Funcs()` method: - - t := templates.New("foo").Funcs(sprig.FuncMap()) - -Note that you should add the function map before you parse any template files. - - In several cases, Sprig reverses the order of arguments from the way they - appear in the standard library. This is to make it easier to pipe - arguments into functions. - -See http://masterminds.github.io/sprig/ for more detailed documentation on each of the available functions. -*/ -package sprig diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/functions.go b/plugins/traefik/vendor/github.com/go-task/slim-sprig/functions.go deleted file mode 100644 index 5ea74f899..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/functions.go +++ /dev/null @@ -1,317 +0,0 @@ -package sprig - -import ( - "errors" - "html/template" - "math/rand" - "os" - "path" - "path/filepath" - "reflect" - "strconv" - "strings" - ttemplate "text/template" - "time" -) - -// FuncMap produces the function map. -// -// Use this to pass the functions into the template engine: -// -// tpl := template.New("foo").Funcs(sprig.FuncMap())) -// -func FuncMap() template.FuncMap { - return HtmlFuncMap() -} - -// HermeticTxtFuncMap returns a 'text/template'.FuncMap with only repeatable functions. -func HermeticTxtFuncMap() ttemplate.FuncMap { - r := TxtFuncMap() - for _, name := range nonhermeticFunctions { - delete(r, name) - } - return r -} - -// HermeticHtmlFuncMap returns an 'html/template'.Funcmap with only repeatable functions. -func HermeticHtmlFuncMap() template.FuncMap { - r := HtmlFuncMap() - for _, name := range nonhermeticFunctions { - delete(r, name) - } - return r -} - -// TxtFuncMap returns a 'text/template'.FuncMap -func TxtFuncMap() ttemplate.FuncMap { - return ttemplate.FuncMap(GenericFuncMap()) -} - -// HtmlFuncMap returns an 'html/template'.Funcmap -func HtmlFuncMap() template.FuncMap { - return template.FuncMap(GenericFuncMap()) -} - -// GenericFuncMap returns a copy of the basic function map as a map[string]interface{}. -func GenericFuncMap() map[string]interface{} { - gfm := make(map[string]interface{}, len(genericMap)) - for k, v := range genericMap { - gfm[k] = v - } - return gfm -} - -// These functions are not guaranteed to evaluate to the same result for given input, because they -// refer to the environment or global state. -var nonhermeticFunctions = []string{ - // Date functions - "date", - "date_in_zone", - "date_modify", - "now", - "htmlDate", - "htmlDateInZone", - "dateInZone", - "dateModify", - - // Strings - "randAlphaNum", - "randAlpha", - "randAscii", - "randNumeric", - "randBytes", - "uuidv4", - - // OS - "env", - "expandenv", - - // Network - "getHostByName", -} - -var genericMap = map[string]interface{}{ - "hello": func() string { return "Hello!" }, - - // Date functions - "ago": dateAgo, - "date": date, - "date_in_zone": dateInZone, - "date_modify": dateModify, - "dateInZone": dateInZone, - "dateModify": dateModify, - "duration": duration, - "durationRound": durationRound, - "htmlDate": htmlDate, - "htmlDateInZone": htmlDateInZone, - "must_date_modify": mustDateModify, - "mustDateModify": mustDateModify, - "mustToDate": mustToDate, - "now": time.Now, - "toDate": toDate, - "unixEpoch": unixEpoch, - - // Strings - "trunc": trunc, - "trim": strings.TrimSpace, - "upper": strings.ToUpper, - "lower": strings.ToLower, - "title": strings.Title, - "substr": substring, - // Switch order so that "foo" | repeat 5 - "repeat": func(count int, str string) string { return strings.Repeat(str, count) }, - // Deprecated: Use trimAll. - "trimall": func(a, b string) string { return strings.Trim(b, a) }, - // Switch order so that "$foo" | trimall "$" - "trimAll": func(a, b string) string { return strings.Trim(b, a) }, - "trimSuffix": func(a, b string) string { return strings.TrimSuffix(b, a) }, - "trimPrefix": func(a, b string) string { return strings.TrimPrefix(b, a) }, - // Switch order so that "foobar" | contains "foo" - "contains": func(substr string, str string) bool { return strings.Contains(str, substr) }, - "hasPrefix": func(substr string, str string) bool { return strings.HasPrefix(str, substr) }, - "hasSuffix": func(substr string, str string) bool { return strings.HasSuffix(str, substr) }, - "quote": quote, - "squote": squote, - "cat": cat, - "indent": indent, - "nindent": nindent, - "replace": replace, - "plural": plural, - "sha1sum": sha1sum, - "sha256sum": sha256sum, - "adler32sum": adler32sum, - "toString": strval, - - // Wrap Atoi to stop errors. - "atoi": func(a string) int { i, _ := strconv.Atoi(a); return i }, - "int64": toInt64, - "int": toInt, - "float64": toFloat64, - "seq": seq, - "toDecimal": toDecimal, - - //"gt": func(a, b int) bool {return a > b}, - //"gte": func(a, b int) bool {return a >= b}, - //"lt": func(a, b int) bool {return a < b}, - //"lte": func(a, b int) bool {return a <= b}, - - // split "/" foo/bar returns map[int]string{0: foo, 1: bar} - "split": split, - "splitList": func(sep, orig string) []string { return strings.Split(orig, sep) }, - // splitn "/" foo/bar/fuu returns map[int]string{0: foo, 1: bar/fuu} - "splitn": splitn, - "toStrings": strslice, - - "until": until, - "untilStep": untilStep, - - // VERY basic arithmetic. - "add1": func(i interface{}) int64 { return toInt64(i) + 1 }, - "add": func(i ...interface{}) int64 { - var a int64 = 0 - for _, b := range i { - a += toInt64(b) - } - return a - }, - "sub": func(a, b interface{}) int64 { return toInt64(a) - toInt64(b) }, - "div": func(a, b interface{}) int64 { return toInt64(a) / toInt64(b) }, - "mod": func(a, b interface{}) int64 { return toInt64(a) % toInt64(b) }, - "mul": func(a interface{}, v ...interface{}) int64 { - val := toInt64(a) - for _, b := range v { - val = val * toInt64(b) - } - return val - }, - "randInt": func(min, max int) int { return rand.Intn(max-min) + min }, - "biggest": max, - "max": max, - "min": min, - "maxf": maxf, - "minf": minf, - "ceil": ceil, - "floor": floor, - "round": round, - - // string slices. Note that we reverse the order b/c that's better - // for template processing. - "join": join, - "sortAlpha": sortAlpha, - - // Defaults - "default": dfault, - "empty": empty, - "coalesce": coalesce, - "all": all, - "any": any, - "compact": compact, - "mustCompact": mustCompact, - "fromJson": fromJson, - "toJson": toJson, - "toPrettyJson": toPrettyJson, - "toRawJson": toRawJson, - "mustFromJson": mustFromJson, - "mustToJson": mustToJson, - "mustToPrettyJson": mustToPrettyJson, - "mustToRawJson": mustToRawJson, - "ternary": ternary, - - // Reflection - "typeOf": typeOf, - "typeIs": typeIs, - "typeIsLike": typeIsLike, - "kindOf": kindOf, - "kindIs": kindIs, - "deepEqual": reflect.DeepEqual, - - // OS: - "env": os.Getenv, - "expandenv": os.ExpandEnv, - - // Network: - "getHostByName": getHostByName, - - // Paths: - "base": path.Base, - "dir": path.Dir, - "clean": path.Clean, - "ext": path.Ext, - "isAbs": path.IsAbs, - - // Filepaths: - "osBase": filepath.Base, - "osClean": filepath.Clean, - "osDir": filepath.Dir, - "osExt": filepath.Ext, - "osIsAbs": filepath.IsAbs, - - // Encoding: - "b64enc": base64encode, - "b64dec": base64decode, - "b32enc": base32encode, - "b32dec": base32decode, - - // Data Structures: - "tuple": list, // FIXME: with the addition of append/prepend these are no longer immutable. - "list": list, - "dict": dict, - "get": get, - "set": set, - "unset": unset, - "hasKey": hasKey, - "pluck": pluck, - "keys": keys, - "pick": pick, - "omit": omit, - "values": values, - - "append": push, "push": push, - "mustAppend": mustPush, "mustPush": mustPush, - "prepend": prepend, - "mustPrepend": mustPrepend, - "first": first, - "mustFirst": mustFirst, - "rest": rest, - "mustRest": mustRest, - "last": last, - "mustLast": mustLast, - "initial": initial, - "mustInitial": mustInitial, - "reverse": reverse, - "mustReverse": mustReverse, - "uniq": uniq, - "mustUniq": mustUniq, - "without": without, - "mustWithout": mustWithout, - "has": has, - "mustHas": mustHas, - "slice": slice, - "mustSlice": mustSlice, - "concat": concat, - "dig": dig, - "chunk": chunk, - "mustChunk": mustChunk, - - // Flow Control: - "fail": func(msg string) (string, error) { return "", errors.New(msg) }, - - // Regex - "regexMatch": regexMatch, - "mustRegexMatch": mustRegexMatch, - "regexFindAll": regexFindAll, - "mustRegexFindAll": mustRegexFindAll, - "regexFind": regexFind, - "mustRegexFind": mustRegexFind, - "regexReplaceAll": regexReplaceAll, - "mustRegexReplaceAll": mustRegexReplaceAll, - "regexReplaceAllLiteral": regexReplaceAllLiteral, - "mustRegexReplaceAllLiteral": mustRegexReplaceAllLiteral, - "regexSplit": regexSplit, - "mustRegexSplit": mustRegexSplit, - "regexQuoteMeta": regexQuoteMeta, - - // URLs: - "urlParse": urlParse, - "urlJoin": urlJoin, -} diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/list.go b/plugins/traefik/vendor/github.com/go-task/slim-sprig/list.go deleted file mode 100644 index ca0fbb789..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/list.go +++ /dev/null @@ -1,464 +0,0 @@ -package sprig - -import ( - "fmt" - "math" - "reflect" - "sort" -) - -// Reflection is used in these functions so that slices and arrays of strings, -// ints, and other types not implementing []interface{} can be worked with. -// For example, this is useful if you need to work on the output of regexs. - -func list(v ...interface{}) []interface{} { - return v -} - -func push(list interface{}, v interface{}) []interface{} { - l, err := mustPush(list, v) - if err != nil { - panic(err) - } - - return l -} - -func mustPush(list interface{}, v interface{}) ([]interface{}, error) { - tp := reflect.TypeOf(list).Kind() - switch tp { - case reflect.Slice, reflect.Array: - l2 := reflect.ValueOf(list) - - l := l2.Len() - nl := make([]interface{}, l) - for i := 0; i < l; i++ { - nl[i] = l2.Index(i).Interface() - } - - return append(nl, v), nil - - default: - return nil, fmt.Errorf("Cannot push on type %s", tp) - } -} - -func prepend(list interface{}, v interface{}) []interface{} { - l, err := mustPrepend(list, v) - if err != nil { - panic(err) - } - - return l -} - -func mustPrepend(list interface{}, v interface{}) ([]interface{}, error) { - //return append([]interface{}{v}, list...) - - tp := reflect.TypeOf(list).Kind() - switch tp { - case reflect.Slice, reflect.Array: - l2 := reflect.ValueOf(list) - - l := l2.Len() - nl := make([]interface{}, l) - for i := 0; i < l; i++ { - nl[i] = l2.Index(i).Interface() - } - - return append([]interface{}{v}, nl...), nil - - default: - return nil, fmt.Errorf("Cannot prepend on type %s", tp) - } -} - -func chunk(size int, list interface{}) [][]interface{} { - l, err := mustChunk(size, list) - if err != nil { - panic(err) - } - - return l -} - -func mustChunk(size int, list interface{}) ([][]interface{}, error) { - tp := reflect.TypeOf(list).Kind() - switch tp { - case reflect.Slice, reflect.Array: - l2 := reflect.ValueOf(list) - - l := l2.Len() - - cs := int(math.Floor(float64(l-1)/float64(size)) + 1) - nl := make([][]interface{}, cs) - - for i := 0; i < cs; i++ { - clen := size - if i == cs-1 { - clen = int(math.Floor(math.Mod(float64(l), float64(size)))) - if clen == 0 { - clen = size - } - } - - nl[i] = make([]interface{}, clen) - - for j := 0; j < clen; j++ { - ix := i*size + j - nl[i][j] = l2.Index(ix).Interface() - } - } - - return nl, nil - - default: - return nil, fmt.Errorf("Cannot chunk type %s", tp) - } -} - -func last(list interface{}) interface{} { - l, err := mustLast(list) - if err != nil { - panic(err) - } - - return l -} - -func mustLast(list interface{}) (interface{}, error) { - tp := reflect.TypeOf(list).Kind() - switch tp { - case reflect.Slice, reflect.Array: - l2 := reflect.ValueOf(list) - - l := l2.Len() - if l == 0 { - return nil, nil - } - - return l2.Index(l - 1).Interface(), nil - default: - return nil, fmt.Errorf("Cannot find last on type %s", tp) - } -} - -func first(list interface{}) interface{} { - l, err := mustFirst(list) - if err != nil { - panic(err) - } - - return l -} - -func mustFirst(list interface{}) (interface{}, error) { - tp := reflect.TypeOf(list).Kind() - switch tp { - case reflect.Slice, reflect.Array: - l2 := reflect.ValueOf(list) - - l := l2.Len() - if l == 0 { - return nil, nil - } - - return l2.Index(0).Interface(), nil - default: - return nil, fmt.Errorf("Cannot find first on type %s", tp) - } -} - -func rest(list interface{}) []interface{} { - l, err := mustRest(list) - if err != nil { - panic(err) - } - - return l -} - -func mustRest(list interface{}) ([]interface{}, error) { - tp := reflect.TypeOf(list).Kind() - switch tp { - case reflect.Slice, reflect.Array: - l2 := reflect.ValueOf(list) - - l := l2.Len() - if l == 0 { - return nil, nil - } - - nl := make([]interface{}, l-1) - for i := 1; i < l; i++ { - nl[i-1] = l2.Index(i).Interface() - } - - return nl, nil - default: - return nil, fmt.Errorf("Cannot find rest on type %s", tp) - } -} - -func initial(list interface{}) []interface{} { - l, err := mustInitial(list) - if err != nil { - panic(err) - } - - return l -} - -func mustInitial(list interface{}) ([]interface{}, error) { - tp := reflect.TypeOf(list).Kind() - switch tp { - case reflect.Slice, reflect.Array: - l2 := reflect.ValueOf(list) - - l := l2.Len() - if l == 0 { - return nil, nil - } - - nl := make([]interface{}, l-1) - for i := 0; i < l-1; i++ { - nl[i] = l2.Index(i).Interface() - } - - return nl, nil - default: - return nil, fmt.Errorf("Cannot find initial on type %s", tp) - } -} - -func sortAlpha(list interface{}) []string { - k := reflect.Indirect(reflect.ValueOf(list)).Kind() - switch k { - case reflect.Slice, reflect.Array: - a := strslice(list) - s := sort.StringSlice(a) - s.Sort() - return s - } - return []string{strval(list)} -} - -func reverse(v interface{}) []interface{} { - l, err := mustReverse(v) - if err != nil { - panic(err) - } - - return l -} - -func mustReverse(v interface{}) ([]interface{}, error) { - tp := reflect.TypeOf(v).Kind() - switch tp { - case reflect.Slice, reflect.Array: - l2 := reflect.ValueOf(v) - - l := l2.Len() - // We do not sort in place because the incoming array should not be altered. - nl := make([]interface{}, l) - for i := 0; i < l; i++ { - nl[l-i-1] = l2.Index(i).Interface() - } - - return nl, nil - default: - return nil, fmt.Errorf("Cannot find reverse on type %s", tp) - } -} - -func compact(list interface{}) []interface{} { - l, err := mustCompact(list) - if err != nil { - panic(err) - } - - return l -} - -func mustCompact(list interface{}) ([]interface{}, error) { - tp := reflect.TypeOf(list).Kind() - switch tp { - case reflect.Slice, reflect.Array: - l2 := reflect.ValueOf(list) - - l := l2.Len() - nl := []interface{}{} - var item interface{} - for i := 0; i < l; i++ { - item = l2.Index(i).Interface() - if !empty(item) { - nl = append(nl, item) - } - } - - return nl, nil - default: - return nil, fmt.Errorf("Cannot compact on type %s", tp) - } -} - -func uniq(list interface{}) []interface{} { - l, err := mustUniq(list) - if err != nil { - panic(err) - } - - return l -} - -func mustUniq(list interface{}) ([]interface{}, error) { - tp := reflect.TypeOf(list).Kind() - switch tp { - case reflect.Slice, reflect.Array: - l2 := reflect.ValueOf(list) - - l := l2.Len() - dest := []interface{}{} - var item interface{} - for i := 0; i < l; i++ { - item = l2.Index(i).Interface() - if !inList(dest, item) { - dest = append(dest, item) - } - } - - return dest, nil - default: - return nil, fmt.Errorf("Cannot find uniq on type %s", tp) - } -} - -func inList(haystack []interface{}, needle interface{}) bool { - for _, h := range haystack { - if reflect.DeepEqual(needle, h) { - return true - } - } - return false -} - -func without(list interface{}, omit ...interface{}) []interface{} { - l, err := mustWithout(list, omit...) - if err != nil { - panic(err) - } - - return l -} - -func mustWithout(list interface{}, omit ...interface{}) ([]interface{}, error) { - tp := reflect.TypeOf(list).Kind() - switch tp { - case reflect.Slice, reflect.Array: - l2 := reflect.ValueOf(list) - - l := l2.Len() - res := []interface{}{} - var item interface{} - for i := 0; i < l; i++ { - item = l2.Index(i).Interface() - if !inList(omit, item) { - res = append(res, item) - } - } - - return res, nil - default: - return nil, fmt.Errorf("Cannot find without on type %s", tp) - } -} - -func has(needle interface{}, haystack interface{}) bool { - l, err := mustHas(needle, haystack) - if err != nil { - panic(err) - } - - return l -} - -func mustHas(needle interface{}, haystack interface{}) (bool, error) { - if haystack == nil { - return false, nil - } - tp := reflect.TypeOf(haystack).Kind() - switch tp { - case reflect.Slice, reflect.Array: - l2 := reflect.ValueOf(haystack) - var item interface{} - l := l2.Len() - for i := 0; i < l; i++ { - item = l2.Index(i).Interface() - if reflect.DeepEqual(needle, item) { - return true, nil - } - } - - return false, nil - default: - return false, fmt.Errorf("Cannot find has on type %s", tp) - } -} - -// $list := [1, 2, 3, 4, 5] -// slice $list -> list[0:5] = list[:] -// slice $list 0 3 -> list[0:3] = list[:3] -// slice $list 3 5 -> list[3:5] -// slice $list 3 -> list[3:5] = list[3:] -func slice(list interface{}, indices ...interface{}) interface{} { - l, err := mustSlice(list, indices...) - if err != nil { - panic(err) - } - - return l -} - -func mustSlice(list interface{}, indices ...interface{}) (interface{}, error) { - tp := reflect.TypeOf(list).Kind() - switch tp { - case reflect.Slice, reflect.Array: - l2 := reflect.ValueOf(list) - - l := l2.Len() - if l == 0 { - return nil, nil - } - - var start, end int - if len(indices) > 0 { - start = toInt(indices[0]) - } - if len(indices) < 2 { - end = l - } else { - end = toInt(indices[1]) - } - - return l2.Slice(start, end).Interface(), nil - default: - return nil, fmt.Errorf("list should be type of slice or array but %s", tp) - } -} - -func concat(lists ...interface{}) interface{} { - var res []interface{} - for _, list := range lists { - tp := reflect.TypeOf(list).Kind() - switch tp { - case reflect.Slice, reflect.Array: - l2 := reflect.ValueOf(list) - for i := 0; i < l2.Len(); i++ { - res = append(res, l2.Index(i).Interface()) - } - default: - panic(fmt.Sprintf("Cannot concat type %s as list", tp)) - } - } - return res -} diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/network.go b/plugins/traefik/vendor/github.com/go-task/slim-sprig/network.go deleted file mode 100644 index 108d78a94..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/network.go +++ /dev/null @@ -1,12 +0,0 @@ -package sprig - -import ( - "math/rand" - "net" -) - -func getHostByName(name string) string { - addrs, _ := net.LookupHost(name) - //TODO: add error handing when release v3 comes out - return addrs[rand.Intn(len(addrs))] -} diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/numeric.go b/plugins/traefik/vendor/github.com/go-task/slim-sprig/numeric.go deleted file mode 100644 index 98cbb37a1..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/numeric.go +++ /dev/null @@ -1,228 +0,0 @@ -package sprig - -import ( - "fmt" - "math" - "reflect" - "strconv" - "strings" -) - -// toFloat64 converts 64-bit floats -func toFloat64(v interface{}) float64 { - if str, ok := v.(string); ok { - iv, err := strconv.ParseFloat(str, 64) - if err != nil { - return 0 - } - return iv - } - - val := reflect.Indirect(reflect.ValueOf(v)) - switch val.Kind() { - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: - return float64(val.Int()) - case reflect.Uint8, reflect.Uint16, reflect.Uint32: - return float64(val.Uint()) - case reflect.Uint, reflect.Uint64: - return float64(val.Uint()) - case reflect.Float32, reflect.Float64: - return val.Float() - case reflect.Bool: - if val.Bool() { - return 1 - } - return 0 - default: - return 0 - } -} - -func toInt(v interface{}) int { - //It's not optimal. Bud I don't want duplicate toInt64 code. - return int(toInt64(v)) -} - -// toInt64 converts integer types to 64-bit integers -func toInt64(v interface{}) int64 { - if str, ok := v.(string); ok { - iv, err := strconv.ParseInt(str, 10, 64) - if err != nil { - return 0 - } - return iv - } - - val := reflect.Indirect(reflect.ValueOf(v)) - switch val.Kind() { - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: - return val.Int() - case reflect.Uint8, reflect.Uint16, reflect.Uint32: - return int64(val.Uint()) - case reflect.Uint, reflect.Uint64: - tv := val.Uint() - if tv <= math.MaxInt64 { - return int64(tv) - } - // TODO: What is the sensible thing to do here? - return math.MaxInt64 - case reflect.Float32, reflect.Float64: - return int64(val.Float()) - case reflect.Bool: - if val.Bool() { - return 1 - } - return 0 - default: - return 0 - } -} - -func max(a interface{}, i ...interface{}) int64 { - aa := toInt64(a) - for _, b := range i { - bb := toInt64(b) - if bb > aa { - aa = bb - } - } - return aa -} - -func maxf(a interface{}, i ...interface{}) float64 { - aa := toFloat64(a) - for _, b := range i { - bb := toFloat64(b) - aa = math.Max(aa, bb) - } - return aa -} - -func min(a interface{}, i ...interface{}) int64 { - aa := toInt64(a) - for _, b := range i { - bb := toInt64(b) - if bb < aa { - aa = bb - } - } - return aa -} - -func minf(a interface{}, i ...interface{}) float64 { - aa := toFloat64(a) - for _, b := range i { - bb := toFloat64(b) - aa = math.Min(aa, bb) - } - return aa -} - -func until(count int) []int { - step := 1 - if count < 0 { - step = -1 - } - return untilStep(0, count, step) -} - -func untilStep(start, stop, step int) []int { - v := []int{} - - if stop < start { - if step >= 0 { - return v - } - for i := start; i > stop; i += step { - v = append(v, i) - } - return v - } - - if step <= 0 { - return v - } - for i := start; i < stop; i += step { - v = append(v, i) - } - return v -} - -func floor(a interface{}) float64 { - aa := toFloat64(a) - return math.Floor(aa) -} - -func ceil(a interface{}) float64 { - aa := toFloat64(a) - return math.Ceil(aa) -} - -func round(a interface{}, p int, rOpt ...float64) float64 { - roundOn := .5 - if len(rOpt) > 0 { - roundOn = rOpt[0] - } - val := toFloat64(a) - places := toFloat64(p) - - var round float64 - pow := math.Pow(10, places) - digit := pow * val - _, div := math.Modf(digit) - if div >= roundOn { - round = math.Ceil(digit) - } else { - round = math.Floor(digit) - } - return round / pow -} - -// converts unix octal to decimal -func toDecimal(v interface{}) int64 { - result, err := strconv.ParseInt(fmt.Sprint(v), 8, 64) - if err != nil { - return 0 - } - return result -} - -func seq(params ...int) string { - increment := 1 - switch len(params) { - case 0: - return "" - case 1: - start := 1 - end := params[0] - if end < start { - increment = -1 - } - return intArrayToString(untilStep(start, end+increment, increment), " ") - case 3: - start := params[0] - end := params[2] - step := params[1] - if end < start { - increment = -1 - if step > 0 { - return "" - } - } - return intArrayToString(untilStep(start, end+increment, step), " ") - case 2: - start := params[0] - end := params[1] - step := 1 - if end < start { - step = -1 - } - return intArrayToString(untilStep(start, end+step, step), " ") - default: - return "" - } -} - -func intArrayToString(slice []int, delimeter string) string { - return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(slice)), delimeter), "[]") -} diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/reflect.go b/plugins/traefik/vendor/github.com/go-task/slim-sprig/reflect.go deleted file mode 100644 index 8a65c132f..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/reflect.go +++ /dev/null @@ -1,28 +0,0 @@ -package sprig - -import ( - "fmt" - "reflect" -) - -// typeIs returns true if the src is the type named in target. -func typeIs(target string, src interface{}) bool { - return target == typeOf(src) -} - -func typeIsLike(target string, src interface{}) bool { - t := typeOf(src) - return target == t || "*"+target == t -} - -func typeOf(src interface{}) string { - return fmt.Sprintf("%T", src) -} - -func kindIs(target string, src interface{}) bool { - return target == kindOf(src) -} - -func kindOf(src interface{}) string { - return reflect.ValueOf(src).Kind().String() -} diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/regex.go b/plugins/traefik/vendor/github.com/go-task/slim-sprig/regex.go deleted file mode 100644 index fab551018..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/regex.go +++ /dev/null @@ -1,83 +0,0 @@ -package sprig - -import ( - "regexp" -) - -func regexMatch(regex string, s string) bool { - match, _ := regexp.MatchString(regex, s) - return match -} - -func mustRegexMatch(regex string, s string) (bool, error) { - return regexp.MatchString(regex, s) -} - -func regexFindAll(regex string, s string, n int) []string { - r := regexp.MustCompile(regex) - return r.FindAllString(s, n) -} - -func mustRegexFindAll(regex string, s string, n int) ([]string, error) { - r, err := regexp.Compile(regex) - if err != nil { - return []string{}, err - } - return r.FindAllString(s, n), nil -} - -func regexFind(regex string, s string) string { - r := regexp.MustCompile(regex) - return r.FindString(s) -} - -func mustRegexFind(regex string, s string) (string, error) { - r, err := regexp.Compile(regex) - if err != nil { - return "", err - } - return r.FindString(s), nil -} - -func regexReplaceAll(regex string, s string, repl string) string { - r := regexp.MustCompile(regex) - return r.ReplaceAllString(s, repl) -} - -func mustRegexReplaceAll(regex string, s string, repl string) (string, error) { - r, err := regexp.Compile(regex) - if err != nil { - return "", err - } - return r.ReplaceAllString(s, repl), nil -} - -func regexReplaceAllLiteral(regex string, s string, repl string) string { - r := regexp.MustCompile(regex) - return r.ReplaceAllLiteralString(s, repl) -} - -func mustRegexReplaceAllLiteral(regex string, s string, repl string) (string, error) { - r, err := regexp.Compile(regex) - if err != nil { - return "", err - } - return r.ReplaceAllLiteralString(s, repl), nil -} - -func regexSplit(regex string, s string, n int) []string { - r := regexp.MustCompile(regex) - return r.Split(s, n) -} - -func mustRegexSplit(regex string, s string, n int) ([]string, error) { - r, err := regexp.Compile(regex) - if err != nil { - return []string{}, err - } - return r.Split(s, n), nil -} - -func regexQuoteMeta(s string) string { - return regexp.QuoteMeta(s) -} diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/strings.go b/plugins/traefik/vendor/github.com/go-task/slim-sprig/strings.go deleted file mode 100644 index 3c62d6b6f..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/strings.go +++ /dev/null @@ -1,189 +0,0 @@ -package sprig - -import ( - "encoding/base32" - "encoding/base64" - "fmt" - "reflect" - "strconv" - "strings" -) - -func base64encode(v string) string { - return base64.StdEncoding.EncodeToString([]byte(v)) -} - -func base64decode(v string) string { - data, err := base64.StdEncoding.DecodeString(v) - if err != nil { - return err.Error() - } - return string(data) -} - -func base32encode(v string) string { - return base32.StdEncoding.EncodeToString([]byte(v)) -} - -func base32decode(v string) string { - data, err := base32.StdEncoding.DecodeString(v) - if err != nil { - return err.Error() - } - return string(data) -} - -func quote(str ...interface{}) string { - out := make([]string, 0, len(str)) - for _, s := range str { - if s != nil { - out = append(out, fmt.Sprintf("%q", strval(s))) - } - } - return strings.Join(out, " ") -} - -func squote(str ...interface{}) string { - out := make([]string, 0, len(str)) - for _, s := range str { - if s != nil { - out = append(out, fmt.Sprintf("'%v'", s)) - } - } - return strings.Join(out, " ") -} - -func cat(v ...interface{}) string { - v = removeNilElements(v) - r := strings.TrimSpace(strings.Repeat("%v ", len(v))) - return fmt.Sprintf(r, v...) -} - -func indent(spaces int, v string) string { - pad := strings.Repeat(" ", spaces) - return pad + strings.Replace(v, "\n", "\n"+pad, -1) -} - -func nindent(spaces int, v string) string { - return "\n" + indent(spaces, v) -} - -func replace(old, new, src string) string { - return strings.Replace(src, old, new, -1) -} - -func plural(one, many string, count int) string { - if count == 1 { - return one - } - return many -} - -func strslice(v interface{}) []string { - switch v := v.(type) { - case []string: - return v - case []interface{}: - b := make([]string, 0, len(v)) - for _, s := range v { - if s != nil { - b = append(b, strval(s)) - } - } - return b - default: - val := reflect.ValueOf(v) - switch val.Kind() { - case reflect.Array, reflect.Slice: - l := val.Len() - b := make([]string, 0, l) - for i := 0; i < l; i++ { - value := val.Index(i).Interface() - if value != nil { - b = append(b, strval(value)) - } - } - return b - default: - if v == nil { - return []string{} - } - - return []string{strval(v)} - } - } -} - -func removeNilElements(v []interface{}) []interface{} { - newSlice := make([]interface{}, 0, len(v)) - for _, i := range v { - if i != nil { - newSlice = append(newSlice, i) - } - } - return newSlice -} - -func strval(v interface{}) string { - switch v := v.(type) { - case string: - return v - case []byte: - return string(v) - case error: - return v.Error() - case fmt.Stringer: - return v.String() - default: - return fmt.Sprintf("%v", v) - } -} - -func trunc(c int, s string) string { - if c < 0 && len(s)+c > 0 { - return s[len(s)+c:] - } - if c >= 0 && len(s) > c { - return s[:c] - } - return s -} - -func join(sep string, v interface{}) string { - return strings.Join(strslice(v), sep) -} - -func split(sep, orig string) map[string]string { - parts := strings.Split(orig, sep) - res := make(map[string]string, len(parts)) - for i, v := range parts { - res["_"+strconv.Itoa(i)] = v - } - return res -} - -func splitn(sep string, n int, orig string) map[string]string { - parts := strings.SplitN(orig, sep, n) - res := make(map[string]string, len(parts)) - for i, v := range parts { - res["_"+strconv.Itoa(i)] = v - } - return res -} - -// substring creates a substring of the given string. -// -// If start is < 0, this calls string[:end]. -// -// If start is >= 0 and end < 0 or end bigger than s length, this calls string[start:] -// -// Otherwise, this calls string[start, end]. -func substring(start, end int, s string) string { - if start < 0 { - return s[:end] - } - if end < 0 || end > len(s) { - return s[start:] - } - return s[start:end] -} diff --git a/plugins/traefik/vendor/github.com/go-task/slim-sprig/url.go b/plugins/traefik/vendor/github.com/go-task/slim-sprig/url.go deleted file mode 100644 index b8e120e19..000000000 --- a/plugins/traefik/vendor/github.com/go-task/slim-sprig/url.go +++ /dev/null @@ -1,66 +0,0 @@ -package sprig - -import ( - "fmt" - "net/url" - "reflect" -) - -func dictGetOrEmpty(dict map[string]interface{}, key string) string { - value, ok := dict[key] - if !ok { - return "" - } - tp := reflect.TypeOf(value).Kind() - if tp != reflect.String { - panic(fmt.Sprintf("unable to parse %s key, must be of type string, but %s found", key, tp.String())) - } - return reflect.ValueOf(value).String() -} - -// parses given URL to return dict object -func urlParse(v string) map[string]interface{} { - dict := map[string]interface{}{} - parsedURL, err := url.Parse(v) - if err != nil { - panic(fmt.Sprintf("unable to parse url: %s", err)) - } - dict["scheme"] = parsedURL.Scheme - dict["host"] = parsedURL.Host - dict["hostname"] = parsedURL.Hostname() - dict["path"] = parsedURL.Path - dict["query"] = parsedURL.RawQuery - dict["opaque"] = parsedURL.Opaque - dict["fragment"] = parsedURL.Fragment - if parsedURL.User != nil { - dict["userinfo"] = parsedURL.User.String() - } else { - dict["userinfo"] = "" - } - - return dict -} - -// join given dict to URL string -func urlJoin(d map[string]interface{}) string { - resURL := url.URL{ - Scheme: dictGetOrEmpty(d, "scheme"), - Host: dictGetOrEmpty(d, "host"), - Path: dictGetOrEmpty(d, "path"), - RawQuery: dictGetOrEmpty(d, "query"), - Opaque: dictGetOrEmpty(d, "opaque"), - Fragment: dictGetOrEmpty(d, "fragment"), - } - userinfo := dictGetOrEmpty(d, "userinfo") - var user *url.Userinfo - if userinfo != "" { - tempURL, err := url.Parse(fmt.Sprintf("proto://%s@host", userinfo)) - if err != nil { - panic(fmt.Sprintf("unable to parse userinfo in dict: %s", err)) - } - user = tempURL.User - } - - resURL.User = user - return resURL.String() -} diff --git a/plugins/traefik/vendor/github.com/google/pprof/AUTHORS b/plugins/traefik/vendor/github.com/google/pprof/AUTHORS deleted file mode 100644 index fd736cb1c..000000000 --- a/plugins/traefik/vendor/github.com/google/pprof/AUTHORS +++ /dev/null @@ -1,7 +0,0 @@ -# This is the official list of pprof authors for copyright purposes. -# This file is distinct from the CONTRIBUTORS files. -# See the latter for an explanation. -# Names should be added to this file as: -# Name or Organization -# The email address is not required for organizations. -Google Inc. \ No newline at end of file diff --git a/plugins/traefik/vendor/github.com/google/pprof/CONTRIBUTORS b/plugins/traefik/vendor/github.com/google/pprof/CONTRIBUTORS deleted file mode 100644 index 8c8c37d2c..000000000 --- a/plugins/traefik/vendor/github.com/google/pprof/CONTRIBUTORS +++ /dev/null @@ -1,16 +0,0 @@ -# People who have agreed to one of the CLAs and can contribute patches. -# The AUTHORS file lists the copyright holders; this file -# lists people. For example, Google employees are listed here -# but not in AUTHORS, because Google holds the copyright. -# -# https://developers.google.com/open-source/cla/individual -# https://developers.google.com/open-source/cla/corporate -# -# Names should be added to this file as: -# Name -Raul Silvera -Tipp Moseley -Hyoun Kyu Cho -Martin Spier -Taco de Wolff -Andrew Hunter diff --git a/plugins/traefik/vendor/github.com/google/pprof/LICENSE b/plugins/traefik/vendor/github.com/google/pprof/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/plugins/traefik/vendor/github.com/google/pprof/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/plugins/traefik/vendor/github.com/google/pprof/profile/encode.go b/plugins/traefik/vendor/github.com/google/pprof/profile/encode.go deleted file mode 100644 index 182c926b9..000000000 --- a/plugins/traefik/vendor/github.com/google/pprof/profile/encode.go +++ /dev/null @@ -1,588 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package profile - -import ( - "errors" - "sort" - "strings" -) - -func (p *Profile) decoder() []decoder { - return profileDecoder -} - -// preEncode populates the unexported fields to be used by encode -// (with suffix X) from the corresponding exported fields. The -// exported fields are cleared up to facilitate testing. -func (p *Profile) preEncode() { - strings := make(map[string]int) - addString(strings, "") - - for _, st := range p.SampleType { - st.typeX = addString(strings, st.Type) - st.unitX = addString(strings, st.Unit) - } - - for _, s := range p.Sample { - s.labelX = nil - var keys []string - for k := range s.Label { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { - vs := s.Label[k] - for _, v := range vs { - s.labelX = append(s.labelX, - label{ - keyX: addString(strings, k), - strX: addString(strings, v), - }, - ) - } - } - var numKeys []string - for k := range s.NumLabel { - numKeys = append(numKeys, k) - } - sort.Strings(numKeys) - for _, k := range numKeys { - keyX := addString(strings, k) - vs := s.NumLabel[k] - units := s.NumUnit[k] - for i, v := range vs { - var unitX int64 - if len(units) != 0 { - unitX = addString(strings, units[i]) - } - s.labelX = append(s.labelX, - label{ - keyX: keyX, - numX: v, - unitX: unitX, - }, - ) - } - } - s.locationIDX = make([]uint64, len(s.Location)) - for i, loc := range s.Location { - s.locationIDX[i] = loc.ID - } - } - - for _, m := range p.Mapping { - m.fileX = addString(strings, m.File) - m.buildIDX = addString(strings, m.BuildID) - } - - for _, l := range p.Location { - for i, ln := range l.Line { - if ln.Function != nil { - l.Line[i].functionIDX = ln.Function.ID - } else { - l.Line[i].functionIDX = 0 - } - } - if l.Mapping != nil { - l.mappingIDX = l.Mapping.ID - } else { - l.mappingIDX = 0 - } - } - for _, f := range p.Function { - f.nameX = addString(strings, f.Name) - f.systemNameX = addString(strings, f.SystemName) - f.filenameX = addString(strings, f.Filename) - } - - p.dropFramesX = addString(strings, p.DropFrames) - p.keepFramesX = addString(strings, p.KeepFrames) - - if pt := p.PeriodType; pt != nil { - pt.typeX = addString(strings, pt.Type) - pt.unitX = addString(strings, pt.Unit) - } - - p.commentX = nil - for _, c := range p.Comments { - p.commentX = append(p.commentX, addString(strings, c)) - } - - p.defaultSampleTypeX = addString(strings, p.DefaultSampleType) - - p.stringTable = make([]string, len(strings)) - for s, i := range strings { - p.stringTable[i] = s - } -} - -func (p *Profile) encode(b *buffer) { - for _, x := range p.SampleType { - encodeMessage(b, 1, x) - } - for _, x := range p.Sample { - encodeMessage(b, 2, x) - } - for _, x := range p.Mapping { - encodeMessage(b, 3, x) - } - for _, x := range p.Location { - encodeMessage(b, 4, x) - } - for _, x := range p.Function { - encodeMessage(b, 5, x) - } - encodeStrings(b, 6, p.stringTable) - encodeInt64Opt(b, 7, p.dropFramesX) - encodeInt64Opt(b, 8, p.keepFramesX) - encodeInt64Opt(b, 9, p.TimeNanos) - encodeInt64Opt(b, 10, p.DurationNanos) - if pt := p.PeriodType; pt != nil && (pt.typeX != 0 || pt.unitX != 0) { - encodeMessage(b, 11, p.PeriodType) - } - encodeInt64Opt(b, 12, p.Period) - encodeInt64s(b, 13, p.commentX) - encodeInt64(b, 14, p.defaultSampleTypeX) -} - -var profileDecoder = []decoder{ - nil, // 0 - // repeated ValueType sample_type = 1 - func(b *buffer, m message) error { - x := new(ValueType) - pp := m.(*Profile) - pp.SampleType = append(pp.SampleType, x) - return decodeMessage(b, x) - }, - // repeated Sample sample = 2 - func(b *buffer, m message) error { - x := new(Sample) - pp := m.(*Profile) - pp.Sample = append(pp.Sample, x) - return decodeMessage(b, x) - }, - // repeated Mapping mapping = 3 - func(b *buffer, m message) error { - x := new(Mapping) - pp := m.(*Profile) - pp.Mapping = append(pp.Mapping, x) - return decodeMessage(b, x) - }, - // repeated Location location = 4 - func(b *buffer, m message) error { - x := new(Location) - x.Line = b.tmpLines[:0] // Use shared space temporarily - pp := m.(*Profile) - pp.Location = append(pp.Location, x) - err := decodeMessage(b, x) - b.tmpLines = x.Line[:0] - // Copy to shrink size and detach from shared space. - x.Line = append([]Line(nil), x.Line...) - return err - }, - // repeated Function function = 5 - func(b *buffer, m message) error { - x := new(Function) - pp := m.(*Profile) - pp.Function = append(pp.Function, x) - return decodeMessage(b, x) - }, - // repeated string string_table = 6 - func(b *buffer, m message) error { - err := decodeStrings(b, &m.(*Profile).stringTable) - if err != nil { - return err - } - if m.(*Profile).stringTable[0] != "" { - return errors.New("string_table[0] must be ''") - } - return nil - }, - // int64 drop_frames = 7 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).dropFramesX) }, - // int64 keep_frames = 8 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).keepFramesX) }, - // int64 time_nanos = 9 - func(b *buffer, m message) error { - if m.(*Profile).TimeNanos != 0 { - return errConcatProfile - } - return decodeInt64(b, &m.(*Profile).TimeNanos) - }, - // int64 duration_nanos = 10 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).DurationNanos) }, - // ValueType period_type = 11 - func(b *buffer, m message) error { - x := new(ValueType) - pp := m.(*Profile) - pp.PeriodType = x - return decodeMessage(b, x) - }, - // int64 period = 12 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).Period) }, - // repeated int64 comment = 13 - func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Profile).commentX) }, - // int64 defaultSampleType = 14 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).defaultSampleTypeX) }, -} - -// postDecode takes the unexported fields populated by decode (with -// suffix X) and populates the corresponding exported fields. -// The unexported fields are cleared up to facilitate testing. -func (p *Profile) postDecode() error { - var err error - mappings := make(map[uint64]*Mapping, len(p.Mapping)) - mappingIds := make([]*Mapping, len(p.Mapping)+1) - for _, m := range p.Mapping { - m.File, err = getString(p.stringTable, &m.fileX, err) - m.BuildID, err = getString(p.stringTable, &m.buildIDX, err) - if m.ID < uint64(len(mappingIds)) { - mappingIds[m.ID] = m - } else { - mappings[m.ID] = m - } - - // If this a main linux kernel mapping with a relocation symbol suffix - // ("[kernel.kallsyms]_text"), extract said suffix. - // It is fairly hacky to handle at this level, but the alternatives appear even worse. - const prefix = "[kernel.kallsyms]" - if strings.HasPrefix(m.File, prefix) { - m.KernelRelocationSymbol = m.File[len(prefix):] - } - } - - functions := make(map[uint64]*Function, len(p.Function)) - functionIds := make([]*Function, len(p.Function)+1) - for _, f := range p.Function { - f.Name, err = getString(p.stringTable, &f.nameX, err) - f.SystemName, err = getString(p.stringTable, &f.systemNameX, err) - f.Filename, err = getString(p.stringTable, &f.filenameX, err) - if f.ID < uint64(len(functionIds)) { - functionIds[f.ID] = f - } else { - functions[f.ID] = f - } - } - - locations := make(map[uint64]*Location, len(p.Location)) - locationIds := make([]*Location, len(p.Location)+1) - for _, l := range p.Location { - if id := l.mappingIDX; id < uint64(len(mappingIds)) { - l.Mapping = mappingIds[id] - } else { - l.Mapping = mappings[id] - } - l.mappingIDX = 0 - for i, ln := range l.Line { - if id := ln.functionIDX; id != 0 { - l.Line[i].functionIDX = 0 - if id < uint64(len(functionIds)) { - l.Line[i].Function = functionIds[id] - } else { - l.Line[i].Function = functions[id] - } - } - } - if l.ID < uint64(len(locationIds)) { - locationIds[l.ID] = l - } else { - locations[l.ID] = l - } - } - - for _, st := range p.SampleType { - st.Type, err = getString(p.stringTable, &st.typeX, err) - st.Unit, err = getString(p.stringTable, &st.unitX, err) - } - - // Pre-allocate space for all locations. - numLocations := 0 - for _, s := range p.Sample { - numLocations += len(s.locationIDX) - } - locBuffer := make([]*Location, numLocations) - - for _, s := range p.Sample { - if len(s.labelX) > 0 { - labels := make(map[string][]string, len(s.labelX)) - numLabels := make(map[string][]int64, len(s.labelX)) - numUnits := make(map[string][]string, len(s.labelX)) - for _, l := range s.labelX { - var key, value string - key, err = getString(p.stringTable, &l.keyX, err) - if l.strX != 0 { - value, err = getString(p.stringTable, &l.strX, err) - labels[key] = append(labels[key], value) - } else if l.numX != 0 || l.unitX != 0 { - numValues := numLabels[key] - units := numUnits[key] - if l.unitX != 0 { - var unit string - unit, err = getString(p.stringTable, &l.unitX, err) - units = padStringArray(units, len(numValues)) - numUnits[key] = append(units, unit) - } - numLabels[key] = append(numLabels[key], l.numX) - } - } - if len(labels) > 0 { - s.Label = labels - } - if len(numLabels) > 0 { - s.NumLabel = numLabels - for key, units := range numUnits { - if len(units) > 0 { - numUnits[key] = padStringArray(units, len(numLabels[key])) - } - } - s.NumUnit = numUnits - } - } - - s.Location = locBuffer[:len(s.locationIDX)] - locBuffer = locBuffer[len(s.locationIDX):] - for i, lid := range s.locationIDX { - if lid < uint64(len(locationIds)) { - s.Location[i] = locationIds[lid] - } else { - s.Location[i] = locations[lid] - } - } - s.locationIDX = nil - } - - p.DropFrames, err = getString(p.stringTable, &p.dropFramesX, err) - p.KeepFrames, err = getString(p.stringTable, &p.keepFramesX, err) - - if pt := p.PeriodType; pt == nil { - p.PeriodType = &ValueType{} - } - - if pt := p.PeriodType; pt != nil { - pt.Type, err = getString(p.stringTable, &pt.typeX, err) - pt.Unit, err = getString(p.stringTable, &pt.unitX, err) - } - - for _, i := range p.commentX { - var c string - c, err = getString(p.stringTable, &i, err) - p.Comments = append(p.Comments, c) - } - - p.commentX = nil - p.DefaultSampleType, err = getString(p.stringTable, &p.defaultSampleTypeX, err) - p.stringTable = nil - return err -} - -// padStringArray pads arr with enough empty strings to make arr -// length l when arr's length is less than l. -func padStringArray(arr []string, l int) []string { - if l <= len(arr) { - return arr - } - return append(arr, make([]string, l-len(arr))...) -} - -func (p *ValueType) decoder() []decoder { - return valueTypeDecoder -} - -func (p *ValueType) encode(b *buffer) { - encodeInt64Opt(b, 1, p.typeX) - encodeInt64Opt(b, 2, p.unitX) -} - -var valueTypeDecoder = []decoder{ - nil, // 0 - // optional int64 type = 1 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*ValueType).typeX) }, - // optional int64 unit = 2 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*ValueType).unitX) }, -} - -func (p *Sample) decoder() []decoder { - return sampleDecoder -} - -func (p *Sample) encode(b *buffer) { - encodeUint64s(b, 1, p.locationIDX) - encodeInt64s(b, 2, p.Value) - for _, x := range p.labelX { - encodeMessage(b, 3, x) - } -} - -var sampleDecoder = []decoder{ - nil, // 0 - // repeated uint64 location = 1 - func(b *buffer, m message) error { return decodeUint64s(b, &m.(*Sample).locationIDX) }, - // repeated int64 value = 2 - func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Sample).Value) }, - // repeated Label label = 3 - func(b *buffer, m message) error { - s := m.(*Sample) - n := len(s.labelX) - s.labelX = append(s.labelX, label{}) - return decodeMessage(b, &s.labelX[n]) - }, -} - -func (p label) decoder() []decoder { - return labelDecoder -} - -func (p label) encode(b *buffer) { - encodeInt64Opt(b, 1, p.keyX) - encodeInt64Opt(b, 2, p.strX) - encodeInt64Opt(b, 3, p.numX) - encodeInt64Opt(b, 4, p.unitX) -} - -var labelDecoder = []decoder{ - nil, // 0 - // optional int64 key = 1 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).keyX) }, - // optional int64 str = 2 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).strX) }, - // optional int64 num = 3 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).numX) }, - // optional int64 num = 4 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).unitX) }, -} - -func (p *Mapping) decoder() []decoder { - return mappingDecoder -} - -func (p *Mapping) encode(b *buffer) { - encodeUint64Opt(b, 1, p.ID) - encodeUint64Opt(b, 2, p.Start) - encodeUint64Opt(b, 3, p.Limit) - encodeUint64Opt(b, 4, p.Offset) - encodeInt64Opt(b, 5, p.fileX) - encodeInt64Opt(b, 6, p.buildIDX) - encodeBoolOpt(b, 7, p.HasFunctions) - encodeBoolOpt(b, 8, p.HasFilenames) - encodeBoolOpt(b, 9, p.HasLineNumbers) - encodeBoolOpt(b, 10, p.HasInlineFrames) -} - -var mappingDecoder = []decoder{ - nil, // 0 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).ID) }, // optional uint64 id = 1 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Start) }, // optional uint64 memory_offset = 2 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Limit) }, // optional uint64 memory_limit = 3 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Offset) }, // optional uint64 file_offset = 4 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Mapping).fileX) }, // optional int64 filename = 5 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Mapping).buildIDX) }, // optional int64 build_id = 6 - func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasFunctions) }, // optional bool has_functions = 7 - func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasFilenames) }, // optional bool has_filenames = 8 - func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasLineNumbers) }, // optional bool has_line_numbers = 9 - func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasInlineFrames) }, // optional bool has_inline_frames = 10 -} - -func (p *Location) decoder() []decoder { - return locationDecoder -} - -func (p *Location) encode(b *buffer) { - encodeUint64Opt(b, 1, p.ID) - encodeUint64Opt(b, 2, p.mappingIDX) - encodeUint64Opt(b, 3, p.Address) - for i := range p.Line { - encodeMessage(b, 4, &p.Line[i]) - } - encodeBoolOpt(b, 5, p.IsFolded) -} - -var locationDecoder = []decoder{ - nil, // 0 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).ID) }, // optional uint64 id = 1; - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).mappingIDX) }, // optional uint64 mapping_id = 2; - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).Address) }, // optional uint64 address = 3; - func(b *buffer, m message) error { // repeated Line line = 4 - pp := m.(*Location) - n := len(pp.Line) - pp.Line = append(pp.Line, Line{}) - return decodeMessage(b, &pp.Line[n]) - }, - func(b *buffer, m message) error { return decodeBool(b, &m.(*Location).IsFolded) }, // optional bool is_folded = 5; -} - -func (p *Line) decoder() []decoder { - return lineDecoder -} - -func (p *Line) encode(b *buffer) { - encodeUint64Opt(b, 1, p.functionIDX) - encodeInt64Opt(b, 2, p.Line) -} - -var lineDecoder = []decoder{ - nil, // 0 - // optional uint64 function_id = 1 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Line).functionIDX) }, - // optional int64 line = 2 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Line).Line) }, -} - -func (p *Function) decoder() []decoder { - return functionDecoder -} - -func (p *Function) encode(b *buffer) { - encodeUint64Opt(b, 1, p.ID) - encodeInt64Opt(b, 2, p.nameX) - encodeInt64Opt(b, 3, p.systemNameX) - encodeInt64Opt(b, 4, p.filenameX) - encodeInt64Opt(b, 5, p.StartLine) -} - -var functionDecoder = []decoder{ - nil, // 0 - // optional uint64 id = 1 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Function).ID) }, - // optional int64 function_name = 2 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).nameX) }, - // optional int64 function_system_name = 3 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).systemNameX) }, - // repeated int64 filename = 4 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).filenameX) }, - // optional int64 start_line = 5 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).StartLine) }, -} - -func addString(strings map[string]int, s string) int64 { - i, ok := strings[s] - if !ok { - i = len(strings) - strings[s] = i - } - return int64(i) -} - -func getString(strings []string, strng *int64, err error) (string, error) { - if err != nil { - return "", err - } - s := int(*strng) - if s < 0 || s >= len(strings) { - return "", errMalformed - } - *strng = 0 - return strings[s], nil -} diff --git a/plugins/traefik/vendor/github.com/google/pprof/profile/filter.go b/plugins/traefik/vendor/github.com/google/pprof/profile/filter.go deleted file mode 100644 index c794b9390..000000000 --- a/plugins/traefik/vendor/github.com/google/pprof/profile/filter.go +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package profile - -// Implements methods to filter samples from profiles. - -import "regexp" - -// FilterSamplesByName filters the samples in a profile and only keeps -// samples where at least one frame matches focus but none match ignore. -// Returns true is the corresponding regexp matched at least one sample. -func (p *Profile) FilterSamplesByName(focus, ignore, hide, show *regexp.Regexp) (fm, im, hm, hnm bool) { - if focus == nil && ignore == nil && hide == nil && show == nil { - fm = true // Missing focus implies a match - return - } - focusOrIgnore := make(map[uint64]bool) - hidden := make(map[uint64]bool) - for _, l := range p.Location { - if ignore != nil && l.matchesName(ignore) { - im = true - focusOrIgnore[l.ID] = false - } else if focus == nil || l.matchesName(focus) { - fm = true - focusOrIgnore[l.ID] = true - } - - if hide != nil && l.matchesName(hide) { - hm = true - l.Line = l.unmatchedLines(hide) - if len(l.Line) == 0 { - hidden[l.ID] = true - } - } - if show != nil { - l.Line = l.matchedLines(show) - if len(l.Line) == 0 { - hidden[l.ID] = true - } else { - hnm = true - } - } - } - - s := make([]*Sample, 0, len(p.Sample)) - for _, sample := range p.Sample { - if focusedAndNotIgnored(sample.Location, focusOrIgnore) { - if len(hidden) > 0 { - var locs []*Location - for _, loc := range sample.Location { - if !hidden[loc.ID] { - locs = append(locs, loc) - } - } - if len(locs) == 0 { - // Remove sample with no locations (by not adding it to s). - continue - } - sample.Location = locs - } - s = append(s, sample) - } - } - p.Sample = s - - return -} - -// ShowFrom drops all stack frames above the highest matching frame and returns -// whether a match was found. If showFrom is nil it returns false and does not -// modify the profile. -// -// Example: consider a sample with frames [A, B, C, B], where A is the root. -// ShowFrom(nil) returns false and has frames [A, B, C, B]. -// ShowFrom(A) returns true and has frames [A, B, C, B]. -// ShowFrom(B) returns true and has frames [B, C, B]. -// ShowFrom(C) returns true and has frames [C, B]. -// ShowFrom(D) returns false and drops the sample because no frames remain. -func (p *Profile) ShowFrom(showFrom *regexp.Regexp) (matched bool) { - if showFrom == nil { - return false - } - // showFromLocs stores location IDs that matched ShowFrom. - showFromLocs := make(map[uint64]bool) - // Apply to locations. - for _, loc := range p.Location { - if filterShowFromLocation(loc, showFrom) { - showFromLocs[loc.ID] = true - matched = true - } - } - // For all samples, strip locations after the highest matching one. - s := make([]*Sample, 0, len(p.Sample)) - for _, sample := range p.Sample { - for i := len(sample.Location) - 1; i >= 0; i-- { - if showFromLocs[sample.Location[i].ID] { - sample.Location = sample.Location[:i+1] - s = append(s, sample) - break - } - } - } - p.Sample = s - return matched -} - -// filterShowFromLocation tests a showFrom regex against a location, removes -// lines after the last match and returns whether a match was found. If the -// mapping is matched, then all lines are kept. -func filterShowFromLocation(loc *Location, showFrom *regexp.Regexp) bool { - if m := loc.Mapping; m != nil && showFrom.MatchString(m.File) { - return true - } - if i := loc.lastMatchedLineIndex(showFrom); i >= 0 { - loc.Line = loc.Line[:i+1] - return true - } - return false -} - -// lastMatchedLineIndex returns the index of the last line that matches a regex, -// or -1 if no match is found. -func (loc *Location) lastMatchedLineIndex(re *regexp.Regexp) int { - for i := len(loc.Line) - 1; i >= 0; i-- { - if fn := loc.Line[i].Function; fn != nil { - if re.MatchString(fn.Name) || re.MatchString(fn.Filename) { - return i - } - } - } - return -1 -} - -// FilterTagsByName filters the tags in a profile and only keeps -// tags that match show and not hide. -func (p *Profile) FilterTagsByName(show, hide *regexp.Regexp) (sm, hm bool) { - matchRemove := func(name string) bool { - matchShow := show == nil || show.MatchString(name) - matchHide := hide != nil && hide.MatchString(name) - - if matchShow { - sm = true - } - if matchHide { - hm = true - } - return !matchShow || matchHide - } - for _, s := range p.Sample { - for lab := range s.Label { - if matchRemove(lab) { - delete(s.Label, lab) - } - } - for lab := range s.NumLabel { - if matchRemove(lab) { - delete(s.NumLabel, lab) - } - } - } - return -} - -// matchesName returns whether the location matches the regular -// expression. It checks any available function names, file names, and -// mapping object filename. -func (loc *Location) matchesName(re *regexp.Regexp) bool { - for _, ln := range loc.Line { - if fn := ln.Function; fn != nil { - if re.MatchString(fn.Name) || re.MatchString(fn.Filename) { - return true - } - } - } - if m := loc.Mapping; m != nil && re.MatchString(m.File) { - return true - } - return false -} - -// unmatchedLines returns the lines in the location that do not match -// the regular expression. -func (loc *Location) unmatchedLines(re *regexp.Regexp) []Line { - if m := loc.Mapping; m != nil && re.MatchString(m.File) { - return nil - } - var lines []Line - for _, ln := range loc.Line { - if fn := ln.Function; fn != nil { - if re.MatchString(fn.Name) || re.MatchString(fn.Filename) { - continue - } - } - lines = append(lines, ln) - } - return lines -} - -// matchedLines returns the lines in the location that match -// the regular expression. -func (loc *Location) matchedLines(re *regexp.Regexp) []Line { - if m := loc.Mapping; m != nil && re.MatchString(m.File) { - return loc.Line - } - var lines []Line - for _, ln := range loc.Line { - if fn := ln.Function; fn != nil { - if !re.MatchString(fn.Name) && !re.MatchString(fn.Filename) { - continue - } - } - lines = append(lines, ln) - } - return lines -} - -// focusedAndNotIgnored looks up a slice of ids against a map of -// focused/ignored locations. The map only contains locations that are -// explicitly focused or ignored. Returns whether there is at least -// one focused location but no ignored locations. -func focusedAndNotIgnored(locs []*Location, m map[uint64]bool) bool { - var f bool - for _, loc := range locs { - if focus, focusOrIgnore := m[loc.ID]; focusOrIgnore { - if focus { - // Found focused location. Must keep searching in case there - // is an ignored one as well. - f = true - } else { - // Found ignored location. Can return false right away. - return false - } - } - } - return f -} - -// TagMatch selects tags for filtering -type TagMatch func(s *Sample) bool - -// FilterSamplesByTag removes all samples from the profile, except -// those that match focus and do not match the ignore regular -// expression. -func (p *Profile) FilterSamplesByTag(focus, ignore TagMatch) (fm, im bool) { - samples := make([]*Sample, 0, len(p.Sample)) - for _, s := range p.Sample { - focused, ignored := true, false - if focus != nil { - focused = focus(s) - } - if ignore != nil { - ignored = ignore(s) - } - fm = fm || focused - im = im || ignored - if focused && !ignored { - samples = append(samples, s) - } - } - p.Sample = samples - return -} diff --git a/plugins/traefik/vendor/github.com/google/pprof/profile/index.go b/plugins/traefik/vendor/github.com/google/pprof/profile/index.go deleted file mode 100644 index bef1d6046..000000000 --- a/plugins/traefik/vendor/github.com/google/pprof/profile/index.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2016 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package profile - -import ( - "fmt" - "strconv" - "strings" -) - -// SampleIndexByName returns the appropriate index for a value of sample index. -// If numeric, it returns the number, otherwise it looks up the text in the -// profile sample types. -func (p *Profile) SampleIndexByName(sampleIndex string) (int, error) { - if sampleIndex == "" { - if dst := p.DefaultSampleType; dst != "" { - for i, t := range sampleTypes(p) { - if t == dst { - return i, nil - } - } - } - // By default select the last sample value - return len(p.SampleType) - 1, nil - } - if i, err := strconv.Atoi(sampleIndex); err == nil { - if i < 0 || i >= len(p.SampleType) { - return 0, fmt.Errorf("sample_index %s is outside the range [0..%d]", sampleIndex, len(p.SampleType)-1) - } - return i, nil - } - - // Remove the inuse_ prefix to support legacy pprof options - // "inuse_space" and "inuse_objects" for profiles containing types - // "space" and "objects". - noInuse := strings.TrimPrefix(sampleIndex, "inuse_") - for i, t := range p.SampleType { - if t.Type == sampleIndex || t.Type == noInuse { - return i, nil - } - } - - return 0, fmt.Errorf("sample_index %q must be one of: %v", sampleIndex, sampleTypes(p)) -} - -func sampleTypes(p *Profile) []string { - types := make([]string, len(p.SampleType)) - for i, t := range p.SampleType { - types[i] = t.Type - } - return types -} diff --git a/plugins/traefik/vendor/github.com/google/pprof/profile/legacy_java_profile.go b/plugins/traefik/vendor/github.com/google/pprof/profile/legacy_java_profile.go deleted file mode 100644 index 91f45e53c..000000000 --- a/plugins/traefik/vendor/github.com/google/pprof/profile/legacy_java_profile.go +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This file implements parsers to convert java legacy profiles into -// the profile.proto format. - -package profile - -import ( - "bytes" - "fmt" - "io" - "path/filepath" - "regexp" - "strconv" - "strings" -) - -var ( - attributeRx = regexp.MustCompile(`([\w ]+)=([\w ]+)`) - javaSampleRx = regexp.MustCompile(` *(\d+) +(\d+) +@ +([ x0-9a-f]*)`) - javaLocationRx = regexp.MustCompile(`^\s*0x([[:xdigit:]]+)\s+(.*)\s*$`) - javaLocationFileLineRx = regexp.MustCompile(`^(.*)\s+\((.+):(-?[[:digit:]]+)\)$`) - javaLocationPathRx = regexp.MustCompile(`^(.*)\s+\((.*)\)$`) -) - -// javaCPUProfile returns a new Profile from profilez data. -// b is the profile bytes after the header, period is the profiling -// period, and parse is a function to parse 8-byte chunks from the -// profile in its native endianness. -func javaCPUProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte)) (*Profile, error) { - p := &Profile{ - Period: period * 1000, - PeriodType: &ValueType{Type: "cpu", Unit: "nanoseconds"}, - SampleType: []*ValueType{{Type: "samples", Unit: "count"}, {Type: "cpu", Unit: "nanoseconds"}}, - } - var err error - var locs map[uint64]*Location - if b, locs, err = parseCPUSamples(b, parse, false, p); err != nil { - return nil, err - } - - if err = parseJavaLocations(b, locs, p); err != nil { - return nil, err - } - - // Strip out addresses for better merge. - if err = p.Aggregate(true, true, true, true, false); err != nil { - return nil, err - } - - return p, nil -} - -// parseJavaProfile returns a new profile from heapz or contentionz -// data. b is the profile bytes after the header. -func parseJavaProfile(b []byte) (*Profile, error) { - h := bytes.SplitAfterN(b, []byte("\n"), 2) - if len(h) < 2 { - return nil, errUnrecognized - } - - p := &Profile{ - PeriodType: &ValueType{}, - } - header := string(bytes.TrimSpace(h[0])) - - var err error - var pType string - switch header { - case "--- heapz 1 ---": - pType = "heap" - case "--- contentionz 1 ---": - pType = "contention" - default: - return nil, errUnrecognized - } - - if b, err = parseJavaHeader(pType, h[1], p); err != nil { - return nil, err - } - var locs map[uint64]*Location - if b, locs, err = parseJavaSamples(pType, b, p); err != nil { - return nil, err - } - if err = parseJavaLocations(b, locs, p); err != nil { - return nil, err - } - - // Strip out addresses for better merge. - if err = p.Aggregate(true, true, true, true, false); err != nil { - return nil, err - } - - return p, nil -} - -// parseJavaHeader parses the attribute section on a java profile and -// populates a profile. Returns the remainder of the buffer after all -// attributes. -func parseJavaHeader(pType string, b []byte, p *Profile) ([]byte, error) { - nextNewLine := bytes.IndexByte(b, byte('\n')) - for nextNewLine != -1 { - line := string(bytes.TrimSpace(b[0:nextNewLine])) - if line != "" { - h := attributeRx.FindStringSubmatch(line) - if h == nil { - // Not a valid attribute, exit. - return b, nil - } - - attribute, value := strings.TrimSpace(h[1]), strings.TrimSpace(h[2]) - var err error - switch pType + "/" + attribute { - case "heap/format", "cpu/format", "contention/format": - if value != "java" { - return nil, errUnrecognized - } - case "heap/resolution": - p.SampleType = []*ValueType{ - {Type: "inuse_objects", Unit: "count"}, - {Type: "inuse_space", Unit: value}, - } - case "contention/resolution": - p.SampleType = []*ValueType{ - {Type: "contentions", Unit: "count"}, - {Type: "delay", Unit: value}, - } - case "contention/sampling period": - p.PeriodType = &ValueType{ - Type: "contentions", Unit: "count", - } - if p.Period, err = strconv.ParseInt(value, 0, 64); err != nil { - return nil, fmt.Errorf("failed to parse attribute %s: %v", line, err) - } - case "contention/ms since reset": - millis, err := strconv.ParseInt(value, 0, 64) - if err != nil { - return nil, fmt.Errorf("failed to parse attribute %s: %v", line, err) - } - p.DurationNanos = millis * 1000 * 1000 - default: - return nil, errUnrecognized - } - } - // Grab next line. - b = b[nextNewLine+1:] - nextNewLine = bytes.IndexByte(b, byte('\n')) - } - return b, nil -} - -// parseJavaSamples parses the samples from a java profile and -// populates the Samples in a profile. Returns the remainder of the -// buffer after the samples. -func parseJavaSamples(pType string, b []byte, p *Profile) ([]byte, map[uint64]*Location, error) { - nextNewLine := bytes.IndexByte(b, byte('\n')) - locs := make(map[uint64]*Location) - for nextNewLine != -1 { - line := string(bytes.TrimSpace(b[0:nextNewLine])) - if line != "" { - sample := javaSampleRx.FindStringSubmatch(line) - if sample == nil { - // Not a valid sample, exit. - return b, locs, nil - } - - // Java profiles have data/fields inverted compared to other - // profile types. - var err error - value1, value2, value3 := sample[2], sample[1], sample[3] - addrs, err := parseHexAddresses(value3) - if err != nil { - return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) - } - - var sloc []*Location - for _, addr := range addrs { - loc := locs[addr] - if locs[addr] == nil { - loc = &Location{ - Address: addr, - } - p.Location = append(p.Location, loc) - locs[addr] = loc - } - sloc = append(sloc, loc) - } - s := &Sample{ - Value: make([]int64, 2), - Location: sloc, - } - - if s.Value[0], err = strconv.ParseInt(value1, 0, 64); err != nil { - return nil, nil, fmt.Errorf("parsing sample %s: %v", line, err) - } - if s.Value[1], err = strconv.ParseInt(value2, 0, 64); err != nil { - return nil, nil, fmt.Errorf("parsing sample %s: %v", line, err) - } - - switch pType { - case "heap": - const javaHeapzSamplingRate = 524288 // 512K - if s.Value[0] == 0 { - return nil, nil, fmt.Errorf("parsing sample %s: second value must be non-zero", line) - } - s.NumLabel = map[string][]int64{"bytes": {s.Value[1] / s.Value[0]}} - s.Value[0], s.Value[1] = scaleHeapSample(s.Value[0], s.Value[1], javaHeapzSamplingRate) - case "contention": - if period := p.Period; period != 0 { - s.Value[0] = s.Value[0] * p.Period - s.Value[1] = s.Value[1] * p.Period - } - } - p.Sample = append(p.Sample, s) - } - // Grab next line. - b = b[nextNewLine+1:] - nextNewLine = bytes.IndexByte(b, byte('\n')) - } - return b, locs, nil -} - -// parseJavaLocations parses the location information in a java -// profile and populates the Locations in a profile. It uses the -// location addresses from the profile as both the ID of each -// location. -func parseJavaLocations(b []byte, locs map[uint64]*Location, p *Profile) error { - r := bytes.NewBuffer(b) - fns := make(map[string]*Function) - for { - line, err := r.ReadString('\n') - if err != nil { - if err != io.EOF { - return err - } - if line == "" { - break - } - } - - if line = strings.TrimSpace(line); line == "" { - continue - } - - jloc := javaLocationRx.FindStringSubmatch(line) - if len(jloc) != 3 { - continue - } - addr, err := strconv.ParseUint(jloc[1], 16, 64) - if err != nil { - return fmt.Errorf("parsing sample %s: %v", line, err) - } - loc := locs[addr] - if loc == nil { - // Unused/unseen - continue - } - var lineFunc, lineFile string - var lineNo int64 - - if fileLine := javaLocationFileLineRx.FindStringSubmatch(jloc[2]); len(fileLine) == 4 { - // Found a line of the form: "function (file:line)" - lineFunc, lineFile = fileLine[1], fileLine[2] - if n, err := strconv.ParseInt(fileLine[3], 10, 64); err == nil && n > 0 { - lineNo = n - } - } else if filePath := javaLocationPathRx.FindStringSubmatch(jloc[2]); len(filePath) == 3 { - // If there's not a file:line, it's a shared library path. - // The path isn't interesting, so just give the .so. - lineFunc, lineFile = filePath[1], filepath.Base(filePath[2]) - } else if strings.Contains(jloc[2], "generated stub/JIT") { - lineFunc = "STUB" - } else { - // Treat whole line as the function name. This is used by the - // java agent for internal states such as "GC" or "VM". - lineFunc = jloc[2] - } - fn := fns[lineFunc] - - if fn == nil { - fn = &Function{ - Name: lineFunc, - SystemName: lineFunc, - Filename: lineFile, - } - fns[lineFunc] = fn - p.Function = append(p.Function, fn) - } - loc.Line = []Line{ - { - Function: fn, - Line: lineNo, - }, - } - loc.Address = 0 - } - - p.remapLocationIDs() - p.remapFunctionIDs() - p.remapMappingIDs() - - return nil -} diff --git a/plugins/traefik/vendor/github.com/google/pprof/profile/legacy_profile.go b/plugins/traefik/vendor/github.com/google/pprof/profile/legacy_profile.go deleted file mode 100644 index 8d07fd6c2..000000000 --- a/plugins/traefik/vendor/github.com/google/pprof/profile/legacy_profile.go +++ /dev/null @@ -1,1228 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This file implements parsers to convert legacy profiles into the -// profile.proto format. - -package profile - -import ( - "bufio" - "bytes" - "fmt" - "io" - "math" - "regexp" - "strconv" - "strings" -) - -var ( - countStartRE = regexp.MustCompile(`\A(\S+) profile: total \d+\z`) - countRE = regexp.MustCompile(`\A(\d+) @(( 0x[0-9a-f]+)+)\z`) - - heapHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] *@ *(heap[_a-z0-9]*)/?(\d*)`) - heapSampleRE = regexp.MustCompile(`(-?\d+): *(-?\d+) *\[ *(\d+): *(\d+) *] @([ x0-9a-f]*)`) - - contentionSampleRE = regexp.MustCompile(`(\d+) *(\d+) @([ x0-9a-f]*)`) - - hexNumberRE = regexp.MustCompile(`0x[0-9a-f]+`) - - growthHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ growthz?`) - - fragmentationHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ fragmentationz?`) - - threadzStartRE = regexp.MustCompile(`--- threadz \d+ ---`) - threadStartRE = regexp.MustCompile(`--- Thread ([[:xdigit:]]+) \(name: (.*)/(\d+)\) stack: ---`) - - // Regular expressions to parse process mappings. Support the format used by Linux /proc/.../maps and other tools. - // Recommended format: - // Start End object file name offset(optional) linker build id - // 0x40000-0x80000 /path/to/binary (@FF00) abc123456 - spaceDigits = `\s+[[:digit:]]+` - hexPair = `\s+[[:xdigit:]]+:[[:xdigit:]]+` - oSpace = `\s*` - // Capturing expressions. - cHex = `(?:0x)?([[:xdigit:]]+)` - cHexRange = `\s*` + cHex + `[\s-]?` + oSpace + cHex + `:?` - cSpaceString = `(?:\s+(\S+))?` - cSpaceHex = `(?:\s+([[:xdigit:]]+))?` - cSpaceAtOffset = `(?:\s+\(@([[:xdigit:]]+)\))?` - cPerm = `(?:\s+([-rwxp]+))?` - - procMapsRE = regexp.MustCompile(`^` + cHexRange + cPerm + cSpaceHex + hexPair + spaceDigits + cSpaceString) - briefMapsRE = regexp.MustCompile(`^` + cHexRange + cPerm + cSpaceString + cSpaceAtOffset + cSpaceHex) - - // Regular expression to parse log data, of the form: - // ... file:line] msg... - logInfoRE = regexp.MustCompile(`^[^\[\]]+:[0-9]+]\s`) -) - -func isSpaceOrComment(line string) bool { - trimmed := strings.TrimSpace(line) - return len(trimmed) == 0 || trimmed[0] == '#' -} - -// parseGoCount parses a Go count profile (e.g., threadcreate or -// goroutine) and returns a new Profile. -func parseGoCount(b []byte) (*Profile, error) { - s := bufio.NewScanner(bytes.NewBuffer(b)) - // Skip comments at the beginning of the file. - for s.Scan() && isSpaceOrComment(s.Text()) { - } - if err := s.Err(); err != nil { - return nil, err - } - m := countStartRE.FindStringSubmatch(s.Text()) - if m == nil { - return nil, errUnrecognized - } - profileType := m[1] - p := &Profile{ - PeriodType: &ValueType{Type: profileType, Unit: "count"}, - Period: 1, - SampleType: []*ValueType{{Type: profileType, Unit: "count"}}, - } - locations := make(map[uint64]*Location) - for s.Scan() { - line := s.Text() - if isSpaceOrComment(line) { - continue - } - if strings.HasPrefix(line, "---") { - break - } - m := countRE.FindStringSubmatch(line) - if m == nil { - return nil, errMalformed - } - n, err := strconv.ParseInt(m[1], 0, 64) - if err != nil { - return nil, errMalformed - } - fields := strings.Fields(m[2]) - locs := make([]*Location, 0, len(fields)) - for _, stk := range fields { - addr, err := strconv.ParseUint(stk, 0, 64) - if err != nil { - return nil, errMalformed - } - // Adjust all frames by -1 to land on top of the call instruction. - addr-- - loc := locations[addr] - if loc == nil { - loc = &Location{ - Address: addr, - } - locations[addr] = loc - p.Location = append(p.Location, loc) - } - locs = append(locs, loc) - } - p.Sample = append(p.Sample, &Sample{ - Location: locs, - Value: []int64{n}, - }) - } - if err := s.Err(); err != nil { - return nil, err - } - - if err := parseAdditionalSections(s, p); err != nil { - return nil, err - } - return p, nil -} - -// remapLocationIDs ensures there is a location for each address -// referenced by a sample, and remaps the samples to point to the new -// location ids. -func (p *Profile) remapLocationIDs() { - seen := make(map[*Location]bool, len(p.Location)) - var locs []*Location - - for _, s := range p.Sample { - for _, l := range s.Location { - if seen[l] { - continue - } - l.ID = uint64(len(locs) + 1) - locs = append(locs, l) - seen[l] = true - } - } - p.Location = locs -} - -func (p *Profile) remapFunctionIDs() { - seen := make(map[*Function]bool, len(p.Function)) - var fns []*Function - - for _, l := range p.Location { - for _, ln := range l.Line { - fn := ln.Function - if fn == nil || seen[fn] { - continue - } - fn.ID = uint64(len(fns) + 1) - fns = append(fns, fn) - seen[fn] = true - } - } - p.Function = fns -} - -// remapMappingIDs matches location addresses with existing mappings -// and updates them appropriately. This is O(N*M), if this ever shows -// up as a bottleneck, evaluate sorting the mappings and doing a -// binary search, which would make it O(N*log(M)). -func (p *Profile) remapMappingIDs() { - // Some profile handlers will incorrectly set regions for the main - // executable if its section is remapped. Fix them through heuristics. - - if len(p.Mapping) > 0 { - // Remove the initial mapping if named '/anon_hugepage' and has a - // consecutive adjacent mapping. - if m := p.Mapping[0]; strings.HasPrefix(m.File, "/anon_hugepage") { - if len(p.Mapping) > 1 && m.Limit == p.Mapping[1].Start { - p.Mapping = p.Mapping[1:] - } - } - } - - // Subtract the offset from the start of the main mapping if it - // ends up at a recognizable start address. - if len(p.Mapping) > 0 { - const expectedStart = 0x400000 - if m := p.Mapping[0]; m.Start-m.Offset == expectedStart { - m.Start = expectedStart - m.Offset = 0 - } - } - - // Associate each location with an address to the corresponding - // mapping. Create fake mapping if a suitable one isn't found. - var fake *Mapping -nextLocation: - for _, l := range p.Location { - a := l.Address - if l.Mapping != nil || a == 0 { - continue - } - for _, m := range p.Mapping { - if m.Start <= a && a < m.Limit { - l.Mapping = m - continue nextLocation - } - } - // Work around legacy handlers failing to encode the first - // part of mappings split into adjacent ranges. - for _, m := range p.Mapping { - if m.Offset != 0 && m.Start-m.Offset <= a && a < m.Start { - m.Start -= m.Offset - m.Offset = 0 - l.Mapping = m - continue nextLocation - } - } - // If there is still no mapping, create a fake one. - // This is important for the Go legacy handler, which produced - // no mappings. - if fake == nil { - fake = &Mapping{ - ID: 1, - Limit: ^uint64(0), - } - p.Mapping = append(p.Mapping, fake) - } - l.Mapping = fake - } - - // Reset all mapping IDs. - for i, m := range p.Mapping { - m.ID = uint64(i + 1) - } -} - -var cpuInts = []func([]byte) (uint64, []byte){ - get32l, - get32b, - get64l, - get64b, -} - -func get32l(b []byte) (uint64, []byte) { - if len(b) < 4 { - return 0, nil - } - return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24, b[4:] -} - -func get32b(b []byte) (uint64, []byte) { - if len(b) < 4 { - return 0, nil - } - return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24, b[4:] -} - -func get64l(b []byte) (uint64, []byte) { - if len(b) < 8 { - return 0, nil - } - return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56, b[8:] -} - -func get64b(b []byte) (uint64, []byte) { - if len(b) < 8 { - return 0, nil - } - return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56, b[8:] -} - -// parseCPU parses a profilez legacy profile and returns a newly -// populated Profile. -// -// The general format for profilez samples is a sequence of words in -// binary format. The first words are a header with the following data: -// -// 1st word -- 0 -// 2nd word -- 3 -// 3rd word -- 0 if a c++ application, 1 if a java application. -// 4th word -- Sampling period (in microseconds). -// 5th word -- Padding. -func parseCPU(b []byte) (*Profile, error) { - var parse func([]byte) (uint64, []byte) - var n1, n2, n3, n4, n5 uint64 - for _, parse = range cpuInts { - var tmp []byte - n1, tmp = parse(b) - n2, tmp = parse(tmp) - n3, tmp = parse(tmp) - n4, tmp = parse(tmp) - n5, tmp = parse(tmp) - - if tmp != nil && n1 == 0 && n2 == 3 && n3 == 0 && n4 > 0 && n5 == 0 { - b = tmp - return cpuProfile(b, int64(n4), parse) - } - if tmp != nil && n1 == 0 && n2 == 3 && n3 == 1 && n4 > 0 && n5 == 0 { - b = tmp - return javaCPUProfile(b, int64(n4), parse) - } - } - return nil, errUnrecognized -} - -// cpuProfile returns a new Profile from C++ profilez data. -// b is the profile bytes after the header, period is the profiling -// period, and parse is a function to parse 8-byte chunks from the -// profile in its native endianness. -func cpuProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte)) (*Profile, error) { - p := &Profile{ - Period: period * 1000, - PeriodType: &ValueType{Type: "cpu", Unit: "nanoseconds"}, - SampleType: []*ValueType{ - {Type: "samples", Unit: "count"}, - {Type: "cpu", Unit: "nanoseconds"}, - }, - } - var err error - if b, _, err = parseCPUSamples(b, parse, true, p); err != nil { - return nil, err - } - - // If *most* samples have the same second-to-the-bottom frame, it - // strongly suggests that it is an uninteresting artifact of - // measurement -- a stack frame pushed by the signal handler. The - // bottom frame is always correct as it is picked up from the signal - // structure, not the stack. Check if this is the case and if so, - // remove. - - // Remove up to two frames. - maxiter := 2 - // Allow one different sample for this many samples with the same - // second-to-last frame. - similarSamples := 32 - margin := len(p.Sample) / similarSamples - - for iter := 0; iter < maxiter; iter++ { - addr1 := make(map[uint64]int) - for _, s := range p.Sample { - if len(s.Location) > 1 { - a := s.Location[1].Address - addr1[a] = addr1[a] + 1 - } - } - - for id1, count := range addr1 { - if count >= len(p.Sample)-margin { - // Found uninteresting frame, strip it out from all samples - for _, s := range p.Sample { - if len(s.Location) > 1 && s.Location[1].Address == id1 { - s.Location = append(s.Location[:1], s.Location[2:]...) - } - } - break - } - } - } - - if err := p.ParseMemoryMap(bytes.NewBuffer(b)); err != nil { - return nil, err - } - - cleanupDuplicateLocations(p) - return p, nil -} - -func cleanupDuplicateLocations(p *Profile) { - // The profile handler may duplicate the leaf frame, because it gets - // its address both from stack unwinding and from the signal - // context. Detect this and delete the duplicate, which has been - // adjusted by -1. The leaf address should not be adjusted as it is - // not a call. - for _, s := range p.Sample { - if len(s.Location) > 1 && s.Location[0].Address == s.Location[1].Address+1 { - s.Location = append(s.Location[:1], s.Location[2:]...) - } - } -} - -// parseCPUSamples parses a collection of profilez samples from a -// profile. -// -// profilez samples are a repeated sequence of stack frames of the -// form: -// -// 1st word -- The number of times this stack was encountered. -// 2nd word -- The size of the stack (StackSize). -// 3rd word -- The first address on the stack. -// ... -// StackSize + 2 -- The last address on the stack -// -// The last stack trace is of the form: -// -// 1st word -- 0 -// 2nd word -- 1 -// 3rd word -- 0 -// -// Addresses from stack traces may point to the next instruction after -// each call. Optionally adjust by -1 to land somewhere on the actual -// call (except for the leaf, which is not a call). -func parseCPUSamples(b []byte, parse func(b []byte) (uint64, []byte), adjust bool, p *Profile) ([]byte, map[uint64]*Location, error) { - locs := make(map[uint64]*Location) - for len(b) > 0 { - var count, nstk uint64 - count, b = parse(b) - nstk, b = parse(b) - if b == nil || nstk > uint64(len(b)/4) { - return nil, nil, errUnrecognized - } - var sloc []*Location - addrs := make([]uint64, nstk) - for i := 0; i < int(nstk); i++ { - addrs[i], b = parse(b) - } - - if count == 0 && nstk == 1 && addrs[0] == 0 { - // End of data marker - break - } - for i, addr := range addrs { - if adjust && i > 0 { - addr-- - } - loc := locs[addr] - if loc == nil { - loc = &Location{ - Address: addr, - } - locs[addr] = loc - p.Location = append(p.Location, loc) - } - sloc = append(sloc, loc) - } - p.Sample = append(p.Sample, - &Sample{ - Value: []int64{int64(count), int64(count) * p.Period}, - Location: sloc, - }) - } - // Reached the end without finding the EOD marker. - return b, locs, nil -} - -// parseHeap parses a heapz legacy or a growthz profile and -// returns a newly populated Profile. -func parseHeap(b []byte) (p *Profile, err error) { - s := bufio.NewScanner(bytes.NewBuffer(b)) - if !s.Scan() { - if err := s.Err(); err != nil { - return nil, err - } - return nil, errUnrecognized - } - p = &Profile{} - - sampling := "" - hasAlloc := false - - line := s.Text() - p.PeriodType = &ValueType{Type: "space", Unit: "bytes"} - if header := heapHeaderRE.FindStringSubmatch(line); header != nil { - sampling, p.Period, hasAlloc, err = parseHeapHeader(line) - if err != nil { - return nil, err - } - } else if header = growthHeaderRE.FindStringSubmatch(line); header != nil { - p.Period = 1 - } else if header = fragmentationHeaderRE.FindStringSubmatch(line); header != nil { - p.Period = 1 - } else { - return nil, errUnrecognized - } - - if hasAlloc { - // Put alloc before inuse so that default pprof selection - // will prefer inuse_space. - p.SampleType = []*ValueType{ - {Type: "alloc_objects", Unit: "count"}, - {Type: "alloc_space", Unit: "bytes"}, - {Type: "inuse_objects", Unit: "count"}, - {Type: "inuse_space", Unit: "bytes"}, - } - } else { - p.SampleType = []*ValueType{ - {Type: "objects", Unit: "count"}, - {Type: "space", Unit: "bytes"}, - } - } - - locs := make(map[uint64]*Location) - for s.Scan() { - line := strings.TrimSpace(s.Text()) - - if isSpaceOrComment(line) { - continue - } - - if isMemoryMapSentinel(line) { - break - } - - value, blocksize, addrs, err := parseHeapSample(line, p.Period, sampling, hasAlloc) - if err != nil { - return nil, err - } - - var sloc []*Location - for _, addr := range addrs { - // Addresses from stack traces point to the next instruction after - // each call. Adjust by -1 to land somewhere on the actual call. - addr-- - loc := locs[addr] - if locs[addr] == nil { - loc = &Location{ - Address: addr, - } - p.Location = append(p.Location, loc) - locs[addr] = loc - } - sloc = append(sloc, loc) - } - - p.Sample = append(p.Sample, &Sample{ - Value: value, - Location: sloc, - NumLabel: map[string][]int64{"bytes": {blocksize}}, - }) - } - if err := s.Err(); err != nil { - return nil, err - } - if err := parseAdditionalSections(s, p); err != nil { - return nil, err - } - return p, nil -} - -func parseHeapHeader(line string) (sampling string, period int64, hasAlloc bool, err error) { - header := heapHeaderRE.FindStringSubmatch(line) - if header == nil { - return "", 0, false, errUnrecognized - } - - if len(header[6]) > 0 { - if period, err = strconv.ParseInt(header[6], 10, 64); err != nil { - return "", 0, false, errUnrecognized - } - } - - if (header[3] != header[1] && header[3] != "0") || (header[4] != header[2] && header[4] != "0") { - hasAlloc = true - } - - switch header[5] { - case "heapz_v2", "heap_v2": - return "v2", period, hasAlloc, nil - case "heapprofile": - return "", 1, hasAlloc, nil - case "heap": - return "v2", period / 2, hasAlloc, nil - default: - return "", 0, false, errUnrecognized - } -} - -// parseHeapSample parses a single row from a heap profile into a new Sample. -func parseHeapSample(line string, rate int64, sampling string, includeAlloc bool) (value []int64, blocksize int64, addrs []uint64, err error) { - sampleData := heapSampleRE.FindStringSubmatch(line) - if len(sampleData) != 6 { - return nil, 0, nil, fmt.Errorf("unexpected number of sample values: got %d, want 6", len(sampleData)) - } - - // This is a local-scoped helper function to avoid needing to pass - // around rate, sampling and many return parameters. - addValues := func(countString, sizeString string, label string) error { - count, err := strconv.ParseInt(countString, 10, 64) - if err != nil { - return fmt.Errorf("malformed sample: %s: %v", line, err) - } - size, err := strconv.ParseInt(sizeString, 10, 64) - if err != nil { - return fmt.Errorf("malformed sample: %s: %v", line, err) - } - if count == 0 && size != 0 { - return fmt.Errorf("%s count was 0 but %s bytes was %d", label, label, size) - } - if count != 0 { - blocksize = size / count - if sampling == "v2" { - count, size = scaleHeapSample(count, size, rate) - } - } - value = append(value, count, size) - return nil - } - - if includeAlloc { - if err := addValues(sampleData[3], sampleData[4], "allocation"); err != nil { - return nil, 0, nil, err - } - } - - if err := addValues(sampleData[1], sampleData[2], "inuse"); err != nil { - return nil, 0, nil, err - } - - addrs, err = parseHexAddresses(sampleData[5]) - if err != nil { - return nil, 0, nil, fmt.Errorf("malformed sample: %s: %v", line, err) - } - - return value, blocksize, addrs, nil -} - -// parseHexAddresses extracts hex numbers from a string, attempts to convert -// each to an unsigned 64-bit number and returns the resulting numbers as a -// slice, or an error if the string contains hex numbers which are too large to -// handle (which means a malformed profile). -func parseHexAddresses(s string) ([]uint64, error) { - hexStrings := hexNumberRE.FindAllString(s, -1) - var addrs []uint64 - for _, s := range hexStrings { - if addr, err := strconv.ParseUint(s, 0, 64); err == nil { - addrs = append(addrs, addr) - } else { - return nil, fmt.Errorf("failed to parse as hex 64-bit number: %s", s) - } - } - return addrs, nil -} - -// scaleHeapSample adjusts the data from a heapz Sample to -// account for its probability of appearing in the collected -// data. heapz profiles are a sampling of the memory allocations -// requests in a program. We estimate the unsampled value by dividing -// each collected sample by its probability of appearing in the -// profile. heapz v2 profiles rely on a poisson process to determine -// which samples to collect, based on the desired average collection -// rate R. The probability of a sample of size S to appear in that -// profile is 1-exp(-S/R). -func scaleHeapSample(count, size, rate int64) (int64, int64) { - if count == 0 || size == 0 { - return 0, 0 - } - - if rate <= 1 { - // if rate==1 all samples were collected so no adjustment is needed. - // if rate<1 treat as unknown and skip scaling. - return count, size - } - - avgSize := float64(size) / float64(count) - scale := 1 / (1 - math.Exp(-avgSize/float64(rate))) - - return int64(float64(count) * scale), int64(float64(size) * scale) -} - -// parseContention parses a mutex or contention profile. There are 2 cases: -// "--- contentionz " for legacy C++ profiles (and backwards compatibility) -// "--- mutex:" or "--- contention:" for profiles generated by the Go runtime. -func parseContention(b []byte) (*Profile, error) { - s := bufio.NewScanner(bytes.NewBuffer(b)) - if !s.Scan() { - if err := s.Err(); err != nil { - return nil, err - } - return nil, errUnrecognized - } - - switch l := s.Text(); { - case strings.HasPrefix(l, "--- contentionz "): - case strings.HasPrefix(l, "--- mutex:"): - case strings.HasPrefix(l, "--- contention:"): - default: - return nil, errUnrecognized - } - - p := &Profile{ - PeriodType: &ValueType{Type: "contentions", Unit: "count"}, - Period: 1, - SampleType: []*ValueType{ - {Type: "contentions", Unit: "count"}, - {Type: "delay", Unit: "nanoseconds"}, - }, - } - - var cpuHz int64 - // Parse text of the form "attribute = value" before the samples. - const delimiter = "=" - for s.Scan() { - line := s.Text() - if line = strings.TrimSpace(line); isSpaceOrComment(line) { - continue - } - if strings.HasPrefix(line, "---") { - break - } - attr := strings.SplitN(line, delimiter, 2) - if len(attr) != 2 { - break - } - key, val := strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1]) - var err error - switch key { - case "cycles/second": - if cpuHz, err = strconv.ParseInt(val, 0, 64); err != nil { - return nil, errUnrecognized - } - case "sampling period": - if p.Period, err = strconv.ParseInt(val, 0, 64); err != nil { - return nil, errUnrecognized - } - case "ms since reset": - ms, err := strconv.ParseInt(val, 0, 64) - if err != nil { - return nil, errUnrecognized - } - p.DurationNanos = ms * 1000 * 1000 - case "format": - // CPP contentionz profiles don't have format. - return nil, errUnrecognized - case "resolution": - // CPP contentionz profiles don't have resolution. - return nil, errUnrecognized - case "discarded samples": - default: - return nil, errUnrecognized - } - } - if err := s.Err(); err != nil { - return nil, err - } - - locs := make(map[uint64]*Location) - for { - line := strings.TrimSpace(s.Text()) - if strings.HasPrefix(line, "---") { - break - } - if !isSpaceOrComment(line) { - value, addrs, err := parseContentionSample(line, p.Period, cpuHz) - if err != nil { - return nil, err - } - var sloc []*Location - for _, addr := range addrs { - // Addresses from stack traces point to the next instruction after - // each call. Adjust by -1 to land somewhere on the actual call. - addr-- - loc := locs[addr] - if locs[addr] == nil { - loc = &Location{ - Address: addr, - } - p.Location = append(p.Location, loc) - locs[addr] = loc - } - sloc = append(sloc, loc) - } - p.Sample = append(p.Sample, &Sample{ - Value: value, - Location: sloc, - }) - } - if !s.Scan() { - break - } - } - if err := s.Err(); err != nil { - return nil, err - } - - if err := parseAdditionalSections(s, p); err != nil { - return nil, err - } - - return p, nil -} - -// parseContentionSample parses a single row from a contention profile -// into a new Sample. -func parseContentionSample(line string, period, cpuHz int64) (value []int64, addrs []uint64, err error) { - sampleData := contentionSampleRE.FindStringSubmatch(line) - if sampleData == nil { - return nil, nil, errUnrecognized - } - - v1, err := strconv.ParseInt(sampleData[1], 10, 64) - if err != nil { - return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) - } - v2, err := strconv.ParseInt(sampleData[2], 10, 64) - if err != nil { - return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) - } - - // Unsample values if period and cpuHz are available. - // - Delays are scaled to cycles and then to nanoseconds. - // - Contentions are scaled to cycles. - if period > 0 { - if cpuHz > 0 { - cpuGHz := float64(cpuHz) / 1e9 - v1 = int64(float64(v1) * float64(period) / cpuGHz) - } - v2 = v2 * period - } - - value = []int64{v2, v1} - addrs, err = parseHexAddresses(sampleData[3]) - if err != nil { - return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) - } - - return value, addrs, nil -} - -// parseThread parses a Threadz profile and returns a new Profile. -func parseThread(b []byte) (*Profile, error) { - s := bufio.NewScanner(bytes.NewBuffer(b)) - // Skip past comments and empty lines seeking a real header. - for s.Scan() && isSpaceOrComment(s.Text()) { - } - - line := s.Text() - if m := threadzStartRE.FindStringSubmatch(line); m != nil { - // Advance over initial comments until first stack trace. - for s.Scan() { - if line = s.Text(); isMemoryMapSentinel(line) || strings.HasPrefix(line, "-") { - break - } - } - } else if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 { - return nil, errUnrecognized - } - - p := &Profile{ - SampleType: []*ValueType{{Type: "thread", Unit: "count"}}, - PeriodType: &ValueType{Type: "thread", Unit: "count"}, - Period: 1, - } - - locs := make(map[uint64]*Location) - // Recognize each thread and populate profile samples. - for !isMemoryMapSentinel(line) { - if strings.HasPrefix(line, "---- no stack trace for") { - break - } - if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 { - return nil, errUnrecognized - } - - var addrs []uint64 - var err error - line, addrs, err = parseThreadSample(s) - if err != nil { - return nil, err - } - if len(addrs) == 0 { - // We got a --same as previous threads--. Bump counters. - if len(p.Sample) > 0 { - s := p.Sample[len(p.Sample)-1] - s.Value[0]++ - } - continue - } - - var sloc []*Location - for i, addr := range addrs { - // Addresses from stack traces point to the next instruction after - // each call. Adjust by -1 to land somewhere on the actual call - // (except for the leaf, which is not a call). - if i > 0 { - addr-- - } - loc := locs[addr] - if locs[addr] == nil { - loc = &Location{ - Address: addr, - } - p.Location = append(p.Location, loc) - locs[addr] = loc - } - sloc = append(sloc, loc) - } - - p.Sample = append(p.Sample, &Sample{ - Value: []int64{1}, - Location: sloc, - }) - } - - if err := parseAdditionalSections(s, p); err != nil { - return nil, err - } - - cleanupDuplicateLocations(p) - return p, nil -} - -// parseThreadSample parses a symbolized or unsymbolized stack trace. -// Returns the first line after the traceback, the sample (or nil if -// it hits a 'same-as-previous' marker) and an error. -func parseThreadSample(s *bufio.Scanner) (nextl string, addrs []uint64, err error) { - var line string - sameAsPrevious := false - for s.Scan() { - line = strings.TrimSpace(s.Text()) - if line == "" { - continue - } - - if strings.HasPrefix(line, "---") { - break - } - if strings.Contains(line, "same as previous thread") { - sameAsPrevious = true - continue - } - - curAddrs, err := parseHexAddresses(line) - if err != nil { - return "", nil, fmt.Errorf("malformed sample: %s: %v", line, err) - } - addrs = append(addrs, curAddrs...) - } - if err := s.Err(); err != nil { - return "", nil, err - } - if sameAsPrevious { - return line, nil, nil - } - return line, addrs, nil -} - -// parseAdditionalSections parses any additional sections in the -// profile, ignoring any unrecognized sections. -func parseAdditionalSections(s *bufio.Scanner, p *Profile) error { - for !isMemoryMapSentinel(s.Text()) && s.Scan() { - } - if err := s.Err(); err != nil { - return err - } - return p.ParseMemoryMapFromScanner(s) -} - -// ParseProcMaps parses a memory map in the format of /proc/self/maps. -// ParseMemoryMap should be called after setting on a profile to -// associate locations to the corresponding mapping based on their -// address. -func ParseProcMaps(rd io.Reader) ([]*Mapping, error) { - s := bufio.NewScanner(rd) - return parseProcMapsFromScanner(s) -} - -func parseProcMapsFromScanner(s *bufio.Scanner) ([]*Mapping, error) { - var mapping []*Mapping - - var attrs []string - const delimiter = "=" - r := strings.NewReplacer() - for s.Scan() { - line := r.Replace(removeLoggingInfo(s.Text())) - m, err := parseMappingEntry(line) - if err != nil { - if err == errUnrecognized { - // Recognize assignments of the form: attr=value, and replace - // $attr with value on subsequent mappings. - if attr := strings.SplitN(line, delimiter, 2); len(attr) == 2 { - attrs = append(attrs, "$"+strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1])) - r = strings.NewReplacer(attrs...) - } - // Ignore any unrecognized entries - continue - } - return nil, err - } - if m == nil { - continue - } - mapping = append(mapping, m) - } - if err := s.Err(); err != nil { - return nil, err - } - return mapping, nil -} - -// removeLoggingInfo detects and removes log prefix entries generated -// by the glog package. If no logging prefix is detected, the string -// is returned unmodified. -func removeLoggingInfo(line string) string { - if match := logInfoRE.FindStringIndex(line); match != nil { - return line[match[1]:] - } - return line -} - -// ParseMemoryMap parses a memory map in the format of -// /proc/self/maps, and overrides the mappings in the current profile. -// It renumbers the samples and locations in the profile correspondingly. -func (p *Profile) ParseMemoryMap(rd io.Reader) error { - return p.ParseMemoryMapFromScanner(bufio.NewScanner(rd)) -} - -// ParseMemoryMapFromScanner parses a memory map in the format of -// /proc/self/maps or a variety of legacy format, and overrides the -// mappings in the current profile. It renumbers the samples and -// locations in the profile correspondingly. -func (p *Profile) ParseMemoryMapFromScanner(s *bufio.Scanner) error { - mapping, err := parseProcMapsFromScanner(s) - if err != nil { - return err - } - p.Mapping = append(p.Mapping, mapping...) - p.massageMappings() - p.remapLocationIDs() - p.remapFunctionIDs() - p.remapMappingIDs() - return nil -} - -func parseMappingEntry(l string) (*Mapping, error) { - var start, end, perm, file, offset, buildID string - if me := procMapsRE.FindStringSubmatch(l); len(me) == 6 { - start, end, perm, offset, file = me[1], me[2], me[3], me[4], me[5] - } else if me := briefMapsRE.FindStringSubmatch(l); len(me) == 7 { - start, end, perm, file, offset, buildID = me[1], me[2], me[3], me[4], me[5], me[6] - } else { - return nil, errUnrecognized - } - - var err error - mapping := &Mapping{ - File: file, - BuildID: buildID, - } - if perm != "" && !strings.Contains(perm, "x") { - // Skip non-executable entries. - return nil, nil - } - if mapping.Start, err = strconv.ParseUint(start, 16, 64); err != nil { - return nil, errUnrecognized - } - if mapping.Limit, err = strconv.ParseUint(end, 16, 64); err != nil { - return nil, errUnrecognized - } - if offset != "" { - if mapping.Offset, err = strconv.ParseUint(offset, 16, 64); err != nil { - return nil, errUnrecognized - } - } - return mapping, nil -} - -var memoryMapSentinels = []string{ - "--- Memory map: ---", - "MAPPED_LIBRARIES:", -} - -// isMemoryMapSentinel returns true if the string contains one of the -// known sentinels for memory map information. -func isMemoryMapSentinel(line string) bool { - for _, s := range memoryMapSentinels { - if strings.Contains(line, s) { - return true - } - } - return false -} - -func (p *Profile) addLegacyFrameInfo() { - switch { - case isProfileType(p, heapzSampleTypes): - p.DropFrames, p.KeepFrames = allocRxStr, allocSkipRxStr - case isProfileType(p, contentionzSampleTypes): - p.DropFrames, p.KeepFrames = lockRxStr, "" - default: - p.DropFrames, p.KeepFrames = cpuProfilerRxStr, "" - } -} - -var heapzSampleTypes = [][]string{ - {"allocations", "size"}, // early Go pprof profiles - {"objects", "space"}, - {"inuse_objects", "inuse_space"}, - {"alloc_objects", "alloc_space"}, - {"alloc_objects", "alloc_space", "inuse_objects", "inuse_space"}, // Go pprof legacy profiles -} -var contentionzSampleTypes = [][]string{ - {"contentions", "delay"}, -} - -func isProfileType(p *Profile, types [][]string) bool { - st := p.SampleType -nextType: - for _, t := range types { - if len(st) != len(t) { - continue - } - - for i := range st { - if st[i].Type != t[i] { - continue nextType - } - } - return true - } - return false -} - -var allocRxStr = strings.Join([]string{ - // POSIX entry points. - `calloc`, - `cfree`, - `malloc`, - `free`, - `memalign`, - `do_memalign`, - `(__)?posix_memalign`, - `pvalloc`, - `valloc`, - `realloc`, - - // TC malloc. - `tcmalloc::.*`, - `tc_calloc`, - `tc_cfree`, - `tc_malloc`, - `tc_free`, - `tc_memalign`, - `tc_posix_memalign`, - `tc_pvalloc`, - `tc_valloc`, - `tc_realloc`, - `tc_new`, - `tc_delete`, - `tc_newarray`, - `tc_deletearray`, - `tc_new_nothrow`, - `tc_newarray_nothrow`, - - // Memory-allocation routines on OS X. - `malloc_zone_malloc`, - `malloc_zone_calloc`, - `malloc_zone_valloc`, - `malloc_zone_realloc`, - `malloc_zone_memalign`, - `malloc_zone_free`, - - // Go runtime - `runtime\..*`, - - // Other misc. memory allocation routines - `BaseArena::.*`, - `(::)?do_malloc_no_errno`, - `(::)?do_malloc_pages`, - `(::)?do_malloc`, - `DoSampledAllocation`, - `MallocedMemBlock::MallocedMemBlock`, - `_M_allocate`, - `__builtin_(vec_)?delete`, - `__builtin_(vec_)?new`, - `__gnu_cxx::new_allocator::allocate`, - `__libc_malloc`, - `__malloc_alloc_template::allocate`, - `allocate`, - `cpp_alloc`, - `operator new(\[\])?`, - `simple_alloc::allocate`, -}, `|`) - -var allocSkipRxStr = strings.Join([]string{ - // Preserve Go runtime frames that appear in the middle/bottom of - // the stack. - `runtime\.panic`, - `runtime\.reflectcall`, - `runtime\.call[0-9]*`, -}, `|`) - -var cpuProfilerRxStr = strings.Join([]string{ - `ProfileData::Add`, - `ProfileData::prof_handler`, - `CpuProfiler::prof_handler`, - `__pthread_sighandler`, - `__restore`, -}, `|`) - -var lockRxStr = strings.Join([]string{ - `RecordLockProfileData`, - `(base::)?RecordLockProfileData.*`, - `(base::)?SubmitMutexProfileData.*`, - `(base::)?SubmitSpinLockProfileData.*`, - `(base::Mutex::)?AwaitCommon.*`, - `(base::Mutex::)?Unlock.*`, - `(base::Mutex::)?UnlockSlow.*`, - `(base::Mutex::)?ReaderUnlock.*`, - `(base::MutexLock::)?~MutexLock.*`, - `(Mutex::)?AwaitCommon.*`, - `(Mutex::)?Unlock.*`, - `(Mutex::)?UnlockSlow.*`, - `(Mutex::)?ReaderUnlock.*`, - `(MutexLock::)?~MutexLock.*`, - `(SpinLock::)?Unlock.*`, - `(SpinLock::)?SlowUnlock.*`, - `(SpinLockHolder::)?~SpinLockHolder.*`, -}, `|`) diff --git a/plugins/traefik/vendor/github.com/google/pprof/profile/merge.go b/plugins/traefik/vendor/github.com/google/pprof/profile/merge.go deleted file mode 100644 index 4b66282cb..000000000 --- a/plugins/traefik/vendor/github.com/google/pprof/profile/merge.go +++ /dev/null @@ -1,667 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package profile - -import ( - "encoding/binary" - "fmt" - "sort" - "strconv" - "strings" -) - -// Compact performs garbage collection on a profile to remove any -// unreferenced fields. This is useful to reduce the size of a profile -// after samples or locations have been removed. -func (p *Profile) Compact() *Profile { - p, _ = Merge([]*Profile{p}) - return p -} - -// Merge merges all the profiles in profs into a single Profile. -// Returns a new profile independent of the input profiles. The merged -// profile is compacted to eliminate unused samples, locations, -// functions and mappings. Profiles must have identical profile sample -// and period types or the merge will fail. profile.Period of the -// resulting profile will be the maximum of all profiles, and -// profile.TimeNanos will be the earliest nonzero one. Merges are -// associative with the caveat of the first profile having some -// specialization in how headers are combined. There may be other -// subtleties now or in the future regarding associativity. -func Merge(srcs []*Profile) (*Profile, error) { - if len(srcs) == 0 { - return nil, fmt.Errorf("no profiles to merge") - } - p, err := combineHeaders(srcs) - if err != nil { - return nil, err - } - - pm := &profileMerger{ - p: p, - samples: make(map[sampleKey]*Sample, len(srcs[0].Sample)), - locations: make(map[locationKey]*Location, len(srcs[0].Location)), - functions: make(map[functionKey]*Function, len(srcs[0].Function)), - mappings: make(map[mappingKey]*Mapping, len(srcs[0].Mapping)), - } - - for _, src := range srcs { - // Clear the profile-specific hash tables - pm.locationsByID = makeLocationIDMap(len(src.Location)) - pm.functionsByID = make(map[uint64]*Function, len(src.Function)) - pm.mappingsByID = make(map[uint64]mapInfo, len(src.Mapping)) - - if len(pm.mappings) == 0 && len(src.Mapping) > 0 { - // The Mapping list has the property that the first mapping - // represents the main binary. Take the first Mapping we see, - // otherwise the operations below will add mappings in an - // arbitrary order. - pm.mapMapping(src.Mapping[0]) - } - - for _, s := range src.Sample { - if !isZeroSample(s) { - pm.mapSample(s) - } - } - } - - for _, s := range p.Sample { - if isZeroSample(s) { - // If there are any zero samples, re-merge the profile to GC - // them. - return Merge([]*Profile{p}) - } - } - - return p, nil -} - -// Normalize normalizes the source profile by multiplying each value in profile by the -// ratio of the sum of the base profile's values of that sample type to the sum of the -// source profile's value of that sample type. -func (p *Profile) Normalize(pb *Profile) error { - - if err := p.compatible(pb); err != nil { - return err - } - - baseVals := make([]int64, len(p.SampleType)) - for _, s := range pb.Sample { - for i, v := range s.Value { - baseVals[i] += v - } - } - - srcVals := make([]int64, len(p.SampleType)) - for _, s := range p.Sample { - for i, v := range s.Value { - srcVals[i] += v - } - } - - normScale := make([]float64, len(baseVals)) - for i := range baseVals { - if srcVals[i] == 0 { - normScale[i] = 0.0 - } else { - normScale[i] = float64(baseVals[i]) / float64(srcVals[i]) - } - } - p.ScaleN(normScale) - return nil -} - -func isZeroSample(s *Sample) bool { - for _, v := range s.Value { - if v != 0 { - return false - } - } - return true -} - -type profileMerger struct { - p *Profile - - // Memoization tables within a profile. - locationsByID locationIDMap - functionsByID map[uint64]*Function - mappingsByID map[uint64]mapInfo - - // Memoization tables for profile entities. - samples map[sampleKey]*Sample - locations map[locationKey]*Location - functions map[functionKey]*Function - mappings map[mappingKey]*Mapping -} - -type mapInfo struct { - m *Mapping - offset int64 -} - -func (pm *profileMerger) mapSample(src *Sample) *Sample { - // Check memoization table - k := pm.sampleKey(src) - if ss, ok := pm.samples[k]; ok { - for i, v := range src.Value { - ss.Value[i] += v - } - return ss - } - - // Make new sample. - s := &Sample{ - Location: make([]*Location, len(src.Location)), - Value: make([]int64, len(src.Value)), - Label: make(map[string][]string, len(src.Label)), - NumLabel: make(map[string][]int64, len(src.NumLabel)), - NumUnit: make(map[string][]string, len(src.NumLabel)), - } - for i, l := range src.Location { - s.Location[i] = pm.mapLocation(l) - } - for k, v := range src.Label { - vv := make([]string, len(v)) - copy(vv, v) - s.Label[k] = vv - } - for k, v := range src.NumLabel { - u := src.NumUnit[k] - vv := make([]int64, len(v)) - uu := make([]string, len(u)) - copy(vv, v) - copy(uu, u) - s.NumLabel[k] = vv - s.NumUnit[k] = uu - } - copy(s.Value, src.Value) - pm.samples[k] = s - pm.p.Sample = append(pm.p.Sample, s) - return s -} - -func (pm *profileMerger) sampleKey(sample *Sample) sampleKey { - // Accumulate contents into a string. - var buf strings.Builder - buf.Grow(64) // Heuristic to avoid extra allocs - - // encode a number - putNumber := func(v uint64) { - var num [binary.MaxVarintLen64]byte - n := binary.PutUvarint(num[:], v) - buf.Write(num[:n]) - } - - // encode a string prefixed with its length. - putDelimitedString := func(s string) { - putNumber(uint64(len(s))) - buf.WriteString(s) - } - - for _, l := range sample.Location { - // Get the location in the merged profile, which may have a different ID. - if loc := pm.mapLocation(l); loc != nil { - putNumber(loc.ID) - } - } - putNumber(0) // Delimiter - - for _, l := range sortedKeys1(sample.Label) { - putDelimitedString(l) - values := sample.Label[l] - putNumber(uint64(len(values))) - for _, v := range values { - putDelimitedString(v) - } - } - - for _, l := range sortedKeys2(sample.NumLabel) { - putDelimitedString(l) - values := sample.NumLabel[l] - putNumber(uint64(len(values))) - for _, v := range values { - putNumber(uint64(v)) - } - units := sample.NumUnit[l] - putNumber(uint64(len(units))) - for _, v := range units { - putDelimitedString(v) - } - } - - return sampleKey(buf.String()) -} - -type sampleKey string - -// sortedKeys1 returns the sorted keys found in a string->[]string map. -// -// Note: this is currently non-generic since github pprof runs golint, -// which does not support generics. When that issue is fixed, it can -// be merged with sortedKeys2 and made into a generic function. -func sortedKeys1(m map[string][]string) []string { - if len(m) == 0 { - return nil - } - keys := make([]string, 0, len(m)) - for k := range m { - keys = append(keys, k) - } - sort.Strings(keys) - return keys -} - -// sortedKeys2 returns the sorted keys found in a string->[]int64 map. -// -// Note: this is currently non-generic since github pprof runs golint, -// which does not support generics. When that issue is fixed, it can -// be merged with sortedKeys1 and made into a generic function. -func sortedKeys2(m map[string][]int64) []string { - if len(m) == 0 { - return nil - } - keys := make([]string, 0, len(m)) - for k := range m { - keys = append(keys, k) - } - sort.Strings(keys) - return keys -} - -func (pm *profileMerger) mapLocation(src *Location) *Location { - if src == nil { - return nil - } - - if l := pm.locationsByID.get(src.ID); l != nil { - return l - } - - mi := pm.mapMapping(src.Mapping) - l := &Location{ - ID: uint64(len(pm.p.Location) + 1), - Mapping: mi.m, - Address: uint64(int64(src.Address) + mi.offset), - Line: make([]Line, len(src.Line)), - IsFolded: src.IsFolded, - } - for i, ln := range src.Line { - l.Line[i] = pm.mapLine(ln) - } - // Check memoization table. Must be done on the remapped location to - // account for the remapped mapping ID. - k := l.key() - if ll, ok := pm.locations[k]; ok { - pm.locationsByID.set(src.ID, ll) - return ll - } - pm.locationsByID.set(src.ID, l) - pm.locations[k] = l - pm.p.Location = append(pm.p.Location, l) - return l -} - -// key generates locationKey to be used as a key for maps. -func (l *Location) key() locationKey { - key := locationKey{ - addr: l.Address, - isFolded: l.IsFolded, - } - if l.Mapping != nil { - // Normalizes address to handle address space randomization. - key.addr -= l.Mapping.Start - key.mappingID = l.Mapping.ID - } - lines := make([]string, len(l.Line)*2) - for i, line := range l.Line { - if line.Function != nil { - lines[i*2] = strconv.FormatUint(line.Function.ID, 16) - } - lines[i*2+1] = strconv.FormatInt(line.Line, 16) - } - key.lines = strings.Join(lines, "|") - return key -} - -type locationKey struct { - addr, mappingID uint64 - lines string - isFolded bool -} - -func (pm *profileMerger) mapMapping(src *Mapping) mapInfo { - if src == nil { - return mapInfo{} - } - - if mi, ok := pm.mappingsByID[src.ID]; ok { - return mi - } - - // Check memoization tables. - mk := src.key() - if m, ok := pm.mappings[mk]; ok { - mi := mapInfo{m, int64(m.Start) - int64(src.Start)} - pm.mappingsByID[src.ID] = mi - return mi - } - m := &Mapping{ - ID: uint64(len(pm.p.Mapping) + 1), - Start: src.Start, - Limit: src.Limit, - Offset: src.Offset, - File: src.File, - KernelRelocationSymbol: src.KernelRelocationSymbol, - BuildID: src.BuildID, - HasFunctions: src.HasFunctions, - HasFilenames: src.HasFilenames, - HasLineNumbers: src.HasLineNumbers, - HasInlineFrames: src.HasInlineFrames, - } - pm.p.Mapping = append(pm.p.Mapping, m) - - // Update memoization tables. - pm.mappings[mk] = m - mi := mapInfo{m, 0} - pm.mappingsByID[src.ID] = mi - return mi -} - -// key generates encoded strings of Mapping to be used as a key for -// maps. -func (m *Mapping) key() mappingKey { - // Normalize addresses to handle address space randomization. - // Round up to next 4K boundary to avoid minor discrepancies. - const mapsizeRounding = 0x1000 - - size := m.Limit - m.Start - size = size + mapsizeRounding - 1 - size = size - (size % mapsizeRounding) - key := mappingKey{ - size: size, - offset: m.Offset, - } - - switch { - case m.BuildID != "": - key.buildIDOrFile = m.BuildID - case m.File != "": - key.buildIDOrFile = m.File - default: - // A mapping containing neither build ID nor file name is a fake mapping. A - // key with empty buildIDOrFile is used for fake mappings so that they are - // treated as the same mapping during merging. - } - return key -} - -type mappingKey struct { - size, offset uint64 - buildIDOrFile string -} - -func (pm *profileMerger) mapLine(src Line) Line { - ln := Line{ - Function: pm.mapFunction(src.Function), - Line: src.Line, - } - return ln -} - -func (pm *profileMerger) mapFunction(src *Function) *Function { - if src == nil { - return nil - } - if f, ok := pm.functionsByID[src.ID]; ok { - return f - } - k := src.key() - if f, ok := pm.functions[k]; ok { - pm.functionsByID[src.ID] = f - return f - } - f := &Function{ - ID: uint64(len(pm.p.Function) + 1), - Name: src.Name, - SystemName: src.SystemName, - Filename: src.Filename, - StartLine: src.StartLine, - } - pm.functions[k] = f - pm.functionsByID[src.ID] = f - pm.p.Function = append(pm.p.Function, f) - return f -} - -// key generates a struct to be used as a key for maps. -func (f *Function) key() functionKey { - return functionKey{ - f.StartLine, - f.Name, - f.SystemName, - f.Filename, - } -} - -type functionKey struct { - startLine int64 - name, systemName, fileName string -} - -// combineHeaders checks that all profiles can be merged and returns -// their combined profile. -func combineHeaders(srcs []*Profile) (*Profile, error) { - for _, s := range srcs[1:] { - if err := srcs[0].compatible(s); err != nil { - return nil, err - } - } - - var timeNanos, durationNanos, period int64 - var comments []string - seenComments := map[string]bool{} - var defaultSampleType string - for _, s := range srcs { - if timeNanos == 0 || s.TimeNanos < timeNanos { - timeNanos = s.TimeNanos - } - durationNanos += s.DurationNanos - if period == 0 || period < s.Period { - period = s.Period - } - for _, c := range s.Comments { - if seen := seenComments[c]; !seen { - comments = append(comments, c) - seenComments[c] = true - } - } - if defaultSampleType == "" { - defaultSampleType = s.DefaultSampleType - } - } - - p := &Profile{ - SampleType: make([]*ValueType, len(srcs[0].SampleType)), - - DropFrames: srcs[0].DropFrames, - KeepFrames: srcs[0].KeepFrames, - - TimeNanos: timeNanos, - DurationNanos: durationNanos, - PeriodType: srcs[0].PeriodType, - Period: period, - - Comments: comments, - DefaultSampleType: defaultSampleType, - } - copy(p.SampleType, srcs[0].SampleType) - return p, nil -} - -// compatible determines if two profiles can be compared/merged. -// returns nil if the profiles are compatible; otherwise an error with -// details on the incompatibility. -func (p *Profile) compatible(pb *Profile) error { - if !equalValueType(p.PeriodType, pb.PeriodType) { - return fmt.Errorf("incompatible period types %v and %v", p.PeriodType, pb.PeriodType) - } - - if len(p.SampleType) != len(pb.SampleType) { - return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType) - } - - for i := range p.SampleType { - if !equalValueType(p.SampleType[i], pb.SampleType[i]) { - return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType) - } - } - return nil -} - -// equalValueType returns true if the two value types are semantically -// equal. It ignores the internal fields used during encode/decode. -func equalValueType(st1, st2 *ValueType) bool { - return st1.Type == st2.Type && st1.Unit == st2.Unit -} - -// locationIDMap is like a map[uint64]*Location, but provides efficiency for -// ids that are densely numbered, which is often the case. -type locationIDMap struct { - dense []*Location // indexed by id for id < len(dense) - sparse map[uint64]*Location // indexed by id for id >= len(dense) -} - -func makeLocationIDMap(n int) locationIDMap { - return locationIDMap{ - dense: make([]*Location, n), - sparse: map[uint64]*Location{}, - } -} - -func (lm locationIDMap) get(id uint64) *Location { - if id < uint64(len(lm.dense)) { - return lm.dense[int(id)] - } - return lm.sparse[id] -} - -func (lm locationIDMap) set(id uint64, loc *Location) { - if id < uint64(len(lm.dense)) { - lm.dense[id] = loc - return - } - lm.sparse[id] = loc -} - -// CompatibilizeSampleTypes makes profiles compatible to be compared/merged. It -// keeps sample types that appear in all profiles only and drops/reorders the -// sample types as necessary. -// -// In the case of sample types order is not the same for given profiles the -// order is derived from the first profile. -// -// Profiles are modified in-place. -// -// It returns an error if the sample type's intersection is empty. -func CompatibilizeSampleTypes(ps []*Profile) error { - sTypes := commonSampleTypes(ps) - if len(sTypes) == 0 { - return fmt.Errorf("profiles have empty common sample type list") - } - for _, p := range ps { - if err := compatibilizeSampleTypes(p, sTypes); err != nil { - return err - } - } - return nil -} - -// commonSampleTypes returns sample types that appear in all profiles in the -// order how they ordered in the first profile. -func commonSampleTypes(ps []*Profile) []string { - if len(ps) == 0 { - return nil - } - sTypes := map[string]int{} - for _, p := range ps { - for _, st := range p.SampleType { - sTypes[st.Type]++ - } - } - var res []string - for _, st := range ps[0].SampleType { - if sTypes[st.Type] == len(ps) { - res = append(res, st.Type) - } - } - return res -} - -// compatibilizeSampleTypes drops sample types that are not present in sTypes -// list and reorder them if needed. -// -// It sets DefaultSampleType to sType[0] if it is not in sType list. -// -// It assumes that all sample types from the sTypes list are present in the -// given profile otherwise it returns an error. -func compatibilizeSampleTypes(p *Profile, sTypes []string) error { - if len(sTypes) == 0 { - return fmt.Errorf("sample type list is empty") - } - defaultSampleType := sTypes[0] - reMap, needToModify := make([]int, len(sTypes)), false - for i, st := range sTypes { - if st == p.DefaultSampleType { - defaultSampleType = p.DefaultSampleType - } - idx := searchValueType(p.SampleType, st) - if idx < 0 { - return fmt.Errorf("%q sample type is not found in profile", st) - } - reMap[i] = idx - if idx != i { - needToModify = true - } - } - if !needToModify && len(sTypes) == len(p.SampleType) { - return nil - } - p.DefaultSampleType = defaultSampleType - oldSampleTypes := p.SampleType - p.SampleType = make([]*ValueType, len(sTypes)) - for i, idx := range reMap { - p.SampleType[i] = oldSampleTypes[idx] - } - values := make([]int64, len(sTypes)) - for _, s := range p.Sample { - for i, idx := range reMap { - values[i] = s.Value[idx] - } - s.Value = s.Value[:len(values)] - copy(s.Value, values) - } - return nil -} - -func searchValueType(vts []*ValueType, s string) int { - for i, vt := range vts { - if vt.Type == s { - return i - } - } - return -1 -} diff --git a/plugins/traefik/vendor/github.com/google/pprof/profile/profile.go b/plugins/traefik/vendor/github.com/google/pprof/profile/profile.go deleted file mode 100644 index 60ef7e926..000000000 --- a/plugins/traefik/vendor/github.com/google/pprof/profile/profile.go +++ /dev/null @@ -1,856 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package profile provides a representation of profile.proto and -// methods to encode/decode profiles in this format. -package profile - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "math" - "path/filepath" - "regexp" - "sort" - "strings" - "sync" - "time" -) - -// Profile is an in-memory representation of profile.proto. -type Profile struct { - SampleType []*ValueType - DefaultSampleType string - Sample []*Sample - Mapping []*Mapping - Location []*Location - Function []*Function - Comments []string - - DropFrames string - KeepFrames string - - TimeNanos int64 - DurationNanos int64 - PeriodType *ValueType - Period int64 - - // The following fields are modified during encoding and copying, - // so are protected by a Mutex. - encodeMu sync.Mutex - - commentX []int64 - dropFramesX int64 - keepFramesX int64 - stringTable []string - defaultSampleTypeX int64 -} - -// ValueType corresponds to Profile.ValueType -type ValueType struct { - Type string // cpu, wall, inuse_space, etc - Unit string // seconds, nanoseconds, bytes, etc - - typeX int64 - unitX int64 -} - -// Sample corresponds to Profile.Sample -type Sample struct { - Location []*Location - Value []int64 - // Label is a per-label-key map to values for string labels. - // - // In general, having multiple values for the given label key is strongly - // discouraged - see docs for the sample label field in profile.proto. The - // main reason this unlikely state is tracked here is to make the - // decoding->encoding roundtrip not lossy. But we expect that the value - // slices present in this map are always of length 1. - Label map[string][]string - // NumLabel is a per-label-key map to values for numeric labels. See a note - // above on handling multiple values for a label. - NumLabel map[string][]int64 - // NumUnit is a per-label-key map to the unit names of corresponding numeric - // label values. The unit info may be missing even if the label is in - // NumLabel, see the docs in profile.proto for details. When the value is - // slice is present and not nil, its length must be equal to the length of - // the corresponding value slice in NumLabel. - NumUnit map[string][]string - - locationIDX []uint64 - labelX []label -} - -// label corresponds to Profile.Label -type label struct { - keyX int64 - // Exactly one of the two following values must be set - strX int64 - numX int64 // Integer value for this label - // can be set if numX has value - unitX int64 -} - -// Mapping corresponds to Profile.Mapping -type Mapping struct { - ID uint64 - Start uint64 - Limit uint64 - Offset uint64 - File string - BuildID string - HasFunctions bool - HasFilenames bool - HasLineNumbers bool - HasInlineFrames bool - - fileX int64 - buildIDX int64 - - // Name of the kernel relocation symbol ("_text" or "_stext"), extracted from File. - // For linux kernel mappings generated by some tools, correct symbolization depends - // on knowing which of the two possible relocation symbols was used for `Start`. - // This is given to us as a suffix in `File` (e.g. "[kernel.kallsyms]_stext"). - // - // Note, this public field is not persisted in the proto. For the purposes of - // copying / merging / hashing profiles, it is considered subsumed by `File`. - KernelRelocationSymbol string -} - -// Location corresponds to Profile.Location -type Location struct { - ID uint64 - Mapping *Mapping - Address uint64 - Line []Line - IsFolded bool - - mappingIDX uint64 -} - -// Line corresponds to Profile.Line -type Line struct { - Function *Function - Line int64 - - functionIDX uint64 -} - -// Function corresponds to Profile.Function -type Function struct { - ID uint64 - Name string - SystemName string - Filename string - StartLine int64 - - nameX int64 - systemNameX int64 - filenameX int64 -} - -// Parse parses a profile and checks for its validity. The input -// may be a gzip-compressed encoded protobuf or one of many legacy -// profile formats which may be unsupported in the future. -func Parse(r io.Reader) (*Profile, error) { - data, err := io.ReadAll(r) - if err != nil { - return nil, err - } - return ParseData(data) -} - -// ParseData parses a profile from a buffer and checks for its -// validity. -func ParseData(data []byte) (*Profile, error) { - var p *Profile - var err error - if len(data) >= 2 && data[0] == 0x1f && data[1] == 0x8b { - gz, err := gzip.NewReader(bytes.NewBuffer(data)) - if err == nil { - data, err = io.ReadAll(gz) - } - if err != nil { - return nil, fmt.Errorf("decompressing profile: %v", err) - } - } - if p, err = ParseUncompressed(data); err != nil && err != errNoData && err != errConcatProfile { - p, err = parseLegacy(data) - } - - if err != nil { - return nil, fmt.Errorf("parsing profile: %v", err) - } - - if err := p.CheckValid(); err != nil { - return nil, fmt.Errorf("malformed profile: %v", err) - } - return p, nil -} - -var errUnrecognized = fmt.Errorf("unrecognized profile format") -var errMalformed = fmt.Errorf("malformed profile format") -var errNoData = fmt.Errorf("empty input file") -var errConcatProfile = fmt.Errorf("concatenated profiles detected") - -func parseLegacy(data []byte) (*Profile, error) { - parsers := []func([]byte) (*Profile, error){ - parseCPU, - parseHeap, - parseGoCount, // goroutine, threadcreate - parseThread, - parseContention, - parseJavaProfile, - } - - for _, parser := range parsers { - p, err := parser(data) - if err == nil { - p.addLegacyFrameInfo() - return p, nil - } - if err != errUnrecognized { - return nil, err - } - } - return nil, errUnrecognized -} - -// ParseUncompressed parses an uncompressed protobuf into a profile. -func ParseUncompressed(data []byte) (*Profile, error) { - if len(data) == 0 { - return nil, errNoData - } - p := &Profile{} - if err := unmarshal(data, p); err != nil { - return nil, err - } - - if err := p.postDecode(); err != nil { - return nil, err - } - - return p, nil -} - -var libRx = regexp.MustCompile(`([.]so$|[.]so[._][0-9]+)`) - -// massageMappings applies heuristic-based changes to the profile -// mappings to account for quirks of some environments. -func (p *Profile) massageMappings() { - // Merge adjacent regions with matching names, checking that the offsets match - if len(p.Mapping) > 1 { - mappings := []*Mapping{p.Mapping[0]} - for _, m := range p.Mapping[1:] { - lm := mappings[len(mappings)-1] - if adjacent(lm, m) { - lm.Limit = m.Limit - if m.File != "" { - lm.File = m.File - } - if m.BuildID != "" { - lm.BuildID = m.BuildID - } - p.updateLocationMapping(m, lm) - continue - } - mappings = append(mappings, m) - } - p.Mapping = mappings - } - - // Use heuristics to identify main binary and move it to the top of the list of mappings - for i, m := range p.Mapping { - file := strings.TrimSpace(strings.Replace(m.File, "(deleted)", "", -1)) - if len(file) == 0 { - continue - } - if len(libRx.FindStringSubmatch(file)) > 0 { - continue - } - if file[0] == '[' { - continue - } - // Swap what we guess is main to position 0. - p.Mapping[0], p.Mapping[i] = p.Mapping[i], p.Mapping[0] - break - } - - // Keep the mapping IDs neatly sorted - for i, m := range p.Mapping { - m.ID = uint64(i + 1) - } -} - -// adjacent returns whether two mapping entries represent the same -// mapping that has been split into two. Check that their addresses are adjacent, -// and if the offsets match, if they are available. -func adjacent(m1, m2 *Mapping) bool { - if m1.File != "" && m2.File != "" { - if m1.File != m2.File { - return false - } - } - if m1.BuildID != "" && m2.BuildID != "" { - if m1.BuildID != m2.BuildID { - return false - } - } - if m1.Limit != m2.Start { - return false - } - if m1.Offset != 0 && m2.Offset != 0 { - offset := m1.Offset + (m1.Limit - m1.Start) - if offset != m2.Offset { - return false - } - } - return true -} - -func (p *Profile) updateLocationMapping(from, to *Mapping) { - for _, l := range p.Location { - if l.Mapping == from { - l.Mapping = to - } - } -} - -func serialize(p *Profile) []byte { - p.encodeMu.Lock() - p.preEncode() - b := marshal(p) - p.encodeMu.Unlock() - return b -} - -// Write writes the profile as a gzip-compressed marshaled protobuf. -func (p *Profile) Write(w io.Writer) error { - zw := gzip.NewWriter(w) - defer zw.Close() - _, err := zw.Write(serialize(p)) - return err -} - -// WriteUncompressed writes the profile as a marshaled protobuf. -func (p *Profile) WriteUncompressed(w io.Writer) error { - _, err := w.Write(serialize(p)) - return err -} - -// CheckValid tests whether the profile is valid. Checks include, but are -// not limited to: -// - len(Profile.Sample[n].value) == len(Profile.value_unit) -// - Sample.id has a corresponding Profile.Location -func (p *Profile) CheckValid() error { - // Check that sample values are consistent - sampleLen := len(p.SampleType) - if sampleLen == 0 && len(p.Sample) != 0 { - return fmt.Errorf("missing sample type information") - } - for _, s := range p.Sample { - if s == nil { - return fmt.Errorf("profile has nil sample") - } - if len(s.Value) != sampleLen { - return fmt.Errorf("mismatch: sample has %d values vs. %d types", len(s.Value), len(p.SampleType)) - } - for _, l := range s.Location { - if l == nil { - return fmt.Errorf("sample has nil location") - } - } - } - - // Check that all mappings/locations/functions are in the tables - // Check that there are no duplicate ids - mappings := make(map[uint64]*Mapping, len(p.Mapping)) - for _, m := range p.Mapping { - if m == nil { - return fmt.Errorf("profile has nil mapping") - } - if m.ID == 0 { - return fmt.Errorf("found mapping with reserved ID=0") - } - if mappings[m.ID] != nil { - return fmt.Errorf("multiple mappings with same id: %d", m.ID) - } - mappings[m.ID] = m - } - functions := make(map[uint64]*Function, len(p.Function)) - for _, f := range p.Function { - if f == nil { - return fmt.Errorf("profile has nil function") - } - if f.ID == 0 { - return fmt.Errorf("found function with reserved ID=0") - } - if functions[f.ID] != nil { - return fmt.Errorf("multiple functions with same id: %d", f.ID) - } - functions[f.ID] = f - } - locations := make(map[uint64]*Location, len(p.Location)) - for _, l := range p.Location { - if l == nil { - return fmt.Errorf("profile has nil location") - } - if l.ID == 0 { - return fmt.Errorf("found location with reserved id=0") - } - if locations[l.ID] != nil { - return fmt.Errorf("multiple locations with same id: %d", l.ID) - } - locations[l.ID] = l - if m := l.Mapping; m != nil { - if m.ID == 0 || mappings[m.ID] != m { - return fmt.Errorf("inconsistent mapping %p: %d", m, m.ID) - } - } - for _, ln := range l.Line { - f := ln.Function - if f == nil { - return fmt.Errorf("location id: %d has a line with nil function", l.ID) - } - if f.ID == 0 || functions[f.ID] != f { - return fmt.Errorf("inconsistent function %p: %d", f, f.ID) - } - } - } - return nil -} - -// Aggregate merges the locations in the profile into equivalence -// classes preserving the request attributes. It also updates the -// samples to point to the merged locations. -func (p *Profile) Aggregate(inlineFrame, function, filename, linenumber, address bool) error { - for _, m := range p.Mapping { - m.HasInlineFrames = m.HasInlineFrames && inlineFrame - m.HasFunctions = m.HasFunctions && function - m.HasFilenames = m.HasFilenames && filename - m.HasLineNumbers = m.HasLineNumbers && linenumber - } - - // Aggregate functions - if !function || !filename { - for _, f := range p.Function { - if !function { - f.Name = "" - f.SystemName = "" - } - if !filename { - f.Filename = "" - } - } - } - - // Aggregate locations - if !inlineFrame || !address || !linenumber { - for _, l := range p.Location { - if !inlineFrame && len(l.Line) > 1 { - l.Line = l.Line[len(l.Line)-1:] - } - if !linenumber { - for i := range l.Line { - l.Line[i].Line = 0 - } - } - if !address { - l.Address = 0 - } - } - } - - return p.CheckValid() -} - -// NumLabelUnits returns a map of numeric label keys to the units -// associated with those keys and a map of those keys to any units -// that were encountered but not used. -// Unit for a given key is the first encountered unit for that key. If multiple -// units are encountered for values paired with a particular key, then the first -// unit encountered is used and all other units are returned in sorted order -// in map of ignored units. -// If no units are encountered for a particular key, the unit is then inferred -// based on the key. -func (p *Profile) NumLabelUnits() (map[string]string, map[string][]string) { - numLabelUnits := map[string]string{} - ignoredUnits := map[string]map[string]bool{} - encounteredKeys := map[string]bool{} - - // Determine units based on numeric tags for each sample. - for _, s := range p.Sample { - for k := range s.NumLabel { - encounteredKeys[k] = true - for _, unit := range s.NumUnit[k] { - if unit == "" { - continue - } - if wantUnit, ok := numLabelUnits[k]; !ok { - numLabelUnits[k] = unit - } else if wantUnit != unit { - if v, ok := ignoredUnits[k]; ok { - v[unit] = true - } else { - ignoredUnits[k] = map[string]bool{unit: true} - } - } - } - } - } - // Infer units for keys without any units associated with - // numeric tag values. - for key := range encounteredKeys { - unit := numLabelUnits[key] - if unit == "" { - switch key { - case "alignment", "request": - numLabelUnits[key] = "bytes" - default: - numLabelUnits[key] = key - } - } - } - - // Copy ignored units into more readable format - unitsIgnored := make(map[string][]string, len(ignoredUnits)) - for key, values := range ignoredUnits { - units := make([]string, len(values)) - i := 0 - for unit := range values { - units[i] = unit - i++ - } - sort.Strings(units) - unitsIgnored[key] = units - } - - return numLabelUnits, unitsIgnored -} - -// String dumps a text representation of a profile. Intended mainly -// for debugging purposes. -func (p *Profile) String() string { - ss := make([]string, 0, len(p.Comments)+len(p.Sample)+len(p.Mapping)+len(p.Location)) - for _, c := range p.Comments { - ss = append(ss, "Comment: "+c) - } - if pt := p.PeriodType; pt != nil { - ss = append(ss, fmt.Sprintf("PeriodType: %s %s", pt.Type, pt.Unit)) - } - ss = append(ss, fmt.Sprintf("Period: %d", p.Period)) - if p.TimeNanos != 0 { - ss = append(ss, fmt.Sprintf("Time: %v", time.Unix(0, p.TimeNanos))) - } - if p.DurationNanos != 0 { - ss = append(ss, fmt.Sprintf("Duration: %.4v", time.Duration(p.DurationNanos))) - } - - ss = append(ss, "Samples:") - var sh1 string - for _, s := range p.SampleType { - dflt := "" - if s.Type == p.DefaultSampleType { - dflt = "[dflt]" - } - sh1 = sh1 + fmt.Sprintf("%s/%s%s ", s.Type, s.Unit, dflt) - } - ss = append(ss, strings.TrimSpace(sh1)) - for _, s := range p.Sample { - ss = append(ss, s.string()) - } - - ss = append(ss, "Locations") - for _, l := range p.Location { - ss = append(ss, l.string()) - } - - ss = append(ss, "Mappings") - for _, m := range p.Mapping { - ss = append(ss, m.string()) - } - - return strings.Join(ss, "\n") + "\n" -} - -// string dumps a text representation of a mapping. Intended mainly -// for debugging purposes. -func (m *Mapping) string() string { - bits := "" - if m.HasFunctions { - bits = bits + "[FN]" - } - if m.HasFilenames { - bits = bits + "[FL]" - } - if m.HasLineNumbers { - bits = bits + "[LN]" - } - if m.HasInlineFrames { - bits = bits + "[IN]" - } - return fmt.Sprintf("%d: %#x/%#x/%#x %s %s %s", - m.ID, - m.Start, m.Limit, m.Offset, - m.File, - m.BuildID, - bits) -} - -// string dumps a text representation of a location. Intended mainly -// for debugging purposes. -func (l *Location) string() string { - ss := []string{} - locStr := fmt.Sprintf("%6d: %#x ", l.ID, l.Address) - if m := l.Mapping; m != nil { - locStr = locStr + fmt.Sprintf("M=%d ", m.ID) - } - if l.IsFolded { - locStr = locStr + "[F] " - } - if len(l.Line) == 0 { - ss = append(ss, locStr) - } - for li := range l.Line { - lnStr := "??" - if fn := l.Line[li].Function; fn != nil { - lnStr = fmt.Sprintf("%s %s:%d s=%d", - fn.Name, - fn.Filename, - l.Line[li].Line, - fn.StartLine) - if fn.Name != fn.SystemName { - lnStr = lnStr + "(" + fn.SystemName + ")" - } - } - ss = append(ss, locStr+lnStr) - // Do not print location details past the first line - locStr = " " - } - return strings.Join(ss, "\n") -} - -// string dumps a text representation of a sample. Intended mainly -// for debugging purposes. -func (s *Sample) string() string { - ss := []string{} - var sv string - for _, v := range s.Value { - sv = fmt.Sprintf("%s %10d", sv, v) - } - sv = sv + ": " - for _, l := range s.Location { - sv = sv + fmt.Sprintf("%d ", l.ID) - } - ss = append(ss, sv) - const labelHeader = " " - if len(s.Label) > 0 { - ss = append(ss, labelHeader+labelsToString(s.Label)) - } - if len(s.NumLabel) > 0 { - ss = append(ss, labelHeader+numLabelsToString(s.NumLabel, s.NumUnit)) - } - return strings.Join(ss, "\n") -} - -// labelsToString returns a string representation of a -// map representing labels. -func labelsToString(labels map[string][]string) string { - ls := []string{} - for k, v := range labels { - ls = append(ls, fmt.Sprintf("%s:%v", k, v)) - } - sort.Strings(ls) - return strings.Join(ls, " ") -} - -// numLabelsToString returns a string representation of a map -// representing numeric labels. -func numLabelsToString(numLabels map[string][]int64, numUnits map[string][]string) string { - ls := []string{} - for k, v := range numLabels { - units := numUnits[k] - var labelString string - if len(units) == len(v) { - values := make([]string, len(v)) - for i, vv := range v { - values[i] = fmt.Sprintf("%d %s", vv, units[i]) - } - labelString = fmt.Sprintf("%s:%v", k, values) - } else { - labelString = fmt.Sprintf("%s:%v", k, v) - } - ls = append(ls, labelString) - } - sort.Strings(ls) - return strings.Join(ls, " ") -} - -// SetLabel sets the specified key to the specified value for all samples in the -// profile. -func (p *Profile) SetLabel(key string, value []string) { - for _, sample := range p.Sample { - if sample.Label == nil { - sample.Label = map[string][]string{key: value} - } else { - sample.Label[key] = value - } - } -} - -// RemoveLabel removes all labels associated with the specified key for all -// samples in the profile. -func (p *Profile) RemoveLabel(key string) { - for _, sample := range p.Sample { - delete(sample.Label, key) - } -} - -// HasLabel returns true if a sample has a label with indicated key and value. -func (s *Sample) HasLabel(key, value string) bool { - for _, v := range s.Label[key] { - if v == value { - return true - } - } - return false -} - -// SetNumLabel sets the specified key to the specified value for all samples in the -// profile. "unit" is a slice that describes the units that each corresponding member -// of "values" is measured in (e.g. bytes or seconds). If there is no relevant -// unit for a given value, that member of "unit" should be the empty string. -// "unit" must either have the same length as "value", or be nil. -func (p *Profile) SetNumLabel(key string, value []int64, unit []string) { - for _, sample := range p.Sample { - if sample.NumLabel == nil { - sample.NumLabel = map[string][]int64{key: value} - } else { - sample.NumLabel[key] = value - } - if sample.NumUnit == nil { - sample.NumUnit = map[string][]string{key: unit} - } else { - sample.NumUnit[key] = unit - } - } -} - -// RemoveNumLabel removes all numerical labels associated with the specified key for all -// samples in the profile. -func (p *Profile) RemoveNumLabel(key string) { - for _, sample := range p.Sample { - delete(sample.NumLabel, key) - delete(sample.NumUnit, key) - } -} - -// DiffBaseSample returns true if a sample belongs to the diff base and false -// otherwise. -func (s *Sample) DiffBaseSample() bool { - return s.HasLabel("pprof::base", "true") -} - -// Scale multiplies all sample values in a profile by a constant and keeps -// only samples that have at least one non-zero value. -func (p *Profile) Scale(ratio float64) { - if ratio == 1 { - return - } - ratios := make([]float64, len(p.SampleType)) - for i := range p.SampleType { - ratios[i] = ratio - } - p.ScaleN(ratios) -} - -// ScaleN multiplies each sample values in a sample by a different amount -// and keeps only samples that have at least one non-zero value. -func (p *Profile) ScaleN(ratios []float64) error { - if len(p.SampleType) != len(ratios) { - return fmt.Errorf("mismatched scale ratios, got %d, want %d", len(ratios), len(p.SampleType)) - } - allOnes := true - for _, r := range ratios { - if r != 1 { - allOnes = false - break - } - } - if allOnes { - return nil - } - fillIdx := 0 - for _, s := range p.Sample { - keepSample := false - for i, v := range s.Value { - if ratios[i] != 1 { - val := int64(math.Round(float64(v) * ratios[i])) - s.Value[i] = val - keepSample = keepSample || val != 0 - } - } - if keepSample { - p.Sample[fillIdx] = s - fillIdx++ - } - } - p.Sample = p.Sample[:fillIdx] - return nil -} - -// HasFunctions determines if all locations in this profile have -// symbolized function information. -func (p *Profile) HasFunctions() bool { - for _, l := range p.Location { - if l.Mapping != nil && !l.Mapping.HasFunctions { - return false - } - } - return true -} - -// HasFileLines determines if all locations in this profile have -// symbolized file and line number information. -func (p *Profile) HasFileLines() bool { - for _, l := range p.Location { - if l.Mapping != nil && (!l.Mapping.HasFilenames || !l.Mapping.HasLineNumbers) { - return false - } - } - return true -} - -// Unsymbolizable returns true if a mapping points to a binary for which -// locations can't be symbolized in principle, at least now. Examples are -// "[vdso]", [vsyscall]" and some others, see the code. -func (m *Mapping) Unsymbolizable() bool { - name := filepath.Base(m.File) - return strings.HasPrefix(name, "[") || strings.HasPrefix(name, "linux-vdso") || strings.HasPrefix(m.File, "/dev/dri/") -} - -// Copy makes a fully independent copy of a profile. -func (p *Profile) Copy() *Profile { - pp := &Profile{} - if err := unmarshal(serialize(p), pp); err != nil { - panic(err) - } - if err := pp.postDecode(); err != nil { - panic(err) - } - - return pp -} diff --git a/plugins/traefik/vendor/github.com/google/pprof/profile/proto.go b/plugins/traefik/vendor/github.com/google/pprof/profile/proto.go deleted file mode 100644 index a15696ba1..000000000 --- a/plugins/traefik/vendor/github.com/google/pprof/profile/proto.go +++ /dev/null @@ -1,367 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This file is a simple protocol buffer encoder and decoder. -// The format is described at -// https://developers.google.com/protocol-buffers/docs/encoding -// -// A protocol message must implement the message interface: -// decoder() []decoder -// encode(*buffer) -// -// The decode method returns a slice indexed by field number that gives the -// function to decode that field. -// The encode method encodes its receiver into the given buffer. -// -// The two methods are simple enough to be implemented by hand rather than -// by using a protocol compiler. -// -// See profile.go for examples of messages implementing this interface. -// -// There is no support for groups, message sets, or "has" bits. - -package profile - -import ( - "errors" - "fmt" -) - -type buffer struct { - field int // field tag - typ int // proto wire type code for field - u64 uint64 - data []byte - tmp [16]byte - tmpLines []Line // temporary storage used while decoding "repeated Line". -} - -type decoder func(*buffer, message) error - -type message interface { - decoder() []decoder - encode(*buffer) -} - -func marshal(m message) []byte { - var b buffer - m.encode(&b) - return b.data -} - -func encodeVarint(b *buffer, x uint64) { - for x >= 128 { - b.data = append(b.data, byte(x)|0x80) - x >>= 7 - } - b.data = append(b.data, byte(x)) -} - -func encodeLength(b *buffer, tag int, len int) { - encodeVarint(b, uint64(tag)<<3|2) - encodeVarint(b, uint64(len)) -} - -func encodeUint64(b *buffer, tag int, x uint64) { - // append varint to b.data - encodeVarint(b, uint64(tag)<<3) - encodeVarint(b, x) -} - -func encodeUint64s(b *buffer, tag int, x []uint64) { - if len(x) > 2 { - // Use packed encoding - n1 := len(b.data) - for _, u := range x { - encodeVarint(b, u) - } - n2 := len(b.data) - encodeLength(b, tag, n2-n1) - n3 := len(b.data) - copy(b.tmp[:], b.data[n2:n3]) - copy(b.data[n1+(n3-n2):], b.data[n1:n2]) - copy(b.data[n1:], b.tmp[:n3-n2]) - return - } - for _, u := range x { - encodeUint64(b, tag, u) - } -} - -func encodeUint64Opt(b *buffer, tag int, x uint64) { - if x == 0 { - return - } - encodeUint64(b, tag, x) -} - -func encodeInt64(b *buffer, tag int, x int64) { - u := uint64(x) - encodeUint64(b, tag, u) -} - -func encodeInt64s(b *buffer, tag int, x []int64) { - if len(x) > 2 { - // Use packed encoding - n1 := len(b.data) - for _, u := range x { - encodeVarint(b, uint64(u)) - } - n2 := len(b.data) - encodeLength(b, tag, n2-n1) - n3 := len(b.data) - copy(b.tmp[:], b.data[n2:n3]) - copy(b.data[n1+(n3-n2):], b.data[n1:n2]) - copy(b.data[n1:], b.tmp[:n3-n2]) - return - } - for _, u := range x { - encodeInt64(b, tag, u) - } -} - -func encodeInt64Opt(b *buffer, tag int, x int64) { - if x == 0 { - return - } - encodeInt64(b, tag, x) -} - -func encodeString(b *buffer, tag int, x string) { - encodeLength(b, tag, len(x)) - b.data = append(b.data, x...) -} - -func encodeStrings(b *buffer, tag int, x []string) { - for _, s := range x { - encodeString(b, tag, s) - } -} - -func encodeBool(b *buffer, tag int, x bool) { - if x { - encodeUint64(b, tag, 1) - } else { - encodeUint64(b, tag, 0) - } -} - -func encodeBoolOpt(b *buffer, tag int, x bool) { - if x { - encodeBool(b, tag, x) - } -} - -func encodeMessage(b *buffer, tag int, m message) { - n1 := len(b.data) - m.encode(b) - n2 := len(b.data) - encodeLength(b, tag, n2-n1) - n3 := len(b.data) - copy(b.tmp[:], b.data[n2:n3]) - copy(b.data[n1+(n3-n2):], b.data[n1:n2]) - copy(b.data[n1:], b.tmp[:n3-n2]) -} - -func unmarshal(data []byte, m message) (err error) { - b := buffer{data: data, typ: 2} - return decodeMessage(&b, m) -} - -func le64(p []byte) uint64 { - return uint64(p[0]) | uint64(p[1])<<8 | uint64(p[2])<<16 | uint64(p[3])<<24 | uint64(p[4])<<32 | uint64(p[5])<<40 | uint64(p[6])<<48 | uint64(p[7])<<56 -} - -func le32(p []byte) uint32 { - return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 -} - -func decodeVarint(data []byte) (uint64, []byte, error) { - var u uint64 - for i := 0; ; i++ { - if i >= 10 || i >= len(data) { - return 0, nil, errors.New("bad varint") - } - u |= uint64(data[i]&0x7F) << uint(7*i) - if data[i]&0x80 == 0 { - return u, data[i+1:], nil - } - } -} - -func decodeField(b *buffer, data []byte) ([]byte, error) { - x, data, err := decodeVarint(data) - if err != nil { - return nil, err - } - b.field = int(x >> 3) - b.typ = int(x & 7) - b.data = nil - b.u64 = 0 - switch b.typ { - case 0: - b.u64, data, err = decodeVarint(data) - if err != nil { - return nil, err - } - case 1: - if len(data) < 8 { - return nil, errors.New("not enough data") - } - b.u64 = le64(data[:8]) - data = data[8:] - case 2: - var n uint64 - n, data, err = decodeVarint(data) - if err != nil { - return nil, err - } - if n > uint64(len(data)) { - return nil, errors.New("too much data") - } - b.data = data[:n] - data = data[n:] - case 5: - if len(data) < 4 { - return nil, errors.New("not enough data") - } - b.u64 = uint64(le32(data[:4])) - data = data[4:] - default: - return nil, fmt.Errorf("unknown wire type: %d", b.typ) - } - - return data, nil -} - -func checkType(b *buffer, typ int) error { - if b.typ != typ { - return errors.New("type mismatch") - } - return nil -} - -func decodeMessage(b *buffer, m message) error { - if err := checkType(b, 2); err != nil { - return err - } - dec := m.decoder() - data := b.data - for len(data) > 0 { - // pull varint field# + type - var err error - data, err = decodeField(b, data) - if err != nil { - return err - } - if b.field >= len(dec) || dec[b.field] == nil { - continue - } - if err := dec[b.field](b, m); err != nil { - return err - } - } - return nil -} - -func decodeInt64(b *buffer, x *int64) error { - if err := checkType(b, 0); err != nil { - return err - } - *x = int64(b.u64) - return nil -} - -func decodeInt64s(b *buffer, x *[]int64) error { - if b.typ == 2 { - // Packed encoding - data := b.data - for len(data) > 0 { - var u uint64 - var err error - - if u, data, err = decodeVarint(data); err != nil { - return err - } - *x = append(*x, int64(u)) - } - return nil - } - var i int64 - if err := decodeInt64(b, &i); err != nil { - return err - } - *x = append(*x, i) - return nil -} - -func decodeUint64(b *buffer, x *uint64) error { - if err := checkType(b, 0); err != nil { - return err - } - *x = b.u64 - return nil -} - -func decodeUint64s(b *buffer, x *[]uint64) error { - if b.typ == 2 { - data := b.data - // Packed encoding - for len(data) > 0 { - var u uint64 - var err error - - if u, data, err = decodeVarint(data); err != nil { - return err - } - *x = append(*x, u) - } - return nil - } - var u uint64 - if err := decodeUint64(b, &u); err != nil { - return err - } - *x = append(*x, u) - return nil -} - -func decodeString(b *buffer, x *string) error { - if err := checkType(b, 2); err != nil { - return err - } - *x = string(b.data) - return nil -} - -func decodeStrings(b *buffer, x *[]string) error { - var s string - if err := decodeString(b, &s); err != nil { - return err - } - *x = append(*x, s) - return nil -} - -func decodeBool(b *buffer, x *bool) error { - if err := checkType(b, 0); err != nil { - return err - } - if int64(b.u64) == 0 { - *x = false - } else { - *x = true - } - return nil -} diff --git a/plugins/traefik/vendor/github.com/google/pprof/profile/prune.go b/plugins/traefik/vendor/github.com/google/pprof/profile/prune.go deleted file mode 100644 index b2f9fd546..000000000 --- a/plugins/traefik/vendor/github.com/google/pprof/profile/prune.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Implements methods to remove frames from profiles. - -package profile - -import ( - "fmt" - "regexp" - "strings" -) - -var ( - reservedNames = []string{"(anonymous namespace)", "operator()"} - bracketRx = func() *regexp.Regexp { - var quotedNames []string - for _, name := range append(reservedNames, "(") { - quotedNames = append(quotedNames, regexp.QuoteMeta(name)) - } - return regexp.MustCompile(strings.Join(quotedNames, "|")) - }() -) - -// simplifyFunc does some primitive simplification of function names. -func simplifyFunc(f string) string { - // Account for leading '.' on the PPC ELF v1 ABI. - funcName := strings.TrimPrefix(f, ".") - // Account for unsimplified names -- try to remove the argument list by trimming - // starting from the first '(', but skipping reserved names that have '('. - for _, ind := range bracketRx.FindAllStringSubmatchIndex(funcName, -1) { - foundReserved := false - for _, res := range reservedNames { - if funcName[ind[0]:ind[1]] == res { - foundReserved = true - break - } - } - if !foundReserved { - funcName = funcName[:ind[0]] - break - } - } - return funcName -} - -// Prune removes all nodes beneath a node matching dropRx, and not -// matching keepRx. If the root node of a Sample matches, the sample -// will have an empty stack. -func (p *Profile) Prune(dropRx, keepRx *regexp.Regexp) { - prune := make(map[uint64]bool) - pruneBeneath := make(map[uint64]bool) - - // simplifyFunc can be expensive, so cache results. - // Note that the same function name can be encountered many times due - // different lines and addresses in the same function. - pruneCache := map[string]bool{} // Map from function to whether or not to prune - pruneFromHere := func(s string) bool { - if r, ok := pruneCache[s]; ok { - return r - } - funcName := simplifyFunc(s) - if dropRx.MatchString(funcName) { - if keepRx == nil || !keepRx.MatchString(funcName) { - pruneCache[s] = true - return true - } - } - pruneCache[s] = false - return false - } - - for _, loc := range p.Location { - var i int - for i = len(loc.Line) - 1; i >= 0; i-- { - if fn := loc.Line[i].Function; fn != nil && fn.Name != "" { - if pruneFromHere(fn.Name) { - break - } - } - } - - if i >= 0 { - // Found matching entry to prune. - pruneBeneath[loc.ID] = true - - // Remove the matching location. - if i == len(loc.Line)-1 { - // Matched the top entry: prune the whole location. - prune[loc.ID] = true - } else { - loc.Line = loc.Line[i+1:] - } - } - } - - // Prune locs from each Sample - for _, sample := range p.Sample { - // Scan from the root to the leaves to find the prune location. - // Do not prune frames before the first user frame, to avoid - // pruning everything. - foundUser := false - for i := len(sample.Location) - 1; i >= 0; i-- { - id := sample.Location[i].ID - if !prune[id] && !pruneBeneath[id] { - foundUser = true - continue - } - if !foundUser { - continue - } - if prune[id] { - sample.Location = sample.Location[i+1:] - break - } - if pruneBeneath[id] { - sample.Location = sample.Location[i:] - break - } - } - } -} - -// RemoveUninteresting prunes and elides profiles using built-in -// tables of uninteresting function names. -func (p *Profile) RemoveUninteresting() error { - var keep, drop *regexp.Regexp - var err error - - if p.DropFrames != "" { - if drop, err = regexp.Compile("^(" + p.DropFrames + ")$"); err != nil { - return fmt.Errorf("failed to compile regexp %s: %v", p.DropFrames, err) - } - if p.KeepFrames != "" { - if keep, err = regexp.Compile("^(" + p.KeepFrames + ")$"); err != nil { - return fmt.Errorf("failed to compile regexp %s: %v", p.KeepFrames, err) - } - } - p.Prune(drop, keep) - } - return nil -} - -// PruneFrom removes all nodes beneath the lowest node matching dropRx, not including itself. -// -// Please see the example below to understand this method as well as -// the difference from Prune method. -// -// A sample contains Location of [A,B,C,B,D] where D is the top frame and there's no inline. -// -// PruneFrom(A) returns [A,B,C,B,D] because there's no node beneath A. -// Prune(A, nil) returns [B,C,B,D] by removing A itself. -// -// PruneFrom(B) returns [B,C,B,D] by removing all nodes beneath the first B when scanning from the bottom. -// Prune(B, nil) returns [D] because a matching node is found by scanning from the root. -func (p *Profile) PruneFrom(dropRx *regexp.Regexp) { - pruneBeneath := make(map[uint64]bool) - - for _, loc := range p.Location { - for i := 0; i < len(loc.Line); i++ { - if fn := loc.Line[i].Function; fn != nil && fn.Name != "" { - funcName := simplifyFunc(fn.Name) - if dropRx.MatchString(funcName) { - // Found matching entry to prune. - pruneBeneath[loc.ID] = true - loc.Line = loc.Line[i:] - break - } - } - } - } - - // Prune locs from each Sample - for _, sample := range p.Sample { - // Scan from the bottom leaf to the root to find the prune location. - for i, loc := range sample.Location { - if pruneBeneath[loc.ID] { - sample.Location = sample.Location[i:] - break - } - } - } -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/LICENSE b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/LICENSE deleted file mode 100644 index 9415ee72c..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2013-2014 Onsi Fakhouri - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/config/deprecated.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/config/deprecated.go deleted file mode 100644 index a61021d08..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/config/deprecated.go +++ /dev/null @@ -1,69 +0,0 @@ -package config - -// GinkgoConfigType has been deprecated and its equivalent now lives in -// the types package. You can no longer access Ginkgo configuration from the config -// package. Instead use the DSL's GinkgoConfiguration() function to get copies of the -// current configuration -// -// GinkgoConfigType is still here so custom V1 reporters do not result in a compilation error -// It will be removed in a future minor release of Ginkgo -type GinkgoConfigType = DeprecatedGinkgoConfigType -type DeprecatedGinkgoConfigType struct { - RandomSeed int64 - RandomizeAllSpecs bool - RegexScansFilePath bool - FocusStrings []string - SkipStrings []string - SkipMeasurements bool - FailOnPending bool - FailFast bool - FlakeAttempts int - EmitSpecProgress bool - DryRun bool - DebugParallel bool - - ParallelNode int - ParallelTotal int - SyncHost string - StreamHost string -} - -// DefaultReporterConfigType has been deprecated and its equivalent now lives in -// the types package. You can no longer access Ginkgo configuration from the config -// package. Instead use the DSL's GinkgoConfiguration() function to get copies of the -// current configuration -// -// DefaultReporterConfigType is still here so custom V1 reporters do not result in a compilation error -// It will be removed in a future minor release of Ginkgo -type DefaultReporterConfigType = DeprecatedDefaultReporterConfigType -type DeprecatedDefaultReporterConfigType struct { - NoColor bool - SlowSpecThreshold float64 - NoisyPendings bool - NoisySkippings bool - Succinct bool - Verbose bool - FullTrace bool - ReportPassed bool - ReportFile string -} - -// Sadly there is no way to gracefully deprecate access to these global config variables. -// Users who need access to Ginkgo's configuration should use the DSL's GinkgoConfiguration() method -// These new unwieldy type names exist to give users a hint when they try to compile and the compilation fails -type GinkgoConfigIsNoLongerAccessibleFromTheConfigPackageUseTheDSLsGinkgoConfigurationFunctionInstead struct{} - -// Sadly there is no way to gracefully deprecate access to these global config variables. -// Users who need access to Ginkgo's configuration should use the DSL's GinkgoConfiguration() method -// These new unwieldy type names exist to give users a hint when they try to compile and the compilation fails -var GinkgoConfig = GinkgoConfigIsNoLongerAccessibleFromTheConfigPackageUseTheDSLsGinkgoConfigurationFunctionInstead{} - -// Sadly there is no way to gracefully deprecate access to these global config variables. -// Users who need access to Ginkgo's configuration should use the DSL's GinkgoConfiguration() method -// These new unwieldy type names exist to give users a hint when they try to compile and the compilation fails -type DefaultReporterConfigIsNoLongerAccessibleFromTheConfigPackageUseTheDSLsGinkgoConfigurationFunctionInstead struct{} - -// Sadly there is no way to gracefully deprecate access to these global config variables. -// Users who need access to Ginkgo's configuration should use the DSL's GinkgoConfiguration() method -// These new unwieldy type names exist to give users a hint when they try to compile and the compilation fails -var DefaultReporterConfig = DefaultReporterConfigIsNoLongerAccessibleFromTheConfigPackageUseTheDSLsGinkgoConfigurationFunctionInstead{} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/formatter/colorable_others.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/formatter/colorable_others.go deleted file mode 100644 index 778bfd7c7..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/formatter/colorable_others.go +++ /dev/null @@ -1,41 +0,0 @@ -// +build !windows - -/* -These packages are used for colorize on Windows and contributed by mattn.jp@gmail.com - - * go-colorable: - * go-isatty: - -The MIT License (MIT) - -Copyright (c) 2016 Yasuhiro Matsumoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package formatter - -import ( - "io" - "os" -) - -func newColorable(file *os.File) io.Writer { - return file -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/formatter/colorable_windows.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/formatter/colorable_windows.go deleted file mode 100644 index dd1d143cc..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/formatter/colorable_windows.go +++ /dev/null @@ -1,809 +0,0 @@ -/* -These packages are used for colorize on Windows and contributed by mattn.jp@gmail.com - - * go-colorable: - * go-isatty: - -The MIT License (MIT) - -Copyright (c) 2016 Yasuhiro Matsumoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -package formatter - -import ( - "bytes" - "fmt" - "io" - "math" - "os" - "strconv" - "strings" - "syscall" - "unsafe" -) - -var ( - kernel32 = syscall.NewLazyDLL("kernel32.dll") - procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") - procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") - procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") - procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") - procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") - procGetConsoleMode = kernel32.NewProc("GetConsoleMode") -) - -func isTerminal(fd uintptr) bool { - var st uint32 - r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) - return r != 0 && e == 0 -} - -const ( - foregroundBlue = 0x1 - foregroundGreen = 0x2 - foregroundRed = 0x4 - foregroundIntensity = 0x8 - foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) - backgroundBlue = 0x10 - backgroundGreen = 0x20 - backgroundRed = 0x40 - backgroundIntensity = 0x80 - backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) -) - -type wchar uint16 -type short int16 -type dword uint32 -type word uint16 - -type coord struct { - x short - y short -} - -type smallRect struct { - left short - top short - right short - bottom short -} - -type consoleScreenBufferInfo struct { - size coord - cursorPosition coord - attributes word - window smallRect - maximumWindowSize coord -} - -type writer struct { - out io.Writer - handle syscall.Handle - lastbuf bytes.Buffer - oldattr word -} - -func newColorable(file *os.File) io.Writer { - if file == nil { - panic("nil passed instead of *os.File to NewColorable()") - } - - if isTerminal(file.Fd()) { - var csbi consoleScreenBufferInfo - handle := syscall.Handle(file.Fd()) - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - return &writer{out: file, handle: handle, oldattr: csbi.attributes} - } else { - return file - } -} - -var color256 = map[int]int{ - 0: 0x000000, - 1: 0x800000, - 2: 0x008000, - 3: 0x808000, - 4: 0x000080, - 5: 0x800080, - 6: 0x008080, - 7: 0xc0c0c0, - 8: 0x808080, - 9: 0xff0000, - 10: 0x00ff00, - 11: 0xffff00, - 12: 0x0000ff, - 13: 0xff00ff, - 14: 0x00ffff, - 15: 0xffffff, - 16: 0x000000, - 17: 0x00005f, - 18: 0x000087, - 19: 0x0000af, - 20: 0x0000d7, - 21: 0x0000ff, - 22: 0x005f00, - 23: 0x005f5f, - 24: 0x005f87, - 25: 0x005faf, - 26: 0x005fd7, - 27: 0x005fff, - 28: 0x008700, - 29: 0x00875f, - 30: 0x008787, - 31: 0x0087af, - 32: 0x0087d7, - 33: 0x0087ff, - 34: 0x00af00, - 35: 0x00af5f, - 36: 0x00af87, - 37: 0x00afaf, - 38: 0x00afd7, - 39: 0x00afff, - 40: 0x00d700, - 41: 0x00d75f, - 42: 0x00d787, - 43: 0x00d7af, - 44: 0x00d7d7, - 45: 0x00d7ff, - 46: 0x00ff00, - 47: 0x00ff5f, - 48: 0x00ff87, - 49: 0x00ffaf, - 50: 0x00ffd7, - 51: 0x00ffff, - 52: 0x5f0000, - 53: 0x5f005f, - 54: 0x5f0087, - 55: 0x5f00af, - 56: 0x5f00d7, - 57: 0x5f00ff, - 58: 0x5f5f00, - 59: 0x5f5f5f, - 60: 0x5f5f87, - 61: 0x5f5faf, - 62: 0x5f5fd7, - 63: 0x5f5fff, - 64: 0x5f8700, - 65: 0x5f875f, - 66: 0x5f8787, - 67: 0x5f87af, - 68: 0x5f87d7, - 69: 0x5f87ff, - 70: 0x5faf00, - 71: 0x5faf5f, - 72: 0x5faf87, - 73: 0x5fafaf, - 74: 0x5fafd7, - 75: 0x5fafff, - 76: 0x5fd700, - 77: 0x5fd75f, - 78: 0x5fd787, - 79: 0x5fd7af, - 80: 0x5fd7d7, - 81: 0x5fd7ff, - 82: 0x5fff00, - 83: 0x5fff5f, - 84: 0x5fff87, - 85: 0x5fffaf, - 86: 0x5fffd7, - 87: 0x5fffff, - 88: 0x870000, - 89: 0x87005f, - 90: 0x870087, - 91: 0x8700af, - 92: 0x8700d7, - 93: 0x8700ff, - 94: 0x875f00, - 95: 0x875f5f, - 96: 0x875f87, - 97: 0x875faf, - 98: 0x875fd7, - 99: 0x875fff, - 100: 0x878700, - 101: 0x87875f, - 102: 0x878787, - 103: 0x8787af, - 104: 0x8787d7, - 105: 0x8787ff, - 106: 0x87af00, - 107: 0x87af5f, - 108: 0x87af87, - 109: 0x87afaf, - 110: 0x87afd7, - 111: 0x87afff, - 112: 0x87d700, - 113: 0x87d75f, - 114: 0x87d787, - 115: 0x87d7af, - 116: 0x87d7d7, - 117: 0x87d7ff, - 118: 0x87ff00, - 119: 0x87ff5f, - 120: 0x87ff87, - 121: 0x87ffaf, - 122: 0x87ffd7, - 123: 0x87ffff, - 124: 0xaf0000, - 125: 0xaf005f, - 126: 0xaf0087, - 127: 0xaf00af, - 128: 0xaf00d7, - 129: 0xaf00ff, - 130: 0xaf5f00, - 131: 0xaf5f5f, - 132: 0xaf5f87, - 133: 0xaf5faf, - 134: 0xaf5fd7, - 135: 0xaf5fff, - 136: 0xaf8700, - 137: 0xaf875f, - 138: 0xaf8787, - 139: 0xaf87af, - 140: 0xaf87d7, - 141: 0xaf87ff, - 142: 0xafaf00, - 143: 0xafaf5f, - 144: 0xafaf87, - 145: 0xafafaf, - 146: 0xafafd7, - 147: 0xafafff, - 148: 0xafd700, - 149: 0xafd75f, - 150: 0xafd787, - 151: 0xafd7af, - 152: 0xafd7d7, - 153: 0xafd7ff, - 154: 0xafff00, - 155: 0xafff5f, - 156: 0xafff87, - 157: 0xafffaf, - 158: 0xafffd7, - 159: 0xafffff, - 160: 0xd70000, - 161: 0xd7005f, - 162: 0xd70087, - 163: 0xd700af, - 164: 0xd700d7, - 165: 0xd700ff, - 166: 0xd75f00, - 167: 0xd75f5f, - 168: 0xd75f87, - 169: 0xd75faf, - 170: 0xd75fd7, - 171: 0xd75fff, - 172: 0xd78700, - 173: 0xd7875f, - 174: 0xd78787, - 175: 0xd787af, - 176: 0xd787d7, - 177: 0xd787ff, - 178: 0xd7af00, - 179: 0xd7af5f, - 180: 0xd7af87, - 181: 0xd7afaf, - 182: 0xd7afd7, - 183: 0xd7afff, - 184: 0xd7d700, - 185: 0xd7d75f, - 186: 0xd7d787, - 187: 0xd7d7af, - 188: 0xd7d7d7, - 189: 0xd7d7ff, - 190: 0xd7ff00, - 191: 0xd7ff5f, - 192: 0xd7ff87, - 193: 0xd7ffaf, - 194: 0xd7ffd7, - 195: 0xd7ffff, - 196: 0xff0000, - 197: 0xff005f, - 198: 0xff0087, - 199: 0xff00af, - 200: 0xff00d7, - 201: 0xff00ff, - 202: 0xff5f00, - 203: 0xff5f5f, - 204: 0xff5f87, - 205: 0xff5faf, - 206: 0xff5fd7, - 207: 0xff5fff, - 208: 0xff8700, - 209: 0xff875f, - 210: 0xff8787, - 211: 0xff87af, - 212: 0xff87d7, - 213: 0xff87ff, - 214: 0xffaf00, - 215: 0xffaf5f, - 216: 0xffaf87, - 217: 0xffafaf, - 218: 0xffafd7, - 219: 0xffafff, - 220: 0xffd700, - 221: 0xffd75f, - 222: 0xffd787, - 223: 0xffd7af, - 224: 0xffd7d7, - 225: 0xffd7ff, - 226: 0xffff00, - 227: 0xffff5f, - 228: 0xffff87, - 229: 0xffffaf, - 230: 0xffffd7, - 231: 0xffffff, - 232: 0x080808, - 233: 0x121212, - 234: 0x1c1c1c, - 235: 0x262626, - 236: 0x303030, - 237: 0x3a3a3a, - 238: 0x444444, - 239: 0x4e4e4e, - 240: 0x585858, - 241: 0x626262, - 242: 0x6c6c6c, - 243: 0x767676, - 244: 0x808080, - 245: 0x8a8a8a, - 246: 0x949494, - 247: 0x9e9e9e, - 248: 0xa8a8a8, - 249: 0xb2b2b2, - 250: 0xbcbcbc, - 251: 0xc6c6c6, - 252: 0xd0d0d0, - 253: 0xdadada, - 254: 0xe4e4e4, - 255: 0xeeeeee, -} - -func (w *writer) Write(data []byte) (n int, err error) { - var csbi consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) - - er := bytes.NewBuffer(data) -loop: - for { - r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) - if r1 == 0 { - break loop - } - - c1, _, err := er.ReadRune() - if err != nil { - break loop - } - if c1 != 0x1b { - fmt.Fprint(w.out, string(c1)) - continue - } - c2, _, err := er.ReadRune() - if err != nil { - w.lastbuf.WriteRune(c1) - break loop - } - if c2 != 0x5b { - w.lastbuf.WriteRune(c1) - w.lastbuf.WriteRune(c2) - continue - } - - var buf bytes.Buffer - var m rune - for { - c, _, err := er.ReadRune() - if err != nil { - w.lastbuf.WriteRune(c1) - w.lastbuf.WriteRune(c2) - w.lastbuf.Write(buf.Bytes()) - break loop - } - if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { - m = c - break - } - buf.Write([]byte(string(c))) - } - - var csbi consoleScreenBufferInfo - switch m { - case 'A': - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.y -= short(n) - procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'B': - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.y += short(n) - procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'C': - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x -= short(n) - procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'D': - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - if n, err = strconv.Atoi(buf.String()); err == nil { - var csbi consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x += short(n) - procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - } - case 'E': - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x = 0 - csbi.cursorPosition.y += short(n) - procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'F': - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x = 0 - csbi.cursorPosition.y -= short(n) - procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'G': - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x = short(n) - procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'H': - token := strings.Split(buf.String(), ";") - if len(token) != 2 { - continue - } - n1, err := strconv.Atoi(token[0]) - if err != nil { - continue - } - n2, err := strconv.Atoi(token[1]) - if err != nil { - continue - } - csbi.cursorPosition.x = short(n2) - csbi.cursorPosition.x = short(n1) - procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'J': - n, err := strconv.Atoi(buf.String()) - if err != nil { - continue - } - var cursor coord - switch n { - case 0: - cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} - case 1: - cursor = coord{x: csbi.window.left, y: csbi.window.top} - case 2: - cursor = coord{x: csbi.window.left, y: csbi.window.top} - } - var count, written dword - count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x) - procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - case 'K': - n, err := strconv.Atoi(buf.String()) - if err != nil { - continue - } - var cursor coord - switch n { - case 0: - cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} - case 1: - cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y} - case 2: - cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y} - } - var count, written dword - count = dword(csbi.size.x - csbi.cursorPosition.x) - procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - case 'm': - attr := csbi.attributes - cs := buf.String() - if cs == "" { - procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr)) - continue - } - token := strings.Split(cs, ";") - for i := 0; i < len(token); i += 1 { - ns := token[i] - if n, err = strconv.Atoi(ns); err == nil { - switch { - case n == 0 || n == 100: - attr = w.oldattr - case 1 <= n && n <= 5: - attr |= foregroundIntensity - case n == 7: - attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) - case 22 == n || n == 25 || n == 25: - attr |= foregroundIntensity - case n == 27: - attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) - case 30 <= n && n <= 37: - attr = (attr & backgroundMask) - if (n-30)&1 != 0 { - attr |= foregroundRed - } - if (n-30)&2 != 0 { - attr |= foregroundGreen - } - if (n-30)&4 != 0 { - attr |= foregroundBlue - } - case n == 38: // set foreground color. - if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") { - if n256, err := strconv.Atoi(token[i+2]); err == nil { - if n256foreAttr == nil { - n256setup() - } - attr &= backgroundMask - attr |= n256foreAttr[n256] - i += 2 - } - } else { - attr = attr & (w.oldattr & backgroundMask) - } - case n == 39: // reset foreground color. - attr &= backgroundMask - attr |= w.oldattr & foregroundMask - case 40 <= n && n <= 47: - attr = (attr & foregroundMask) - if (n-40)&1 != 0 { - attr |= backgroundRed - } - if (n-40)&2 != 0 { - attr |= backgroundGreen - } - if (n-40)&4 != 0 { - attr |= backgroundBlue - } - case n == 48: // set background color. - if i < len(token)-2 && token[i+1] == "5" { - if n256, err := strconv.Atoi(token[i+2]); err == nil { - if n256backAttr == nil { - n256setup() - } - attr &= foregroundMask - attr |= n256backAttr[n256] - i += 2 - } - } else { - attr = attr & (w.oldattr & foregroundMask) - } - case n == 49: // reset foreground color. - attr &= foregroundMask - attr |= w.oldattr & backgroundMask - case 90 <= n && n <= 97: - attr = (attr & backgroundMask) - attr |= foregroundIntensity - if (n-90)&1 != 0 { - attr |= foregroundRed - } - if (n-90)&2 != 0 { - attr |= foregroundGreen - } - if (n-90)&4 != 0 { - attr |= foregroundBlue - } - case 100 <= n && n <= 107: - attr = (attr & foregroundMask) - attr |= backgroundIntensity - if (n-100)&1 != 0 { - attr |= backgroundRed - } - if (n-100)&2 != 0 { - attr |= backgroundGreen - } - if (n-100)&4 != 0 { - attr |= backgroundBlue - } - } - procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr)) - } - } - } - } - return len(data) - w.lastbuf.Len(), nil -} - -type consoleColor struct { - rgb int - red bool - green bool - blue bool - intensity bool -} - -func (c consoleColor) foregroundAttr() (attr word) { - if c.red { - attr |= foregroundRed - } - if c.green { - attr |= foregroundGreen - } - if c.blue { - attr |= foregroundBlue - } - if c.intensity { - attr |= foregroundIntensity - } - return -} - -func (c consoleColor) backgroundAttr() (attr word) { - if c.red { - attr |= backgroundRed - } - if c.green { - attr |= backgroundGreen - } - if c.blue { - attr |= backgroundBlue - } - if c.intensity { - attr |= backgroundIntensity - } - return -} - -var color16 = []consoleColor{ - consoleColor{0x000000, false, false, false, false}, - consoleColor{0x000080, false, false, true, false}, - consoleColor{0x008000, false, true, false, false}, - consoleColor{0x008080, false, true, true, false}, - consoleColor{0x800000, true, false, false, false}, - consoleColor{0x800080, true, false, true, false}, - consoleColor{0x808000, true, true, false, false}, - consoleColor{0xc0c0c0, true, true, true, false}, - consoleColor{0x808080, false, false, false, true}, - consoleColor{0x0000ff, false, false, true, true}, - consoleColor{0x00ff00, false, true, false, true}, - consoleColor{0x00ffff, false, true, true, true}, - consoleColor{0xff0000, true, false, false, true}, - consoleColor{0xff00ff, true, false, true, true}, - consoleColor{0xffff00, true, true, false, true}, - consoleColor{0xffffff, true, true, true, true}, -} - -type hsv struct { - h, s, v float32 -} - -func (a hsv) dist(b hsv) float32 { - dh := a.h - b.h - switch { - case dh > 0.5: - dh = 1 - dh - case dh < -0.5: - dh = -1 - dh - } - ds := a.s - b.s - dv := a.v - b.v - return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv))) -} - -func toHSV(rgb int) hsv { - r, g, b := float32((rgb&0xFF0000)>>16)/256.0, - float32((rgb&0x00FF00)>>8)/256.0, - float32(rgb&0x0000FF)/256.0 - min, max := minmax3f(r, g, b) - h := max - min - if h > 0 { - if max == r { - h = (g - b) / h - if h < 0 { - h += 6 - } - } else if max == g { - h = 2 + (b-r)/h - } else { - h = 4 + (r-g)/h - } - } - h /= 6.0 - s := max - min - if max != 0 { - s /= max - } - v := max - return hsv{h: h, s: s, v: v} -} - -type hsvTable []hsv - -func toHSVTable(rgbTable []consoleColor) hsvTable { - t := make(hsvTable, len(rgbTable)) - for i, c := range rgbTable { - t[i] = toHSV(c.rgb) - } - return t -} - -func (t hsvTable) find(rgb int) consoleColor { - hsv := toHSV(rgb) - n := 7 - l := float32(5.0) - for i, p := range t { - d := hsv.dist(p) - if d < l { - l, n = d, i - } - } - return color16[n] -} - -func minmax3f(a, b, c float32) (min, max float32) { - if a < b { - if b < c { - return a, c - } else if a < c { - return a, b - } else { - return c, b - } - } else { - if a < c { - return b, c - } else if b < c { - return b, a - } else { - return c, a - } - } -} - -var n256foreAttr []word -var n256backAttr []word - -func n256setup() { - n256foreAttr = make([]word, 256) - n256backAttr = make([]word, 256) - t := toHSVTable(color16) - for i, rgb := range color256 { - c := t.find(rgb) - n256foreAttr[i] = c.foregroundAttr() - n256backAttr[i] = c.backgroundAttr() - } -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go deleted file mode 100644 index 743555dde..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go +++ /dev/null @@ -1,230 +0,0 @@ -package formatter - -import ( - "fmt" - "os" - "regexp" - "strconv" - "strings" -) - -// ColorableStdOut and ColorableStdErr enable color output support on Windows -var ColorableStdOut = newColorable(os.Stdout) -var ColorableStdErr = newColorable(os.Stderr) - -const COLS = 80 - -type ColorMode uint8 - -const ( - ColorModeNone ColorMode = iota - ColorModeTerminal - ColorModePassthrough -) - -var SingletonFormatter = New(ColorModeTerminal) - -func F(format string, args ...interface{}) string { - return SingletonFormatter.F(format, args...) -} - -func Fi(indentation uint, format string, args ...interface{}) string { - return SingletonFormatter.Fi(indentation, format, args...) -} - -func Fiw(indentation uint, maxWidth uint, format string, args ...interface{}) string { - return SingletonFormatter.Fiw(indentation, maxWidth, format, args...) -} - -type Formatter struct { - ColorMode ColorMode - colors map[string]string - styleRe *regexp.Regexp - preserveColorStylingTags bool -} - -func NewWithNoColorBool(noColor bool) Formatter { - if noColor { - return New(ColorModeNone) - } - return New(ColorModeTerminal) -} - -func New(colorMode ColorMode) Formatter { - colorAliases := map[string]int{ - "black": 0, - "red": 1, - "green": 2, - "yellow": 3, - "blue": 4, - "magenta": 5, - "cyan": 6, - "white": 7, - } - for colorAlias, n := range colorAliases { - colorAliases[fmt.Sprintf("bright-%s", colorAlias)] = n + 8 - } - - getColor := func(color, defaultEscapeCode string) string { - color = strings.ToUpper(strings.ReplaceAll(color, "-", "_")) - envVar := fmt.Sprintf("GINKGO_CLI_COLOR_%s", color) - envVarColor := os.Getenv(envVar) - if envVarColor == "" { - return defaultEscapeCode - } - if colorCode, ok := colorAliases[envVarColor]; ok { - return fmt.Sprintf("\x1b[38;5;%dm", colorCode) - } - colorCode, err := strconv.Atoi(envVarColor) - if err != nil || colorCode < 0 || colorCode > 255 { - return defaultEscapeCode - } - return fmt.Sprintf("\x1b[38;5;%dm", colorCode) - } - - f := Formatter{ - ColorMode: colorMode, - colors: map[string]string{ - "/": "\x1b[0m", - "bold": "\x1b[1m", - "underline": "\x1b[4m", - - "red": getColor("red", "\x1b[38;5;9m"), - "orange": getColor("orange", "\x1b[38;5;214m"), - "coral": getColor("coral", "\x1b[38;5;204m"), - "magenta": getColor("magenta", "\x1b[38;5;13m"), - "green": getColor("green", "\x1b[38;5;10m"), - "dark-green": getColor("dark-green", "\x1b[38;5;28m"), - "yellow": getColor("yellow", "\x1b[38;5;11m"), - "light-yellow": getColor("light-yellow", "\x1b[38;5;228m"), - "cyan": getColor("cyan", "\x1b[38;5;14m"), - "gray": getColor("gray", "\x1b[38;5;243m"), - "light-gray": getColor("light-gray", "\x1b[38;5;246m"), - "blue": getColor("blue", "\x1b[38;5;12m"), - }, - } - colors := []string{} - for color := range f.colors { - colors = append(colors, color) - } - f.styleRe = regexp.MustCompile("{{(" + strings.Join(colors, "|") + ")}}") - return f -} - -func (f Formatter) F(format string, args ...interface{}) string { - return f.Fi(0, format, args...) -} - -func (f Formatter) Fi(indentation uint, format string, args ...interface{}) string { - return f.Fiw(indentation, 0, format, args...) -} - -func (f Formatter) Fiw(indentation uint, maxWidth uint, format string, args ...interface{}) string { - out := f.style(format) - if len(args) > 0 { - out = fmt.Sprintf(out, args...) - } - - if indentation == 0 && maxWidth == 0 { - return out - } - - lines := strings.Split(out, "\n") - - if maxWidth != 0 { - outLines := []string{} - - maxWidth = maxWidth - indentation*2 - for _, line := range lines { - if f.length(line) <= maxWidth { - outLines = append(outLines, line) - continue - } - words := strings.Split(line, " ") - outWords := []string{words[0]} - length := uint(f.length(words[0])) - for _, word := range words[1:] { - wordLength := f.length(word) - if length+wordLength+1 <= maxWidth { - length += wordLength + 1 - outWords = append(outWords, word) - continue - } - outLines = append(outLines, strings.Join(outWords, " ")) - outWords = []string{word} - length = wordLength - } - if len(outWords) > 0 { - outLines = append(outLines, strings.Join(outWords, " ")) - } - } - - lines = outLines - } - - if indentation == 0 { - return strings.Join(lines, "\n") - } - - padding := strings.Repeat(" ", int(indentation)) - for i := range lines { - if lines[i] != "" { - lines[i] = padding + lines[i] - } - } - - return strings.Join(lines, "\n") -} - -func (f Formatter) length(styled string) uint { - n := uint(0) - inStyle := false - for _, b := range styled { - if inStyle { - if b == 'm' { - inStyle = false - } - continue - } - if b == '\x1b' { - inStyle = true - continue - } - n += 1 - } - return n -} - -func (f Formatter) CycleJoin(elements []string, joiner string, cycle []string) string { - if len(elements) == 0 { - return "" - } - n := len(cycle) - out := "" - for i, text := range elements { - out += cycle[i%n] + text - if i < len(elements)-1 { - out += joiner - } - } - out += "{{/}}" - return f.style(out) -} - -func (f Formatter) style(s string) string { - switch f.ColorMode { - case ColorModeNone: - return f.styleRe.ReplaceAllString(s, "") - case ColorModePassthrough: - return s - case ColorModeTerminal: - return f.styleRe.ReplaceAllStringFunc(s, func(match string) string { - if out, ok := f.colors[strings.Trim(match, "{}")]; ok { - return out - } - return match - }) - } - - return "" -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/build/build_command.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/build/build_command.go deleted file mode 100644 index 5db5d1a7b..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/build/build_command.go +++ /dev/null @@ -1,63 +0,0 @@ -package build - -import ( - "fmt" - - "github.com/onsi/ginkgo/v2/ginkgo/command" - "github.com/onsi/ginkgo/v2/ginkgo/internal" - "github.com/onsi/ginkgo/v2/types" -) - -func BuildBuildCommand() command.Command { - var cliConfig = types.NewDefaultCLIConfig() - var goFlagsConfig = types.NewDefaultGoFlagsConfig() - - flags, err := types.BuildBuildCommandFlagSet(&cliConfig, &goFlagsConfig) - if err != nil { - panic(err) - } - - return command.Command{ - Name: "build", - Flags: flags, - Usage: "ginkgo build ", - ShortDoc: "Build the passed in (or the package in the current directory if left blank).", - DocLink: "precompiling-suites", - Command: func(args []string, _ []string) { - var errors []error - cliConfig, goFlagsConfig, errors = types.VetAndInitializeCLIAndGoConfig(cliConfig, goFlagsConfig) - command.AbortIfErrors("Ginkgo detected configuration issues:", errors) - - buildSpecs(args, cliConfig, goFlagsConfig) - }, - } -} - -func buildSpecs(args []string, cliConfig types.CLIConfig, goFlagsConfig types.GoFlagsConfig) { - suites := internal.FindSuites(args, cliConfig, false).WithoutState(internal.TestSuiteStateSkippedByFilter) - if len(suites) == 0 { - command.AbortWith("Found no test suites") - } - - internal.VerifyCLIAndFrameworkVersion(suites) - - opc := internal.NewOrderedParallelCompiler(cliConfig.ComputedNumCompilers()) - opc.StartCompiling(suites, goFlagsConfig) - - for { - suiteIdx, suite := opc.Next() - if suiteIdx >= len(suites) { - break - } - suites[suiteIdx] = suite - if suite.State.Is(internal.TestSuiteStateFailedToCompile) { - fmt.Println(suite.CompilationError.Error()) - } else { - fmt.Printf("Compiled %s.test\n", suite.PackageName) - } - } - - if suites.CountWithState(internal.TestSuiteStateFailedToCompile) > 0 { - command.AbortWith("Failed to compile all tests") - } -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/abort.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/abort.go deleted file mode 100644 index 2efd28608..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/abort.go +++ /dev/null @@ -1,61 +0,0 @@ -package command - -import "fmt" - -type AbortDetails struct { - ExitCode int - Error error - EmitUsage bool -} - -func Abort(details AbortDetails) { - panic(details) -} - -func AbortGracefullyWith(format string, args ...interface{}) { - Abort(AbortDetails{ - ExitCode: 0, - Error: fmt.Errorf(format, args...), - EmitUsage: false, - }) -} - -func AbortWith(format string, args ...interface{}) { - Abort(AbortDetails{ - ExitCode: 1, - Error: fmt.Errorf(format, args...), - EmitUsage: false, - }) -} - -func AbortWithUsage(format string, args ...interface{}) { - Abort(AbortDetails{ - ExitCode: 1, - Error: fmt.Errorf(format, args...), - EmitUsage: true, - }) -} - -func AbortIfError(preamble string, err error) { - if err != nil { - Abort(AbortDetails{ - ExitCode: 1, - Error: fmt.Errorf("%s\n%s", preamble, err.Error()), - EmitUsage: false, - }) - } -} - -func AbortIfErrors(preamble string, errors []error) { - if len(errors) > 0 { - out := "" - for _, err := range errors { - out += err.Error() - } - Abort(AbortDetails{ - ExitCode: 1, - Error: fmt.Errorf("%s\n%s", preamble, out), - EmitUsage: false, - }) - } -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/command.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/command.go deleted file mode 100644 index 12e0e5659..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/command.go +++ /dev/null @@ -1,50 +0,0 @@ -package command - -import ( - "fmt" - "io" - "strings" - - "github.com/onsi/ginkgo/v2/formatter" - "github.com/onsi/ginkgo/v2/types" -) - -type Command struct { - Name string - Flags types.GinkgoFlagSet - Usage string - ShortDoc string - Documentation string - DocLink string - Command func(args []string, additionalArgs []string) -} - -func (c Command) Run(args []string, additionalArgs []string) { - args, err := c.Flags.Parse(args) - if err != nil { - AbortWithUsage(err.Error()) - } - - c.Command(args, additionalArgs) -} - -func (c Command) EmitUsage(writer io.Writer) { - fmt.Fprintln(writer, formatter.F("{{bold}}"+c.Usage+"{{/}}")) - fmt.Fprintln(writer, formatter.F("{{gray}}%s{{/}}", strings.Repeat("-", len(c.Usage)))) - if c.ShortDoc != "" { - fmt.Fprintln(writer, formatter.Fiw(0, formatter.COLS, c.ShortDoc)) - fmt.Fprintln(writer, "") - } - if c.Documentation != "" { - fmt.Fprintln(writer, formatter.Fiw(0, formatter.COLS, c.Documentation)) - fmt.Fprintln(writer, "") - } - if c.DocLink != "" { - fmt.Fprintln(writer, formatter.Fi(0, "{{bold}}Learn more at:{{/}} {{cyan}}{{underline}}http://onsi.github.io/ginkgo/#%s{{/}}", c.DocLink)) - fmt.Fprintln(writer, "") - } - flagUsage := c.Flags.Usage() - if flagUsage != "" { - fmt.Fprintf(writer, formatter.F(flagUsage)) - } -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/program.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/program.go deleted file mode 100644 index 88dd8d6b0..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/program.go +++ /dev/null @@ -1,182 +0,0 @@ -package command - -import ( - "fmt" - "io" - "os" - "strings" - - "github.com/onsi/ginkgo/v2/formatter" - "github.com/onsi/ginkgo/v2/types" -) - -type Program struct { - Name string - Heading string - Commands []Command - DefaultCommand Command - DeprecatedCommands []DeprecatedCommand - - //For testing - leave as nil in production - OutWriter io.Writer - ErrWriter io.Writer - Exiter func(code int) -} - -type DeprecatedCommand struct { - Name string - Deprecation types.Deprecation -} - -func (p Program) RunAndExit(osArgs []string) { - var command Command - deprecationTracker := types.NewDeprecationTracker() - if p.Exiter == nil { - p.Exiter = os.Exit - } - if p.OutWriter == nil { - p.OutWriter = formatter.ColorableStdOut - } - if p.ErrWriter == nil { - p.ErrWriter = formatter.ColorableStdErr - } - - defer func() { - exitCode := 0 - - if r := recover(); r != nil { - details, ok := r.(AbortDetails) - if !ok { - panic(r) - } - - if details.Error != nil { - fmt.Fprintln(p.ErrWriter, formatter.F("{{red}}{{bold}}%s %s{{/}} {{red}}failed{{/}}", p.Name, command.Name)) - fmt.Fprintln(p.ErrWriter, formatter.Fi(1, details.Error.Error())) - } - if details.EmitUsage { - if details.Error != nil { - fmt.Fprintln(p.ErrWriter, "") - } - command.EmitUsage(p.ErrWriter) - } - exitCode = details.ExitCode - } - - command.Flags.ValidateDeprecations(deprecationTracker) - if deprecationTracker.DidTrackDeprecations() { - fmt.Fprintln(p.ErrWriter, deprecationTracker.DeprecationsReport()) - } - p.Exiter(exitCode) - return - }() - - args, additionalArgs := []string{}, []string{} - - foundDelimiter := false - for _, arg := range osArgs[1:] { - if !foundDelimiter { - if arg == "--" { - foundDelimiter = true - continue - } - } - - if foundDelimiter { - additionalArgs = append(additionalArgs, arg) - } else { - args = append(args, arg) - } - } - - command = p.DefaultCommand - if len(args) > 0 { - p.handleHelpRequestsAndExit(p.OutWriter, args) - if command.Name == args[0] { - args = args[1:] - } else { - for _, deprecatedCommand := range p.DeprecatedCommands { - if deprecatedCommand.Name == args[0] { - deprecationTracker.TrackDeprecation(deprecatedCommand.Deprecation) - return - } - } - for _, tryCommand := range p.Commands { - if tryCommand.Name == args[0] { - command, args = tryCommand, args[1:] - break - } - } - } - } - - command.Run(args, additionalArgs) -} - -func (p Program) handleHelpRequestsAndExit(writer io.Writer, args []string) { - if len(args) == 0 { - return - } - - matchesHelpFlag := func(args ...string) bool { - for _, arg := range args { - if arg == "--help" || arg == "-help" || arg == "-h" || arg == "--h" { - return true - } - } - return false - } - if len(args) == 1 { - if args[0] == "help" || matchesHelpFlag(args[0]) { - p.EmitUsage(writer) - Abort(AbortDetails{}) - } - } else { - var name string - if args[0] == "help" || matchesHelpFlag(args[0]) { - name = args[1] - } else if matchesHelpFlag(args[1:]...) { - name = args[0] - } else { - return - } - - if p.DefaultCommand.Name == name || p.Name == name { - p.DefaultCommand.EmitUsage(writer) - Abort(AbortDetails{}) - } - for _, command := range p.Commands { - if command.Name == name { - command.EmitUsage(writer) - Abort(AbortDetails{}) - } - } - - fmt.Fprintln(writer, formatter.F("{{red}}Unknown Command: {{bold}}%s{{/}}", name)) - fmt.Fprintln(writer, "") - p.EmitUsage(writer) - Abort(AbortDetails{ExitCode: 1}) - } - return -} - -func (p Program) EmitUsage(writer io.Writer) { - fmt.Fprintln(writer, formatter.F(p.Heading)) - fmt.Fprintln(writer, formatter.F("{{gray}}%s{{/}}", strings.Repeat("-", len(p.Heading)))) - fmt.Fprintln(writer, formatter.F("For usage information for a command, run {{bold}}%s help COMMAND{{/}}.", p.Name)) - fmt.Fprintln(writer, formatter.F("For usage information for the default command, run {{bold}}%s help %s{{/}} or {{bold}}%s help %s{{/}}.", p.Name, p.Name, p.Name, p.DefaultCommand.Name)) - fmt.Fprintln(writer, "") - fmt.Fprintln(writer, formatter.F("The following commands are available:")) - - fmt.Fprintln(writer, formatter.Fi(1, "{{bold}}%s{{/}} or %s {{bold}}%s{{/}} - {{gray}}%s{{/}}", p.Name, p.Name, p.DefaultCommand.Name, p.DefaultCommand.Usage)) - if p.DefaultCommand.ShortDoc != "" { - fmt.Fprintln(writer, formatter.Fi(2, p.DefaultCommand.ShortDoc)) - } - - for _, command := range p.Commands { - fmt.Fprintln(writer, formatter.Fi(1, "{{bold}}%s{{/}} - {{gray}}%s{{/}}", command.Name, command.Usage)) - if command.ShortDoc != "" { - fmt.Fprintln(writer, formatter.Fi(2, command.ShortDoc)) - } - } -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/boostrap_templates.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/boostrap_templates.go deleted file mode 100644 index a367a1fc9..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/boostrap_templates.go +++ /dev/null @@ -1,48 +0,0 @@ -package generators - -var bootstrapText = `package {{.Package}} - -import ( - "testing" - - {{.GinkgoImport}} - {{.GomegaImport}} -) - -func Test{{.FormattedName}}(t *testing.T) { - {{.GomegaPackage}}RegisterFailHandler({{.GinkgoPackage}}Fail) - {{.GinkgoPackage}}RunSpecs(t, "{{.FormattedName}} Suite") -} -` - -var agoutiBootstrapText = `package {{.Package}} - -import ( - "testing" - - {{.GinkgoImport}} - {{.GomegaImport}} - "github.com/sclevine/agouti" -) - -func Test{{.FormattedName}}(t *testing.T) { - {{.GomegaPackage}}RegisterFailHandler({{.GinkgoPackage}}Fail) - {{.GinkgoPackage}}RunSpecs(t, "{{.FormattedName}} Suite") -} - -var agoutiDriver *agouti.WebDriver - -var _ = {{.GinkgoPackage}}BeforeSuite(func() { - // Choose a WebDriver: - - agoutiDriver = agouti.PhantomJS() - // agoutiDriver = agouti.Selenium() - // agoutiDriver = agouti.ChromeDriver() - - {{.GomegaPackage}}Expect(agoutiDriver.Start()).To({{.GomegaPackage}}Succeed()) -}) - -var _ = {{.GinkgoPackage}}AfterSuite(func() { - {{.GomegaPackage}}Expect(agoutiDriver.Stop()).To({{.GomegaPackage}}Succeed()) -}) -` diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go deleted file mode 100644 index 73aff0b7a..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go +++ /dev/null @@ -1,133 +0,0 @@ -package generators - -import ( - "bytes" - "encoding/json" - "fmt" - "os" - "text/template" - - sprig "github.com/go-task/slim-sprig" - "github.com/onsi/ginkgo/v2/ginkgo/command" - "github.com/onsi/ginkgo/v2/ginkgo/internal" - "github.com/onsi/ginkgo/v2/types" -) - -func BuildBootstrapCommand() command.Command { - conf := GeneratorsConfig{} - flags, err := types.NewGinkgoFlagSet( - types.GinkgoFlags{ - {Name: "agouti", KeyPath: "Agouti", - Usage: "If set, bootstrap will generate a bootstrap file for writing Agouti tests"}, - {Name: "nodot", KeyPath: "NoDot", - Usage: "If set, bootstrap will generate a bootstrap test file that does not dot-import ginkgo and gomega"}, - {Name: "internal", KeyPath: "Internal", - Usage: "If set, bootstrap will generate a bootstrap test file that uses the regular package name (i.e. `package X`, not `package X_test`)"}, - {Name: "template", KeyPath: "CustomTemplate", - UsageArgument: "template-file", - Usage: "If specified, generate will use the contents of the file passed as the bootstrap template"}, - {Name: "template-data", KeyPath: "CustomTemplateData", - UsageArgument: "template-data-file", - Usage: "If specified, generate will use the contents of the file passed as data to be rendered in the bootstrap template"}, - }, - &conf, - types.GinkgoFlagSections{}, - ) - - if err != nil { - panic(err) - } - - return command.Command{ - Name: "bootstrap", - Usage: "ginkgo bootstrap", - ShortDoc: "Bootstrap a test suite for the current package", - Documentation: `Tests written in Ginkgo and Gomega require a small amount of boilerplate to hook into Go's testing infrastructure. - -{{bold}}ginkgo bootstrap{{/}} generates this boilerplate for you in a file named X_suite_test.go where X is the name of the package under test.`, - DocLink: "generators", - Flags: flags, - Command: func(_ []string, _ []string) { - generateBootstrap(conf) - }, - } -} - -type bootstrapData struct { - Package string - FormattedName string - - GinkgoImport string - GomegaImport string - GinkgoPackage string - GomegaPackage string - CustomData map[string]any -} - -func generateBootstrap(conf GeneratorsConfig) { - packageName, bootstrapFilePrefix, formattedName := getPackageAndFormattedName() - - data := bootstrapData{ - Package: determinePackageName(packageName, conf.Internal), - FormattedName: formattedName, - - GinkgoImport: `. "github.com/onsi/ginkgo/v2"`, - GomegaImport: `. "github.com/onsi/gomega"`, - GinkgoPackage: "", - GomegaPackage: "", - } - - if conf.NoDot { - data.GinkgoImport = `"github.com/onsi/ginkgo/v2"` - data.GomegaImport = `"github.com/onsi/gomega"` - data.GinkgoPackage = `ginkgo.` - data.GomegaPackage = `gomega.` - } - - targetFile := fmt.Sprintf("%s_suite_test.go", bootstrapFilePrefix) - if internal.FileExists(targetFile) { - command.AbortWith("{{bold}}%s{{/}} already exists", targetFile) - } else { - fmt.Printf("Generating ginkgo test suite bootstrap for %s in:\n\t%s\n", packageName, targetFile) - } - - f, err := os.Create(targetFile) - command.AbortIfError("Failed to create file:", err) - defer f.Close() - - var templateText string - if conf.CustomTemplate != "" { - tpl, err := os.ReadFile(conf.CustomTemplate) - command.AbortIfError("Failed to read custom bootstrap file:", err) - templateText = string(tpl) - if conf.CustomTemplateData != "" { - var tplCustomDataMap map[string]any - tplCustomData, err := os.ReadFile(conf.CustomTemplateData) - command.AbortIfError("Failed to read custom boostrap data file:", err) - if !json.Valid([]byte(tplCustomData)) { - command.AbortWith("Invalid JSON object in custom data file.") - } - //create map from the custom template data - json.Unmarshal(tplCustomData, &tplCustomDataMap) - data.CustomData = tplCustomDataMap - } - } else if conf.Agouti { - templateText = agoutiBootstrapText - } else { - templateText = bootstrapText - } - - //Setting the option to explicitly fail if template is rendered trying to access missing key - bootstrapTemplate, err := template.New("bootstrap").Funcs(sprig.TxtFuncMap()).Option("missingkey=error").Parse(templateText) - command.AbortIfError("Failed to parse bootstrap template:", err) - - buf := &bytes.Buffer{} - //Being explicit about failing sooner during template rendering - //when accessing custom data rather than during the go fmt command - err = bootstrapTemplate.Execute(buf, data) - command.AbortIfError("Failed to render bootstrap template:", err) - - buf.WriteTo(f) - - internal.GoFmt(targetFile) -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go deleted file mode 100644 index be01dec97..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go +++ /dev/null @@ -1,264 +0,0 @@ -package generators - -import ( - "bytes" - "encoding/json" - "fmt" - "os" - "path/filepath" - "strconv" - "strings" - "text/template" - - sprig "github.com/go-task/slim-sprig" - "github.com/onsi/ginkgo/v2/ginkgo/command" - "github.com/onsi/ginkgo/v2/ginkgo/internal" - "github.com/onsi/ginkgo/v2/types" -) - -func BuildGenerateCommand() command.Command { - conf := GeneratorsConfig{} - flags, err := types.NewGinkgoFlagSet( - types.GinkgoFlags{ - {Name: "agouti", KeyPath: "Agouti", - Usage: "If set, generate will create a test file for writing Agouti tests"}, - {Name: "nodot", KeyPath: "NoDot", - Usage: "If set, generate will create a test file that does not dot-import ginkgo and gomega"}, - {Name: "internal", KeyPath: "Internal", - Usage: "If set, generate will create a test file that uses the regular package name (i.e. `package X`, not `package X_test`)"}, - {Name: "template", KeyPath: "CustomTemplate", - UsageArgument: "template-file", - Usage: "If specified, generate will use the contents of the file passed as the test file template"}, - {Name: "template-data", KeyPath: "CustomTemplateData", - UsageArgument: "template-data-file", - Usage: "If specified, generate will use the contents of the file passed as data to be rendered in the test file template"}, - {Name: "tags", KeyPath: "Tags", - UsageArgument: "build-tags", - Usage: "If specified, generate will create a test file that uses the given build tags (i.e. `--tags e2e,!unit` will add `//go:build e2e,!unit`)"}, - }, - &conf, - types.GinkgoFlagSections{}, - ) - - if err != nil { - panic(err) - } - - return command.Command{ - Name: "generate", - Usage: "ginkgo generate ", - ShortDoc: "Generate a test file named _test.go", - Documentation: `If the optional argument is omitted, a file named after the package in the current directory will be created. - -You can pass multiple to generate multiple files simultaneously. The resulting files are named _test.go. - -You can also pass a of the form "file.go" and generate will emit "file_test.go".`, - DocLink: "generators", - Flags: flags, - Command: func(args []string, _ []string) { - generateTestFiles(conf, args) - }, - } -} - -type specData struct { - BuildTags string - Package string - Subject string - PackageImportPath string - ImportPackage bool - - GinkgoImport string - GomegaImport string - GinkgoPackage string - GomegaPackage string - CustomData map[string]any -} - -func generateTestFiles(conf GeneratorsConfig, args []string) { - subjects := args - if len(subjects) == 0 { - subjects = []string{""} - } - for _, subject := range subjects { - generateTestFileForSubject(subject, conf) - } -} - -func generateTestFileForSubject(subject string, conf GeneratorsConfig) { - packageName, specFilePrefix, formattedName := getPackageAndFormattedName() - if subject != "" { - specFilePrefix = formatSubject(subject) - formattedName = prettifyName(specFilePrefix) - } - - if conf.Internal { - specFilePrefix = specFilePrefix + "_internal" - } - - data := specData{ - BuildTags: getBuildTags(conf.Tags), - Package: determinePackageName(packageName, conf.Internal), - Subject: formattedName, - PackageImportPath: getPackageImportPath(), - ImportPackage: !conf.Internal, - - GinkgoImport: `. "github.com/onsi/ginkgo/v2"`, - GomegaImport: `. "github.com/onsi/gomega"`, - GinkgoPackage: "", - GomegaPackage: "", - } - - if conf.NoDot { - data.GinkgoImport = `"github.com/onsi/ginkgo/v2"` - data.GomegaImport = `"github.com/onsi/gomega"` - data.GinkgoPackage = `ginkgo.` - data.GomegaPackage = `gomega.` - } - - targetFile := fmt.Sprintf("%s_test.go", specFilePrefix) - if internal.FileExists(targetFile) { - command.AbortWith("{{bold}}%s{{/}} already exists", targetFile) - } else { - fmt.Printf("Generating ginkgo test for %s in:\n %s\n", data.Subject, targetFile) - } - - f, err := os.Create(targetFile) - command.AbortIfError("Failed to create test file:", err) - defer f.Close() - - var templateText string - if conf.CustomTemplate != "" { - tpl, err := os.ReadFile(conf.CustomTemplate) - command.AbortIfError("Failed to read custom template file:", err) - templateText = string(tpl) - if conf.CustomTemplateData != "" { - var tplCustomDataMap map[string]any - tplCustomData, err := os.ReadFile(conf.CustomTemplateData) - command.AbortIfError("Failed to read custom template data file:", err) - if !json.Valid([]byte(tplCustomData)) { - command.AbortWith("Invalid JSON object in custom data file.") - } - //create map from the custom template data - json.Unmarshal(tplCustomData, &tplCustomDataMap) - data.CustomData = tplCustomDataMap - } - } else if conf.Agouti { - templateText = agoutiSpecText - } else { - templateText = specText - } - - //Setting the option to explicitly fail if template is rendered trying to access missing key - specTemplate, err := template.New("spec").Funcs(sprig.TxtFuncMap()).Option("missingkey=error").Parse(templateText) - command.AbortIfError("Failed to read parse test template:", err) - - //Being explicit about failing sooner during template rendering - //when accessing custom data rather than during the go fmt command - err = specTemplate.Execute(f, data) - command.AbortIfError("Failed to render bootstrap template:", err) - internal.GoFmt(targetFile) -} - -func formatSubject(name string) string { - name = strings.ReplaceAll(name, "-", "_") - name = strings.ReplaceAll(name, " ", "_") - name = strings.Split(name, ".go")[0] - name = strings.Split(name, "_test")[0] - return name -} - -// moduleName returns module name from go.mod from given module root directory -func moduleName(modRoot string) string { - modFile, err := os.Open(filepath.Join(modRoot, "go.mod")) - if err != nil { - return "" - } - - mod := make([]byte, 128) - _, err = modFile.Read(mod) - if err != nil { - return "" - } - - slashSlash := []byte("//") - moduleStr := []byte("module") - - for len(mod) > 0 { - line := mod - mod = nil - if i := bytes.IndexByte(line, '\n'); i >= 0 { - line, mod = line[:i], line[i+1:] - } - if i := bytes.Index(line, slashSlash); i >= 0 { - line = line[:i] - } - line = bytes.TrimSpace(line) - if !bytes.HasPrefix(line, moduleStr) { - continue - } - line = line[len(moduleStr):] - n := len(line) - line = bytes.TrimSpace(line) - if len(line) == n || len(line) == 0 { - continue - } - - if line[0] == '"' || line[0] == '`' { - p, err := strconv.Unquote(string(line)) - if err != nil { - return "" // malformed quoted string or multiline module path - } - return p - } - - return string(line) - } - - return "" // missing module path -} - -func findModuleRoot(dir string) (root string) { - dir = filepath.Clean(dir) - - // Look for enclosing go.mod. - for { - if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() { - return dir - } - d := filepath.Dir(dir) - if d == dir { - break - } - dir = d - } - return "" -} - -func getPackageImportPath() string { - workingDir, err := os.Getwd() - if err != nil { - panic(err.Error()) - } - - sep := string(filepath.Separator) - - // Try go.mod file first - modRoot := findModuleRoot(workingDir) - if modRoot != "" { - modName := moduleName(modRoot) - if modName != "" { - cd := strings.ReplaceAll(workingDir, modRoot, "") - cd = strings.ReplaceAll(cd, sep, "/") - return modName + cd - } - } - - // Fallback to GOPATH structure - paths := strings.Split(workingDir, sep+"src"+sep) - if len(paths) == 1 { - fmt.Printf("\nCouldn't identify package import path.\n\n\tginkgo generate\n\nMust be run within a package directory under $GOPATH/src/...\nYou're going to have to change UNKNOWN_PACKAGE_PATH in the generated file...\n\n") - return "UNKNOWN_PACKAGE_PATH" - } - return filepath.ToSlash(paths[len(paths)-1]) -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_templates.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_templates.go deleted file mode 100644 index 4dab07d03..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_templates.go +++ /dev/null @@ -1,43 +0,0 @@ -package generators - -var specText = `{{.BuildTags}} -package {{.Package}} - -import ( - {{.GinkgoImport}} - {{.GomegaImport}} - - {{if .ImportPackage}}"{{.PackageImportPath}}"{{end}} -) - -var _ = {{.GinkgoPackage}}Describe("{{.Subject}}", func() { - -}) -` - -var agoutiSpecText = `{{.BuildTags}} -package {{.Package}} - -import ( - {{.GinkgoImport}} - {{.GomegaImport}} - "github.com/sclevine/agouti" - . "github.com/sclevine/agouti/matchers" - - {{if .ImportPackage}}"{{.PackageImportPath}}"{{end}} -) - -var _ = {{.GinkgoPackage}}Describe("{{.Subject}}", func() { - var page *agouti.Page - - {{.GinkgoPackage}}BeforeEach(func() { - var err error - page, err = agoutiDriver.NewPage() - {{.GomegaPackage}}Expect(err).NotTo({{.GomegaPackage}}HaveOccurred()) - }) - - {{.GinkgoPackage}}AfterEach(func() { - {{.GomegaPackage}}Expect(page.Destroy()).To({{.GomegaPackage}}Succeed()) - }) -}) -` diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.go deleted file mode 100644 index 28c7aa6f4..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.go +++ /dev/null @@ -1,76 +0,0 @@ -package generators - -import ( - "fmt" - "go/build" - "os" - "path/filepath" - "strconv" - "strings" - - "github.com/onsi/ginkgo/v2/ginkgo/command" -) - -type GeneratorsConfig struct { - Agouti, NoDot, Internal bool - CustomTemplate string - CustomTemplateData string - Tags string -} - -func getPackageAndFormattedName() (string, string, string) { - path, err := os.Getwd() - command.AbortIfError("Could not get current working directory:", err) - - dirName := strings.ReplaceAll(filepath.Base(path), "-", "_") - dirName = strings.ReplaceAll(dirName, " ", "_") - - pkg, err := build.ImportDir(path, 0) - packageName := pkg.Name - if err != nil { - packageName = ensureLegalPackageName(dirName) - } - - formattedName := prettifyName(filepath.Base(path)) - return packageName, dirName, formattedName -} - -func ensureLegalPackageName(name string) string { - if name == "_" { - return "underscore" - } - if len(name) == 0 { - return "empty" - } - n, isDigitErr := strconv.Atoi(string(name[0])) - if isDigitErr == nil { - return []string{"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}[n] + name[1:] - } - return name -} - -func prettifyName(name string) string { - name = strings.ReplaceAll(name, "-", " ") - name = strings.ReplaceAll(name, "_", " ") - name = strings.Title(name) - name = strings.ReplaceAll(name, " ", "") - return name -} - -func determinePackageName(name string, internal bool) string { - if internal { - return name - } - - return name + "_test" -} - -// getBuildTags returns the resultant string to be added. -// If the input string is not empty, then returns a `//go:build {}` string, -// otherwise returns an empty string. -func getBuildTags(tags string) string { - if tags != "" { - return fmt.Sprintf("//go:build %s\n", tags) - } - return "" -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/compile.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/compile.go deleted file mode 100644 index 86da7340d..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/compile.go +++ /dev/null @@ -1,161 +0,0 @@ -package internal - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - "sync" - - "github.com/onsi/ginkgo/v2/types" -) - -func CompileSuite(suite TestSuite, goFlagsConfig types.GoFlagsConfig) TestSuite { - if suite.PathToCompiledTest != "" { - return suite - } - - suite.CompilationError = nil - - path, err := filepath.Abs(filepath.Join(suite.Path, suite.PackageName+".test")) - if err != nil { - suite.State = TestSuiteStateFailedToCompile - suite.CompilationError = fmt.Errorf("Failed to compute compilation target path:\n%s", err.Error()) - return suite - } - - ginkgoInvocationPath, _ := os.Getwd() - ginkgoInvocationPath, _ = filepath.Abs(ginkgoInvocationPath) - packagePath := suite.AbsPath() - pathToInvocationPath, err := filepath.Rel(packagePath, ginkgoInvocationPath) - if err != nil { - suite.State = TestSuiteStateFailedToCompile - suite.CompilationError = fmt.Errorf("Failed to get relative path from package to the current working directory:\n%s", err.Error()) - return suite - } - args, err := types.GenerateGoTestCompileArgs(goFlagsConfig, path, "./", pathToInvocationPath) - if err != nil { - suite.State = TestSuiteStateFailedToCompile - suite.CompilationError = fmt.Errorf("Failed to generate go test compile flags:\n%s", err.Error()) - return suite - } - - cmd := exec.Command("go", args...) - cmd.Dir = suite.Path - output, err := cmd.CombinedOutput() - if err != nil { - if len(output) > 0 { - suite.State = TestSuiteStateFailedToCompile - suite.CompilationError = fmt.Errorf("Failed to compile %s:\n\n%s", suite.PackageName, output) - } else { - suite.State = TestSuiteStateFailedToCompile - suite.CompilationError = fmt.Errorf("Failed to compile %s\n%s", suite.PackageName, err.Error()) - } - return suite - } - - if strings.Contains(string(output), "[no test files]") { - suite.State = TestSuiteStateSkippedDueToEmptyCompilation - return suite - } - - if len(output) > 0 { - fmt.Println(string(output)) - } - - if !FileExists(path) { - suite.State = TestSuiteStateFailedToCompile - suite.CompilationError = fmt.Errorf("Failed to compile %s:\nOutput file %s could not be found", suite.PackageName, path) - return suite - } - - suite.State = TestSuiteStateCompiled - suite.PathToCompiledTest = path - return suite -} - -func Cleanup(goFlagsConfig types.GoFlagsConfig, suites ...TestSuite) { - if goFlagsConfig.BinaryMustBePreserved() { - return - } - for _, suite := range suites { - if !suite.Precompiled { - os.Remove(suite.PathToCompiledTest) - } - } -} - -type parallelSuiteBundle struct { - suite TestSuite - compiled chan TestSuite -} - -type OrderedParallelCompiler struct { - mutex *sync.Mutex - stopped bool - numCompilers int - - idx int - numSuites int - completionChannels []chan TestSuite -} - -func NewOrderedParallelCompiler(numCompilers int) *OrderedParallelCompiler { - return &OrderedParallelCompiler{ - mutex: &sync.Mutex{}, - numCompilers: numCompilers, - } -} - -func (opc *OrderedParallelCompiler) StartCompiling(suites TestSuites, goFlagsConfig types.GoFlagsConfig) { - opc.stopped = false - opc.idx = 0 - opc.numSuites = len(suites) - opc.completionChannels = make([]chan TestSuite, opc.numSuites) - - toCompile := make(chan parallelSuiteBundle, opc.numCompilers) - for compiler := 0; compiler < opc.numCompilers; compiler++ { - go func() { - for bundle := range toCompile { - c, suite := bundle.compiled, bundle.suite - opc.mutex.Lock() - stopped := opc.stopped - opc.mutex.Unlock() - if !stopped { - suite = CompileSuite(suite, goFlagsConfig) - } - c <- suite - } - }() - } - - for idx, suite := range suites { - opc.completionChannels[idx] = make(chan TestSuite, 1) - toCompile <- parallelSuiteBundle{suite, opc.completionChannels[idx]} - if idx == 0 { //compile first suite serially - suite = <-opc.completionChannels[0] - opc.completionChannels[0] <- suite - } - } - - close(toCompile) -} - -func (opc *OrderedParallelCompiler) Next() (int, TestSuite) { - if opc.idx >= opc.numSuites { - return opc.numSuites, TestSuite{} - } - - idx := opc.idx - suite := <-opc.completionChannels[idx] - opc.idx = opc.idx + 1 - - return idx, suite -} - -func (opc *OrderedParallelCompiler) StopAndDrain() { - opc.mutex.Lock() - opc.stopped = true - opc.mutex.Unlock() -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/profiles_and_reports.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/profiles_and_reports.go deleted file mode 100644 index 26de28b57..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/profiles_and_reports.go +++ /dev/null @@ -1,237 +0,0 @@ -package internal - -import ( - "bytes" - "fmt" - "os" - "os/exec" - "path/filepath" - "regexp" - "strconv" - - "github.com/google/pprof/profile" - "github.com/onsi/ginkgo/v2/reporters" - "github.com/onsi/ginkgo/v2/types" -) - -func AbsPathForGeneratedAsset(assetName string, suite TestSuite, cliConfig types.CLIConfig, process int) string { - suffix := "" - if process != 0 { - suffix = fmt.Sprintf(".%d", process) - } - if cliConfig.OutputDir == "" { - return filepath.Join(suite.AbsPath(), assetName+suffix) - } - outputDir, _ := filepath.Abs(cliConfig.OutputDir) - return filepath.Join(outputDir, suite.NamespacedName()+"_"+assetName+suffix) -} - -func FinalizeProfilesAndReportsForSuites(suites TestSuites, cliConfig types.CLIConfig, suiteConfig types.SuiteConfig, reporterConfig types.ReporterConfig, goFlagsConfig types.GoFlagsConfig) ([]string, error) { - messages := []string{} - suitesWithProfiles := suites.WithState(TestSuiteStatePassed, TestSuiteStateFailed) //anything else won't have actually run and generated a profile - - // merge cover profiles if need be - if goFlagsConfig.Cover && !cliConfig.KeepSeparateCoverprofiles { - coverProfiles := []string{} - for _, suite := range suitesWithProfiles { - if !suite.HasProgrammaticFocus { - coverProfiles = append(coverProfiles, AbsPathForGeneratedAsset(goFlagsConfig.CoverProfile, suite, cliConfig, 0)) - } - } - - if len(coverProfiles) > 0 { - dst := goFlagsConfig.CoverProfile - if cliConfig.OutputDir != "" { - dst = filepath.Join(cliConfig.OutputDir, goFlagsConfig.CoverProfile) - } - err := MergeAndCleanupCoverProfiles(coverProfiles, dst) - if err != nil { - return messages, err - } - coverage, err := GetCoverageFromCoverProfile(dst) - if err != nil { - return messages, err - } - if coverage == 0 { - messages = append(messages, "composite coverage: [no statements]") - } else if suitesWithProfiles.AnyHaveProgrammaticFocus() { - messages = append(messages, fmt.Sprintf("composite coverage: %.1f%% of statements however some suites did not contribute because they included programatically focused specs", coverage)) - } else { - messages = append(messages, fmt.Sprintf("composite coverage: %.1f%% of statements", coverage)) - } - } else { - messages = append(messages, "no composite coverage computed: all suites included programatically focused specs") - } - } - - // copy binaries if need be - for _, suite := range suitesWithProfiles { - if goFlagsConfig.BinaryMustBePreserved() && cliConfig.OutputDir != "" { - src := suite.PathToCompiledTest - dst := filepath.Join(cliConfig.OutputDir, suite.NamespacedName()+".test") - if suite.Precompiled { - if err := CopyFile(src, dst); err != nil { - return messages, err - } - } else { - if err := os.Rename(src, dst); err != nil { - return messages, err - } - } - } - } - - type reportFormat struct { - ReportName string - GenerateFunc func(types.Report, string) error - MergeFunc func([]string, string) ([]string, error) - } - reportFormats := []reportFormat{} - if reporterConfig.JSONReport != "" { - reportFormats = append(reportFormats, reportFormat{ReportName: reporterConfig.JSONReport, GenerateFunc: reporters.GenerateJSONReport, MergeFunc: reporters.MergeAndCleanupJSONReports}) - } - if reporterConfig.JUnitReport != "" { - reportFormats = append(reportFormats, reportFormat{ReportName: reporterConfig.JUnitReport, GenerateFunc: reporters.GenerateJUnitReport, MergeFunc: reporters.MergeAndCleanupJUnitReports}) - } - if reporterConfig.TeamcityReport != "" { - reportFormats = append(reportFormats, reportFormat{ReportName: reporterConfig.TeamcityReport, GenerateFunc: reporters.GenerateTeamcityReport, MergeFunc: reporters.MergeAndCleanupTeamcityReports}) - } - - // Generate reports for suites that failed to run - reportableSuites := suites.ThatAreGinkgoSuites() - for _, suite := range reportableSuites.WithState(TestSuiteStateFailedToCompile, TestSuiteStateFailedDueToTimeout, TestSuiteStateSkippedDueToPriorFailures, TestSuiteStateSkippedDueToEmptyCompilation) { - report := types.Report{ - SuitePath: suite.AbsPath(), - SuiteConfig: suiteConfig, - SuiteSucceeded: false, - } - switch suite.State { - case TestSuiteStateFailedToCompile: - report.SpecialSuiteFailureReasons = append(report.SpecialSuiteFailureReasons, suite.CompilationError.Error()) - case TestSuiteStateFailedDueToTimeout: - report.SpecialSuiteFailureReasons = append(report.SpecialSuiteFailureReasons, TIMEOUT_ELAPSED_FAILURE_REASON) - case TestSuiteStateSkippedDueToPriorFailures: - report.SpecialSuiteFailureReasons = append(report.SpecialSuiteFailureReasons, PRIOR_FAILURES_FAILURE_REASON) - case TestSuiteStateSkippedDueToEmptyCompilation: - report.SpecialSuiteFailureReasons = append(report.SpecialSuiteFailureReasons, EMPTY_SKIP_FAILURE_REASON) - report.SuiteSucceeded = true - } - - for _, format := range reportFormats { - format.GenerateFunc(report, AbsPathForGeneratedAsset(format.ReportName, suite, cliConfig, 0)) - } - } - - // Merge reports unless we've been asked to keep them separate - if !cliConfig.KeepSeparateReports { - for _, format := range reportFormats { - reports := []string{} - for _, suite := range reportableSuites { - reports = append(reports, AbsPathForGeneratedAsset(format.ReportName, suite, cliConfig, 0)) - } - dst := format.ReportName - if cliConfig.OutputDir != "" { - dst = filepath.Join(cliConfig.OutputDir, format.ReportName) - } - mergeMessages, err := format.MergeFunc(reports, dst) - messages = append(messages, mergeMessages...) - if err != nil { - return messages, err - } - } - } - - return messages, nil -} - -// loads each profile, combines them, deletes them, stores them in destination -func MergeAndCleanupCoverProfiles(profiles []string, destination string) error { - combined := &bytes.Buffer{} - modeRegex := regexp.MustCompile(`^mode: .*\n`) - for i, profile := range profiles { - contents, err := os.ReadFile(profile) - if err != nil { - return fmt.Errorf("Unable to read coverage file %s:\n%s", profile, err.Error()) - } - os.Remove(profile) - - // remove the cover mode line from every file - // except the first one - if i > 0 { - contents = modeRegex.ReplaceAll(contents, []byte{}) - } - - _, err = combined.Write(contents) - - // Add a newline to the end of every file if missing. - if err == nil && len(contents) > 0 && contents[len(contents)-1] != '\n' { - _, err = combined.Write([]byte("\n")) - } - - if err != nil { - return fmt.Errorf("Unable to append to coverprofile:\n%s", err.Error()) - } - } - - err := os.WriteFile(destination, combined.Bytes(), 0666) - if err != nil { - return fmt.Errorf("Unable to create combined cover profile:\n%s", err.Error()) - } - return nil -} - -func GetCoverageFromCoverProfile(profile string) (float64, error) { - cmd := exec.Command("go", "tool", "cover", "-func", profile) - output, err := cmd.CombinedOutput() - if err != nil { - return 0, fmt.Errorf("Could not process Coverprofile %s: %s - %s", profile, err.Error(), string(output)) - } - re := regexp.MustCompile(`total:\s*\(statements\)\s*(\d*\.\d*)\%`) - matches := re.FindStringSubmatch(string(output)) - if matches == nil { - return 0, fmt.Errorf("Could not parse Coverprofile to compute coverage percentage") - } - coverageString := matches[1] - coverage, err := strconv.ParseFloat(coverageString, 64) - if err != nil { - return 0, fmt.Errorf("Could not parse Coverprofile to compute coverage percentage: %s", err.Error()) - } - - return coverage, nil -} - -func MergeProfiles(profilePaths []string, destination string) error { - profiles := []*profile.Profile{} - for _, profilePath := range profilePaths { - proFile, err := os.Open(profilePath) - if err != nil { - return fmt.Errorf("Could not open profile: %s\n%s", profilePath, err.Error()) - } - prof, err := profile.Parse(proFile) - if err != nil { - return fmt.Errorf("Could not parse profile: %s\n%s", profilePath, err.Error()) - } - profiles = append(profiles, prof) - os.Remove(profilePath) - } - - mergedProfile, err := profile.Merge(profiles) - if err != nil { - return fmt.Errorf("Could not merge profiles:\n%s", err.Error()) - } - - outFile, err := os.Create(destination) - if err != nil { - return fmt.Errorf("Could not create merged profile %s:\n%s", destination, err.Error()) - } - err = mergedProfile.Write(outFile) - if err != nil { - return fmt.Errorf("Could not write merged profile %s:\n%s", destination, err.Error()) - } - err = outFile.Close() - if err != nil { - return fmt.Errorf("Could not close merged profile %s:\n%s", destination, err.Error()) - } - - return nil -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.go deleted file mode 100644 index 41052ea19..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.go +++ /dev/null @@ -1,355 +0,0 @@ -package internal - -import ( - "bytes" - "fmt" - "io" - "os" - "os/exec" - "path/filepath" - "regexp" - "strings" - "syscall" - "time" - - "github.com/onsi/ginkgo/v2/formatter" - "github.com/onsi/ginkgo/v2/ginkgo/command" - "github.com/onsi/ginkgo/v2/internal/parallel_support" - "github.com/onsi/ginkgo/v2/reporters" - "github.com/onsi/ginkgo/v2/types" -) - -func RunCompiledSuite(suite TestSuite, ginkgoConfig types.SuiteConfig, reporterConfig types.ReporterConfig, cliConfig types.CLIConfig, goFlagsConfig types.GoFlagsConfig, additionalArgs []string) TestSuite { - suite.State = TestSuiteStateFailed - suite.HasProgrammaticFocus = false - - if suite.PathToCompiledTest == "" { - return suite - } - - if suite.IsGinkgo && cliConfig.ComputedProcs() > 1 { - suite = runParallel(suite, ginkgoConfig, reporterConfig, cliConfig, goFlagsConfig, additionalArgs) - } else if suite.IsGinkgo { - suite = runSerial(suite, ginkgoConfig, reporterConfig, cliConfig, goFlagsConfig, additionalArgs) - } else { - suite = runGoTest(suite, cliConfig, goFlagsConfig) - } - runAfterRunHook(cliConfig.AfterRunHook, reporterConfig.NoColor, suite) - return suite -} - -func buildAndStartCommand(suite TestSuite, args []string, pipeToStdout bool) (*exec.Cmd, *bytes.Buffer) { - buf := &bytes.Buffer{} - cmd := exec.Command(suite.PathToCompiledTest, args...) - cmd.Dir = suite.Path - if pipeToStdout { - cmd.Stderr = io.MultiWriter(os.Stdout, buf) - cmd.Stdout = os.Stdout - } else { - cmd.Stderr = buf - cmd.Stdout = buf - } - err := cmd.Start() - command.AbortIfError("Failed to start test suite", err) - - return cmd, buf -} - -func checkForNoTestsWarning(buf *bytes.Buffer) bool { - if strings.Contains(buf.String(), "warning: no tests to run") { - fmt.Fprintf(os.Stderr, `Found no test suites, did you forget to run "ginkgo bootstrap"?`) - return true - } - return false -} - -func runGoTest(suite TestSuite, cliConfig types.CLIConfig, goFlagsConfig types.GoFlagsConfig) TestSuite { - // As we run the go test from the suite directory, make sure the cover profile is absolute - // and placed into the expected output directory when one is configured. - if goFlagsConfig.Cover && !filepath.IsAbs(goFlagsConfig.CoverProfile) { - goFlagsConfig.CoverProfile = AbsPathForGeneratedAsset(goFlagsConfig.CoverProfile, suite, cliConfig, 0) - } - - args, err := types.GenerateGoTestRunArgs(goFlagsConfig) - command.AbortIfError("Failed to generate test run arguments", err) - cmd, buf := buildAndStartCommand(suite, args, true) - - cmd.Wait() - - exitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() - passed := (exitStatus == 0) || (exitStatus == types.GINKGO_FOCUS_EXIT_CODE) - passed = !(checkForNoTestsWarning(buf) && cliConfig.RequireSuite) && passed - if passed { - suite.State = TestSuiteStatePassed - } else { - suite.State = TestSuiteStateFailed - } - - return suite -} - -func runSerial(suite TestSuite, ginkgoConfig types.SuiteConfig, reporterConfig types.ReporterConfig, cliConfig types.CLIConfig, goFlagsConfig types.GoFlagsConfig, additionalArgs []string) TestSuite { - if goFlagsConfig.Cover { - goFlagsConfig.CoverProfile = AbsPathForGeneratedAsset(goFlagsConfig.CoverProfile, suite, cliConfig, 0) - } - if goFlagsConfig.BlockProfile != "" { - goFlagsConfig.BlockProfile = AbsPathForGeneratedAsset(goFlagsConfig.BlockProfile, suite, cliConfig, 0) - } - if goFlagsConfig.CPUProfile != "" { - goFlagsConfig.CPUProfile = AbsPathForGeneratedAsset(goFlagsConfig.CPUProfile, suite, cliConfig, 0) - } - if goFlagsConfig.MemProfile != "" { - goFlagsConfig.MemProfile = AbsPathForGeneratedAsset(goFlagsConfig.MemProfile, suite, cliConfig, 0) - } - if goFlagsConfig.MutexProfile != "" { - goFlagsConfig.MutexProfile = AbsPathForGeneratedAsset(goFlagsConfig.MutexProfile, suite, cliConfig, 0) - } - if reporterConfig.JSONReport != "" { - reporterConfig.JSONReport = AbsPathForGeneratedAsset(reporterConfig.JSONReport, suite, cliConfig, 0) - } - if reporterConfig.JUnitReport != "" { - reporterConfig.JUnitReport = AbsPathForGeneratedAsset(reporterConfig.JUnitReport, suite, cliConfig, 0) - } - if reporterConfig.TeamcityReport != "" { - reporterConfig.TeamcityReport = AbsPathForGeneratedAsset(reporterConfig.TeamcityReport, suite, cliConfig, 0) - } - - args, err := types.GenerateGinkgoTestRunArgs(ginkgoConfig, reporterConfig, goFlagsConfig) - command.AbortIfError("Failed to generate test run arguments", err) - args = append([]string{"--test.timeout=0"}, args...) - args = append(args, additionalArgs...) - - cmd, buf := buildAndStartCommand(suite, args, true) - - cmd.Wait() - - exitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() - suite.HasProgrammaticFocus = (exitStatus == types.GINKGO_FOCUS_EXIT_CODE) - passed := (exitStatus == 0) || (exitStatus == types.GINKGO_FOCUS_EXIT_CODE) - passed = !(checkForNoTestsWarning(buf) && cliConfig.RequireSuite) && passed - if passed { - suite.State = TestSuiteStatePassed - } else { - suite.State = TestSuiteStateFailed - } - - if suite.HasProgrammaticFocus { - if goFlagsConfig.Cover { - fmt.Fprintln(os.Stdout, "coverage: no coverfile was generated because specs are programmatically focused") - } - if goFlagsConfig.BlockProfile != "" { - fmt.Fprintln(os.Stdout, "no block profile was generated because specs are programmatically focused") - } - if goFlagsConfig.CPUProfile != "" { - fmt.Fprintln(os.Stdout, "no cpu profile was generated because specs are programmatically focused") - } - if goFlagsConfig.MemProfile != "" { - fmt.Fprintln(os.Stdout, "no mem profile was generated because specs are programmatically focused") - } - if goFlagsConfig.MutexProfile != "" { - fmt.Fprintln(os.Stdout, "no mutex profile was generated because specs are programmatically focused") - } - } - - return suite -} - -func runParallel(suite TestSuite, ginkgoConfig types.SuiteConfig, reporterConfig types.ReporterConfig, cliConfig types.CLIConfig, goFlagsConfig types.GoFlagsConfig, additionalArgs []string) TestSuite { - type procResult struct { - passed bool - hasProgrammaticFocus bool - } - - numProcs := cliConfig.ComputedProcs() - procOutput := make([]*bytes.Buffer, numProcs) - coverProfiles := []string{} - - blockProfiles := []string{} - cpuProfiles := []string{} - memProfiles := []string{} - mutexProfiles := []string{} - - procResults := make(chan procResult) - - server, err := parallel_support.NewServer(numProcs, reporters.NewDefaultReporter(reporterConfig, formatter.ColorableStdOut)) - command.AbortIfError("Failed to start parallel spec server", err) - server.Start() - defer server.Close() - - if reporterConfig.JSONReport != "" { - reporterConfig.JSONReport = AbsPathForGeneratedAsset(reporterConfig.JSONReport, suite, cliConfig, 0) - } - if reporterConfig.JUnitReport != "" { - reporterConfig.JUnitReport = AbsPathForGeneratedAsset(reporterConfig.JUnitReport, suite, cliConfig, 0) - } - if reporterConfig.TeamcityReport != "" { - reporterConfig.TeamcityReport = AbsPathForGeneratedAsset(reporterConfig.TeamcityReport, suite, cliConfig, 0) - } - - for proc := 1; proc <= numProcs; proc++ { - procGinkgoConfig := ginkgoConfig - procGinkgoConfig.ParallelProcess, procGinkgoConfig.ParallelTotal, procGinkgoConfig.ParallelHost = proc, numProcs, server.Address() - - procGoFlagsConfig := goFlagsConfig - if goFlagsConfig.Cover { - procGoFlagsConfig.CoverProfile = AbsPathForGeneratedAsset(goFlagsConfig.CoverProfile, suite, cliConfig, proc) - coverProfiles = append(coverProfiles, procGoFlagsConfig.CoverProfile) - } - if goFlagsConfig.BlockProfile != "" { - procGoFlagsConfig.BlockProfile = AbsPathForGeneratedAsset(goFlagsConfig.BlockProfile, suite, cliConfig, proc) - blockProfiles = append(blockProfiles, procGoFlagsConfig.BlockProfile) - } - if goFlagsConfig.CPUProfile != "" { - procGoFlagsConfig.CPUProfile = AbsPathForGeneratedAsset(goFlagsConfig.CPUProfile, suite, cliConfig, proc) - cpuProfiles = append(cpuProfiles, procGoFlagsConfig.CPUProfile) - } - if goFlagsConfig.MemProfile != "" { - procGoFlagsConfig.MemProfile = AbsPathForGeneratedAsset(goFlagsConfig.MemProfile, suite, cliConfig, proc) - memProfiles = append(memProfiles, procGoFlagsConfig.MemProfile) - } - if goFlagsConfig.MutexProfile != "" { - procGoFlagsConfig.MutexProfile = AbsPathForGeneratedAsset(goFlagsConfig.MutexProfile, suite, cliConfig, proc) - mutexProfiles = append(mutexProfiles, procGoFlagsConfig.MutexProfile) - } - - args, err := types.GenerateGinkgoTestRunArgs(procGinkgoConfig, reporterConfig, procGoFlagsConfig) - command.AbortIfError("Failed to generate test run arguments", err) - args = append([]string{"--test.timeout=0"}, args...) - args = append(args, additionalArgs...) - - cmd, buf := buildAndStartCommand(suite, args, false) - procOutput[proc-1] = buf - server.RegisterAlive(proc, func() bool { return cmd.ProcessState == nil || !cmd.ProcessState.Exited() }) - - go func() { - cmd.Wait() - exitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() - procResults <- procResult{ - passed: (exitStatus == 0) || (exitStatus == types.GINKGO_FOCUS_EXIT_CODE), - hasProgrammaticFocus: exitStatus == types.GINKGO_FOCUS_EXIT_CODE, - } - }() - } - - passed := true - for proc := 1; proc <= cliConfig.ComputedProcs(); proc++ { - result := <-procResults - passed = passed && result.passed - suite.HasProgrammaticFocus = suite.HasProgrammaticFocus || result.hasProgrammaticFocus - } - if passed { - suite.State = TestSuiteStatePassed - } else { - suite.State = TestSuiteStateFailed - } - - select { - case <-server.GetSuiteDone(): - fmt.Println("") - case <-time.After(time.Second): - //one of the nodes never finished reporting to the server. Something must have gone wrong. - fmt.Fprint(formatter.ColorableStdErr, formatter.F("\n{{bold}}{{red}}Ginkgo timed out waiting for all parallel procs to report back{{/}}\n")) - fmt.Fprint(formatter.ColorableStdErr, formatter.F("{{gray}}Test suite:{{/}} %s (%s)\n\n", suite.PackageName, suite.Path)) - fmt.Fprint(formatter.ColorableStdErr, formatter.Fiw(0, formatter.COLS, "This occurs if a parallel process exits before it reports its results to the Ginkgo CLI. The CLI will now print out all the stdout/stderr output it's collected from the running processes. However you may not see anything useful in these logs because the individual test processes usually intercept output to stdout/stderr in order to capture it in the spec reports.\n\nYou may want to try rerunning your test suite with {{light-gray}}--output-interceptor-mode=none{{/}} to see additional output here and debug your suite.\n")) - fmt.Fprintln(formatter.ColorableStdErr, " ") - for proc := 1; proc <= cliConfig.ComputedProcs(); proc++ { - fmt.Fprintf(formatter.ColorableStdErr, formatter.F("{{bold}}Output from proc %d:{{/}}\n", proc)) - fmt.Fprintln(os.Stderr, formatter.Fi(1, "%s", procOutput[proc-1].String())) - } - fmt.Fprintf(os.Stderr, "** End **") - } - - for proc := 1; proc <= cliConfig.ComputedProcs(); proc++ { - output := procOutput[proc-1].String() - if proc == 1 && checkForNoTestsWarning(procOutput[0]) && cliConfig.RequireSuite { - suite.State = TestSuiteStateFailed - } - if strings.Contains(output, "deprecated Ginkgo functionality") { - fmt.Fprintln(os.Stderr, output) - } - } - - if len(coverProfiles) > 0 { - if suite.HasProgrammaticFocus { - fmt.Fprintln(os.Stdout, "coverage: no coverfile was generated because specs are programmatically focused") - } else { - coverProfile := AbsPathForGeneratedAsset(goFlagsConfig.CoverProfile, suite, cliConfig, 0) - err := MergeAndCleanupCoverProfiles(coverProfiles, coverProfile) - command.AbortIfError("Failed to combine cover profiles", err) - - coverage, err := GetCoverageFromCoverProfile(coverProfile) - command.AbortIfError("Failed to compute coverage", err) - if coverage == 0 { - fmt.Fprintln(os.Stdout, "coverage: [no statements]") - } else { - fmt.Fprintf(os.Stdout, "coverage: %.1f%% of statements\n", coverage) - } - } - } - if len(blockProfiles) > 0 { - if suite.HasProgrammaticFocus { - fmt.Fprintln(os.Stdout, "no block profile was generated because specs are programmatically focused") - } else { - blockProfile := AbsPathForGeneratedAsset(goFlagsConfig.BlockProfile, suite, cliConfig, 0) - err := MergeProfiles(blockProfiles, blockProfile) - command.AbortIfError("Failed to combine blockprofiles", err) - } - } - if len(cpuProfiles) > 0 { - if suite.HasProgrammaticFocus { - fmt.Fprintln(os.Stdout, "no cpu profile was generated because specs are programmatically focused") - } else { - cpuProfile := AbsPathForGeneratedAsset(goFlagsConfig.CPUProfile, suite, cliConfig, 0) - err := MergeProfiles(cpuProfiles, cpuProfile) - command.AbortIfError("Failed to combine cpuprofiles", err) - } - } - if len(memProfiles) > 0 { - if suite.HasProgrammaticFocus { - fmt.Fprintln(os.Stdout, "no mem profile was generated because specs are programmatically focused") - } else { - memProfile := AbsPathForGeneratedAsset(goFlagsConfig.MemProfile, suite, cliConfig, 0) - err := MergeProfiles(memProfiles, memProfile) - command.AbortIfError("Failed to combine memprofiles", err) - } - } - if len(mutexProfiles) > 0 { - if suite.HasProgrammaticFocus { - fmt.Fprintln(os.Stdout, "no mutex profile was generated because specs are programmatically focused") - } else { - mutexProfile := AbsPathForGeneratedAsset(goFlagsConfig.MutexProfile, suite, cliConfig, 0) - err := MergeProfiles(mutexProfiles, mutexProfile) - command.AbortIfError("Failed to combine mutexprofiles", err) - } - } - - return suite -} - -func runAfterRunHook(command string, noColor bool, suite TestSuite) { - if command == "" { - return - } - f := formatter.NewWithNoColorBool(noColor) - - // Allow for string replacement to pass input to the command - passed := "[FAIL]" - if suite.State.Is(TestSuiteStatePassed) { - passed = "[PASS]" - } - command = strings.ReplaceAll(command, "(ginkgo-suite-passed)", passed) - command = strings.ReplaceAll(command, "(ginkgo-suite-name)", suite.PackageName) - - // Must break command into parts - splitArgs := regexp.MustCompile(`'.+'|".+"|\S+`) - parts := splitArgs.FindAllString(command, -1) - - output, err := exec.Command(parts[0], parts[1:]...).CombinedOutput() - if err != nil { - fmt.Fprintln(formatter.ColorableStdOut, f.Fi(0, "{{red}}{{bold}}After-run-hook failed:{{/}}")) - fmt.Fprintln(formatter.ColorableStdOut, f.Fi(1, "{{red}}%s{{/}}", output)) - } else { - fmt.Fprintln(formatter.ColorableStdOut, f.Fi(0, "{{green}}{{bold}}After-run-hook succeeded:{{/}}")) - fmt.Fprintln(formatter.ColorableStdOut, f.Fi(1, "{{green}}%s{{/}}", output)) - } -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/test_suite.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/test_suite.go deleted file mode 100644 index df99875be..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/test_suite.go +++ /dev/null @@ -1,284 +0,0 @@ -package internal - -import ( - "errors" - "math/rand" - "os" - "path" - "path/filepath" - "regexp" - "runtime" - "strings" - - "github.com/onsi/ginkgo/v2/types" -) - -const TIMEOUT_ELAPSED_FAILURE_REASON = "Suite did not run because the timeout elapsed" -const PRIOR_FAILURES_FAILURE_REASON = "Suite did not run because prior suites failed and --keep-going is not set" -const EMPTY_SKIP_FAILURE_REASON = "Suite did not run go test reported that no test files were found" - -type TestSuiteState uint - -const ( - TestSuiteStateInvalid TestSuiteState = iota - - TestSuiteStateUncompiled - TestSuiteStateCompiled - - TestSuiteStatePassed - - TestSuiteStateSkippedDueToEmptyCompilation - TestSuiteStateSkippedByFilter - TestSuiteStateSkippedDueToPriorFailures - - TestSuiteStateFailed - TestSuiteStateFailedDueToTimeout - TestSuiteStateFailedToCompile -) - -var TestSuiteStateFailureStates = []TestSuiteState{TestSuiteStateFailed, TestSuiteStateFailedDueToTimeout, TestSuiteStateFailedToCompile} - -func (state TestSuiteState) Is(states ...TestSuiteState) bool { - for _, suiteState := range states { - if suiteState == state { - return true - } - } - - return false -} - -type TestSuite struct { - Path string - PackageName string - IsGinkgo bool - - Precompiled bool - PathToCompiledTest string - CompilationError error - - HasProgrammaticFocus bool - State TestSuiteState -} - -func (ts TestSuite) AbsPath() string { - path, _ := filepath.Abs(ts.Path) - return path -} - -func (ts TestSuite) NamespacedName() string { - name := relPath(ts.Path) - name = strings.TrimLeft(name, "."+string(filepath.Separator)) - name = strings.ReplaceAll(name, string(filepath.Separator), "_") - name = strings.ReplaceAll(name, " ", "_") - if name == "" { - return ts.PackageName - } - return name -} - -type TestSuites []TestSuite - -func (ts TestSuites) AnyHaveProgrammaticFocus() bool { - for _, suite := range ts { - if suite.HasProgrammaticFocus { - return true - } - } - - return false -} - -func (ts TestSuites) ThatAreGinkgoSuites() TestSuites { - out := TestSuites{} - for _, suite := range ts { - if suite.IsGinkgo { - out = append(out, suite) - } - } - return out -} - -func (ts TestSuites) CountWithState(states ...TestSuiteState) int { - n := 0 - for _, suite := range ts { - if suite.State.Is(states...) { - n += 1 - } - } - - return n -} - -func (ts TestSuites) WithState(states ...TestSuiteState) TestSuites { - out := TestSuites{} - for _, suite := range ts { - if suite.State.Is(states...) { - out = append(out, suite) - } - } - - return out -} - -func (ts TestSuites) WithoutState(states ...TestSuiteState) TestSuites { - out := TestSuites{} - for _, suite := range ts { - if !suite.State.Is(states...) { - out = append(out, suite) - } - } - - return out -} - -func (ts TestSuites) ShuffledCopy(seed int64) TestSuites { - out := make(TestSuites, len(ts)) - permutation := rand.New(rand.NewSource(seed)).Perm(len(ts)) - for i, j := range permutation { - out[i] = ts[j] - } - return out -} - -func FindSuites(args []string, cliConfig types.CLIConfig, allowPrecompiled bool) TestSuites { - suites := TestSuites{} - - if len(args) > 0 { - for _, arg := range args { - if allowPrecompiled { - suite, err := precompiledTestSuite(arg) - if err == nil { - suites = append(suites, suite) - continue - } - } - recurseForSuite := cliConfig.Recurse - if strings.HasSuffix(arg, "/...") && arg != "/..." { - arg = arg[:len(arg)-4] - recurseForSuite = true - } - suites = append(suites, suitesInDir(arg, recurseForSuite)...) - } - } else { - suites = suitesInDir(".", cliConfig.Recurse) - } - - if cliConfig.SkipPackage != "" { - skipFilters := strings.Split(cliConfig.SkipPackage, ",") - for idx := range suites { - for _, skipFilter := range skipFilters { - if strings.Contains(suites[idx].Path, skipFilter) { - suites[idx].State = TestSuiteStateSkippedByFilter - break - } - } - } - } - - return suites -} - -func precompiledTestSuite(path string) (TestSuite, error) { - info, err := os.Stat(path) - if err != nil { - return TestSuite{}, err - } - - if info.IsDir() { - return TestSuite{}, errors.New("this is a directory, not a file") - } - - if filepath.Ext(path) != ".test" && filepath.Ext(path) != ".exe" { - return TestSuite{}, errors.New("this is not a .test binary") - } - - if filepath.Ext(path) == ".test" && runtime.GOOS != "windows" && info.Mode()&0111 == 0 { - return TestSuite{}, errors.New("this is not executable") - } - - dir := relPath(filepath.Dir(path)) - packageName := strings.TrimSuffix(filepath.Base(path), ".exe") - packageName = strings.TrimSuffix(packageName, ".test") - - path, err = filepath.Abs(path) - if err != nil { - return TestSuite{}, err - } - - return TestSuite{ - Path: dir, - PackageName: packageName, - IsGinkgo: true, - Precompiled: true, - PathToCompiledTest: path, - State: TestSuiteStateCompiled, - }, nil -} - -func suitesInDir(dir string, recurse bool) TestSuites { - suites := TestSuites{} - - if path.Base(dir) == "vendor" { - return suites - } - - files, _ := os.ReadDir(dir) - re := regexp.MustCompile(`^[^._].*_test\.go$`) - for _, file := range files { - if !file.IsDir() && re.MatchString(file.Name()) { - suite := TestSuite{ - Path: relPath(dir), - PackageName: packageNameForSuite(dir), - IsGinkgo: filesHaveGinkgoSuite(dir, files), - State: TestSuiteStateUncompiled, - } - suites = append(suites, suite) - break - } - } - - if recurse { - re = regexp.MustCompile(`^[._]`) - for _, file := range files { - if file.IsDir() && !re.MatchString(file.Name()) { - suites = append(suites, suitesInDir(dir+"/"+file.Name(), recurse)...) - } - } - } - - return suites -} - -func relPath(dir string) string { - dir, _ = filepath.Abs(dir) - cwd, _ := os.Getwd() - dir, _ = filepath.Rel(cwd, filepath.Clean(dir)) - - if string(dir[0]) != "." { - dir = "." + string(filepath.Separator) + dir - } - - return dir -} - -func packageNameForSuite(dir string) string { - path, _ := filepath.Abs(dir) - return filepath.Base(path) -} - -func filesHaveGinkgoSuite(dir string, files []os.DirEntry) bool { - reTestFile := regexp.MustCompile(`_test\.go$`) - reGinkgo := regexp.MustCompile(`package ginkgo|\/ginkgo"|\/ginkgo\/v2"|\/ginkgo\/v2/dsl/`) - - for _, file := range files { - if !file.IsDir() && reTestFile.MatchString(file.Name()) { - contents, _ := os.ReadFile(dir + "/" + file.Name()) - if reGinkgo.Match(contents) { - return true - } - } - } - - return false -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/utils.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/utils.go deleted file mode 100644 index bd9ca7d51..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/utils.go +++ /dev/null @@ -1,86 +0,0 @@ -package internal - -import ( - "fmt" - "io" - "os" - "os/exec" - - "github.com/onsi/ginkgo/v2/formatter" - "github.com/onsi/ginkgo/v2/ginkgo/command" -) - -func FileExists(path string) bool { - _, err := os.Stat(path) - return err == nil -} - -func CopyFile(src string, dest string) error { - srcFile, err := os.Open(src) - if err != nil { - return err - } - - srcStat, err := srcFile.Stat() - if err != nil { - return err - } - - if _, err := os.Stat(dest); err == nil { - os.Remove(dest) - } - - destFile, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE, srcStat.Mode()) - if err != nil { - return err - } - - _, err = io.Copy(destFile, srcFile) - if err != nil { - return err - } - - if err := srcFile.Close(); err != nil { - return err - } - return destFile.Close() -} - -func GoFmt(path string) { - out, err := exec.Command("go", "fmt", path).CombinedOutput() - if err != nil { - command.AbortIfError(fmt.Sprintf("Could not fmt:\n%s\n", string(out)), err) - } -} - -func PluralizedWord(singular, plural string, count int) string { - if count == 1 { - return singular - } - return plural -} - -func FailedSuitesReport(suites TestSuites, f formatter.Formatter) string { - out := "" - out += "There were failures detected in the following suites:\n" - - maxPackageNameLength := 0 - for _, suite := range suites.WithState(TestSuiteStateFailureStates...) { - if len(suite.PackageName) > maxPackageNameLength { - maxPackageNameLength = len(suite.PackageName) - } - } - - packageNameFormatter := fmt.Sprintf("%%%ds", maxPackageNameLength) - for _, suite := range suites { - switch suite.State { - case TestSuiteStateFailed: - out += f.Fi(1, "{{red}}"+packageNameFormatter+" {{gray}}%s{{/}}\n", suite.PackageName, suite.Path) - case TestSuiteStateFailedToCompile: - out += f.Fi(1, "{{red}}"+packageNameFormatter+" {{gray}}%s {{magenta}}[Compilation failure]{{/}}\n", suite.PackageName, suite.Path) - case TestSuiteStateFailedDueToTimeout: - out += f.Fi(1, "{{red}}"+packageNameFormatter+" {{gray}}%s {{orange}}[%s]{{/}}\n", suite.PackageName, suite.Path, TIMEOUT_ELAPSED_FAILURE_REASON) - } - } - return out -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/verify_version.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/verify_version.go deleted file mode 100644 index 9da1bab3d..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/verify_version.go +++ /dev/null @@ -1,54 +0,0 @@ -package internal - -import ( - "fmt" - "os/exec" - "regexp" - "strings" - - "github.com/onsi/ginkgo/v2/formatter" - "github.com/onsi/ginkgo/v2/types" -) - -var versiorRe = regexp.MustCompile(`v(\d+\.\d+\.\d+)`) - -func VerifyCLIAndFrameworkVersion(suites TestSuites) { - cliVersion := types.VERSION - mismatches := map[string][]string{} - - for _, suite := range suites { - cmd := exec.Command("go", "list", "-m", "github.com/onsi/ginkgo/v2") - cmd.Dir = suite.Path - output, err := cmd.CombinedOutput() - if err != nil { - continue - } - components := strings.Split(string(output), " ") - if len(components) != 2 { - continue - } - matches := versiorRe.FindStringSubmatch(components[1]) - if matches == nil || len(matches) != 2 { - continue - } - libraryVersion := matches[1] - if cliVersion != libraryVersion { - mismatches[libraryVersion] = append(mismatches[libraryVersion], suite.PackageName) - } - } - - if len(mismatches) == 0 { - return - } - - fmt.Println(formatter.F("{{red}}{{bold}}Ginkgo detected a version mismatch between the Ginkgo CLI and the version of Ginkgo imported by your packages:{{/}}")) - - fmt.Println(formatter.Fi(1, "Ginkgo CLI Version:")) - fmt.Println(formatter.Fi(2, "{{bold}}%s{{/}}", cliVersion)) - fmt.Println(formatter.Fi(1, "Mismatched package versions found:")) - for version, packages := range mismatches { - fmt.Println(formatter.Fi(2, "{{bold}}%s{{/}} used by %s", version, strings.Join(packages, ", "))) - } - fmt.Println("") - fmt.Println(formatter.Fiw(1, formatter.COLS, "{{gray}}Ginkgo will continue to attempt to run but you may see errors (including flag parsing errors) and should either update your go.mod or your version of the Ginkgo CLI to match.\n\nTo install the matching version of the CLI run\n {{bold}}go install github.com/onsi/ginkgo/v2/ginkgo{{/}}{{gray}}\nfrom a path that contains a go.mod file. Alternatively you can use\n {{bold}}go run github.com/onsi/ginkgo/v2/ginkgo{{/}}{{gray}}\nfrom a path that contains a go.mod file to invoke the matching version of the Ginkgo CLI.\n\nIf you are attempting to test multiple packages that each have a different version of the Ginkgo library with a single Ginkgo CLI that is currently unsupported.\n{{/}}")) -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/labels/labels_command.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/labels/labels_command.go deleted file mode 100644 index 6c61f09d1..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/labels/labels_command.go +++ /dev/null @@ -1,123 +0,0 @@ -package labels - -import ( - "fmt" - "go/ast" - "go/parser" - "go/token" - "sort" - "strconv" - "strings" - - "github.com/onsi/ginkgo/v2/ginkgo/command" - "github.com/onsi/ginkgo/v2/ginkgo/internal" - "github.com/onsi/ginkgo/v2/types" - "golang.org/x/tools/go/ast/inspector" -) - -func BuildLabelsCommand() command.Command { - var cliConfig = types.NewDefaultCLIConfig() - - flags, err := types.BuildLabelsCommandFlagSet(&cliConfig) - if err != nil { - panic(err) - } - - return command.Command{ - Name: "labels", - Usage: "ginkgo labels ", - Flags: flags, - ShortDoc: "List labels detected in the passed-in packages (or the package in the current directory if left blank).", - DocLink: "spec-labels", - Command: func(args []string, _ []string) { - ListLabels(args, cliConfig) - }, - } -} - -func ListLabels(args []string, cliConfig types.CLIConfig) { - suites := internal.FindSuites(args, cliConfig, false).WithoutState(internal.TestSuiteStateSkippedByFilter) - if len(suites) == 0 { - command.AbortWith("Found no test suites") - } - for _, suite := range suites { - labels := fetchLabelsFromPackage(suite.Path) - if len(labels) == 0 { - fmt.Printf("%s: No labels found\n", suite.PackageName) - } else { - fmt.Printf("%s: [%s]\n", suite.PackageName, strings.Join(labels, ", ")) - } - } -} - -func fetchLabelsFromPackage(packagePath string) []string { - fset := token.NewFileSet() - parsedPackages, err := parser.ParseDir(fset, packagePath, nil, 0) - command.AbortIfError("Failed to parse package source:", err) - - files := []*ast.File{} - hasTestPackage := false - for key, pkg := range parsedPackages { - if strings.HasSuffix(key, "_test") { - hasTestPackage = true - for _, file := range pkg.Files { - files = append(files, file) - } - } - } - if !hasTestPackage { - for _, pkg := range parsedPackages { - for _, file := range pkg.Files { - files = append(files, file) - } - } - } - - seen := map[string]bool{} - labels := []string{} - ispr := inspector.New(files) - ispr.Preorder([]ast.Node{&ast.CallExpr{}}, func(n ast.Node) { - potentialLabels := fetchLabels(n.(*ast.CallExpr)) - for _, label := range potentialLabels { - if !seen[label] { - seen[label] = true - labels = append(labels, strconv.Quote(label)) - } - } - }) - - sort.Strings(labels) - return labels -} - -func fetchLabels(callExpr *ast.CallExpr) []string { - out := []string{} - switch expr := callExpr.Fun.(type) { - case *ast.Ident: - if expr.Name != "Label" { - return out - } - case *ast.SelectorExpr: - if expr.Sel.Name != "Label" { - return out - } - default: - return out - } - for _, arg := range callExpr.Args { - switch expr := arg.(type) { - case *ast.BasicLit: - if expr.Kind == token.STRING { - unquoted, err := strconv.Unquote(expr.Value) - if err != nil { - unquoted = expr.Value - } - validated, err := types.ValidateAndCleanupLabel(unquoted, types.CodeLocation{}) - if err == nil { - out = append(out, validated) - } - } - } - } - return out -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/main.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/main.go deleted file mode 100644 index e9abb27d8..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/main.go +++ /dev/null @@ -1,58 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/onsi/ginkgo/v2/ginkgo/build" - "github.com/onsi/ginkgo/v2/ginkgo/command" - "github.com/onsi/ginkgo/v2/ginkgo/generators" - "github.com/onsi/ginkgo/v2/ginkgo/labels" - "github.com/onsi/ginkgo/v2/ginkgo/outline" - "github.com/onsi/ginkgo/v2/ginkgo/run" - "github.com/onsi/ginkgo/v2/ginkgo/unfocus" - "github.com/onsi/ginkgo/v2/ginkgo/watch" - "github.com/onsi/ginkgo/v2/types" -) - -var program command.Program - -func GenerateCommands() []command.Command { - return []command.Command{ - watch.BuildWatchCommand(), - build.BuildBuildCommand(), - generators.BuildBootstrapCommand(), - generators.BuildGenerateCommand(), - labels.BuildLabelsCommand(), - outline.BuildOutlineCommand(), - unfocus.BuildUnfocusCommand(), - BuildVersionCommand(), - } -} - -func main() { - program = command.Program{ - Name: "ginkgo", - Heading: fmt.Sprintf("Ginkgo Version %s", types.VERSION), - Commands: GenerateCommands(), - DefaultCommand: run.BuildRunCommand(), - DeprecatedCommands: []command.DeprecatedCommand{ - {Name: "convert", Deprecation: types.Deprecations.Convert()}, - {Name: "blur", Deprecation: types.Deprecations.Blur()}, - {Name: "nodot", Deprecation: types.Deprecations.Nodot()}, - }, - } - - program.RunAndExit(os.Args) -} - -func BuildVersionCommand() command.Command { - return command.Command{ - Name: "version", - Usage: "ginkgo version", - ShortDoc: "Print Ginkgo's version", - Command: func(_ []string, _ []string) { - fmt.Printf("Ginkgo Version %s\n", types.VERSION) - }, - } -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go deleted file mode 100644 index 5d8d00bb1..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go +++ /dev/null @@ -1,301 +0,0 @@ -package outline - -import ( - "go/ast" - "go/token" - "strconv" - - "github.com/onsi/ginkgo/v2/types" -) - -const ( - // undefinedTextAlt is used if the spec/container text cannot be derived - undefinedTextAlt = "undefined" -) - -// ginkgoMetadata holds useful bits of information for every entry in the outline -type ginkgoMetadata struct { - // Name is the spec or container function name, e.g. `Describe` or `It` - Name string `json:"name"` - - // Text is the `text` argument passed to specs, and some containers - Text string `json:"text"` - - // Start is the position of first character of the spec or container block - Start int `json:"start"` - - // End is the position of first character immediately after the spec or container block - End int `json:"end"` - - Spec bool `json:"spec"` - Focused bool `json:"focused"` - Pending bool `json:"pending"` - Labels []string `json:"labels"` -} - -// ginkgoNode is used to construct the outline as a tree -type ginkgoNode struct { - ginkgoMetadata - Nodes []*ginkgoNode `json:"nodes"` -} - -type walkFunc func(n *ginkgoNode) - -func (n *ginkgoNode) PreOrder(f walkFunc) { - f(n) - for _, m := range n.Nodes { - m.PreOrder(f) - } -} - -func (n *ginkgoNode) PostOrder(f walkFunc) { - for _, m := range n.Nodes { - m.PostOrder(f) - } - f(n) -} - -func (n *ginkgoNode) Walk(pre, post walkFunc) { - pre(n) - for _, m := range n.Nodes { - m.Walk(pre, post) - } - post(n) -} - -// PropagateInheritedProperties propagates the Pending and Focused properties -// through the subtree rooted at n. -func (n *ginkgoNode) PropagateInheritedProperties() { - n.PreOrder(func(thisNode *ginkgoNode) { - for _, descendantNode := range thisNode.Nodes { - if thisNode.Pending { - descendantNode.Pending = true - descendantNode.Focused = false - } - if thisNode.Focused && !descendantNode.Pending { - descendantNode.Focused = true - } - } - }) -} - -// BackpropagateUnfocus propagates the Focused property through the subtree -// rooted at n. It applies the rule described in the Ginkgo docs: -// > Nested programmatically focused specs follow a simple rule: if a -// > leaf-node is marked focused, any of its ancestor nodes that are marked -// > focus will be unfocused. -func (n *ginkgoNode) BackpropagateUnfocus() { - focusedSpecInSubtreeStack := []bool{} - n.PostOrder(func(thisNode *ginkgoNode) { - if thisNode.Spec { - focusedSpecInSubtreeStack = append(focusedSpecInSubtreeStack, thisNode.Focused) - return - } - focusedSpecInSubtree := false - for range thisNode.Nodes { - focusedSpecInSubtree = focusedSpecInSubtree || focusedSpecInSubtreeStack[len(focusedSpecInSubtreeStack)-1] - focusedSpecInSubtreeStack = focusedSpecInSubtreeStack[0 : len(focusedSpecInSubtreeStack)-1] - } - focusedSpecInSubtreeStack = append(focusedSpecInSubtreeStack, focusedSpecInSubtree) - if focusedSpecInSubtree { - thisNode.Focused = false - } - }) - -} - -func packageAndIdentNamesFromCallExpr(ce *ast.CallExpr) (string, string, bool) { - switch ex := ce.Fun.(type) { - case *ast.Ident: - return "", ex.Name, true - case *ast.SelectorExpr: - pkgID, ok := ex.X.(*ast.Ident) - if !ok { - return "", "", false - } - // A package identifier is top-level, so Obj must be nil - if pkgID.Obj != nil { - return "", "", false - } - if ex.Sel == nil { - return "", "", false - } - return pkgID.Name, ex.Sel.Name, true - default: - return "", "", false - } -} - -// absoluteOffsetsForNode derives the absolute character offsets of the node start and -// end positions. -func absoluteOffsetsForNode(fset *token.FileSet, n ast.Node) (start, end int) { - return fset.PositionFor(n.Pos(), false).Offset, fset.PositionFor(n.End(), false).Offset -} - -// ginkgoNodeFromCallExpr derives an outline entry from a go AST subtree -// corresponding to a Ginkgo container or spec. -func ginkgoNodeFromCallExpr(fset *token.FileSet, ce *ast.CallExpr, ginkgoPackageName *string) (*ginkgoNode, bool) { - packageName, identName, ok := packageAndIdentNamesFromCallExpr(ce) - if !ok { - return nil, false - } - - n := ginkgoNode{} - n.Name = identName - n.Start, n.End = absoluteOffsetsForNode(fset, ce) - n.Nodes = make([]*ginkgoNode, 0) - switch identName { - case "It", "Specify", "Entry": - n.Spec = true - n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt) - n.Labels = labelFromCallExpr(ce) - n.Pending = pendingFromCallExpr(ce) - return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName - case "FIt", "FSpecify", "FEntry": - n.Spec = true - n.Focused = true - n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt) - n.Labels = labelFromCallExpr(ce) - return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName - case "PIt", "PSpecify", "XIt", "XSpecify", "PEntry", "XEntry": - n.Spec = true - n.Pending = true - n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt) - n.Labels = labelFromCallExpr(ce) - return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName - case "Context", "Describe", "When", "DescribeTable": - n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt) - n.Labels = labelFromCallExpr(ce) - n.Pending = pendingFromCallExpr(ce) - return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName - case "FContext", "FDescribe", "FWhen", "FDescribeTable": - n.Focused = true - n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt) - n.Labels = labelFromCallExpr(ce) - return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName - case "PContext", "PDescribe", "PWhen", "XContext", "XDescribe", "XWhen", "PDescribeTable", "XDescribeTable": - n.Pending = true - n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt) - n.Labels = labelFromCallExpr(ce) - return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName - case "By": - n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt) - return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName - case "AfterEach", "BeforeEach": - return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName - case "JustAfterEach", "JustBeforeEach": - return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName - case "AfterSuite", "BeforeSuite": - return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName - case "SynchronizedAfterSuite", "SynchronizedBeforeSuite": - return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName - default: - return nil, false - } -} - -// textOrAltFromCallExpr tries to derive the "text" of a Ginkgo spec or -// container. If it cannot derive it, it returns the alt text. -func textOrAltFromCallExpr(ce *ast.CallExpr, alt string) string { - text, defined := textFromCallExpr(ce) - if !defined { - return alt - } - return text -} - -// textFromCallExpr tries to derive the "text" of a Ginkgo spec or container. If -// it cannot derive it, it returns false. -func textFromCallExpr(ce *ast.CallExpr) (string, bool) { - if len(ce.Args) < 1 { - return "", false - } - text, ok := ce.Args[0].(*ast.BasicLit) - if !ok { - return "", false - } - switch text.Kind { - case token.CHAR, token.STRING: - // For token.CHAR and token.STRING, Value is quoted - unquoted, err := strconv.Unquote(text.Value) - if err != nil { - // If unquoting fails, just use the raw Value - return text.Value, true - } - return unquoted, true - default: - return text.Value, true - } -} - -func labelFromCallExpr(ce *ast.CallExpr) []string { - - labels := []string{} - if len(ce.Args) < 2 { - return labels - } - - for _, arg := range ce.Args[1:] { - switch expr := arg.(type) { - case *ast.CallExpr: - id, ok := expr.Fun.(*ast.Ident) - if !ok { - // to skip over cases where the expr.Fun. is actually *ast.SelectorExpr - continue - } - if id.Name == "Label" { - ls := extractLabels(expr) - labels = append(labels, ls...) - } - } - } - return labels -} - -func extractLabels(expr *ast.CallExpr) []string { - out := []string{} - for _, arg := range expr.Args { - switch expr := arg.(type) { - case *ast.BasicLit: - if expr.Kind == token.STRING { - unquoted, err := strconv.Unquote(expr.Value) - if err != nil { - unquoted = expr.Value - } - validated, err := types.ValidateAndCleanupLabel(unquoted, types.CodeLocation{}) - if err == nil { - out = append(out, validated) - } - } - } - } - - return out -} - -func pendingFromCallExpr(ce *ast.CallExpr) bool { - - pending := false - if len(ce.Args) < 2 { - return pending - } - - for _, arg := range ce.Args[1:] { - switch expr := arg.(type) { - case *ast.CallExpr: - id, ok := expr.Fun.(*ast.Ident) - if !ok { - // to skip over cases where the expr.Fun. is actually *ast.SelectorExpr - continue - } - if id.Name == "Pending" { - pending = true - } - case *ast.Ident: - if expr.Name == "Pending" { - pending = true - } - } - } - return pending -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/import.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/import.go deleted file mode 100644 index f0a6b5d26..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/import.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Most of the required functions were available in the -// "golang.org/x/tools/go/ast/astutil" package, but not exported. -// They were copied from https://github.com/golang/tools/blob/2b0845dc783e36ae26d683f4915a5840ef01ab0f/go/ast/astutil/imports.go - -package outline - -import ( - "go/ast" - "strconv" - "strings" -) - -// packageNameForImport returns the package name for the package. If the package -// is not imported, it returns nil. "Package name" refers to `pkgname` in the -// call expression `pkgname.ExportedIdentifier`. Examples: -// (import path not found) -> nil -// "import example.com/pkg/foo" -> "foo" -// "import fooalias example.com/pkg/foo" -> "fooalias" -// "import . example.com/pkg/foo" -> "" -func packageNameForImport(f *ast.File, path string) *string { - spec := importSpec(f, path) - if spec == nil { - return nil - } - name := spec.Name.String() - if name == "" { - name = "ginkgo" - } - if name == "." { - name = "" - } - return &name -} - -// importSpec returns the import spec if f imports path, -// or nil otherwise. -func importSpec(f *ast.File, path string) *ast.ImportSpec { - for _, s := range f.Imports { - if strings.HasPrefix(importPath(s), path) { - return s - } - } - return nil -} - -// importPath returns the unquoted import path of s, -// or "" if the path is not properly quoted. -func importPath(s *ast.ImportSpec) string { - t, err := strconv.Unquote(s.Path.Value) - if err != nil { - return "" - } - return t -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go deleted file mode 100644 index c2327cda8..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go +++ /dev/null @@ -1,110 +0,0 @@ -package outline - -import ( - "encoding/json" - "fmt" - "go/ast" - "go/token" - "strings" - - "golang.org/x/tools/go/ast/inspector" -) - -const ( - // ginkgoImportPath is the well-known ginkgo import path - ginkgoImportPath = "github.com/onsi/ginkgo/v2" -) - -// FromASTFile returns an outline for a Ginkgo test source file -func FromASTFile(fset *token.FileSet, src *ast.File) (*outline, error) { - ginkgoPackageName := packageNameForImport(src, ginkgoImportPath) - if ginkgoPackageName == nil { - return nil, fmt.Errorf("file does not import %q", ginkgoImportPath) - } - - root := ginkgoNode{} - stack := []*ginkgoNode{&root} - ispr := inspector.New([]*ast.File{src}) - ispr.Nodes([]ast.Node{(*ast.CallExpr)(nil)}, func(node ast.Node, push bool) bool { - if push { - // Pre-order traversal - ce, ok := node.(*ast.CallExpr) - if !ok { - // Because `Nodes` calls this function only when the node is an - // ast.CallExpr, this should never happen - panic(fmt.Errorf("node starting at %d, ending at %d is not an *ast.CallExpr", node.Pos(), node.End())) - } - gn, ok := ginkgoNodeFromCallExpr(fset, ce, ginkgoPackageName) - if !ok { - // Node is not a Ginkgo spec or container, continue - return true - } - parent := stack[len(stack)-1] - parent.Nodes = append(parent.Nodes, gn) - stack = append(stack, gn) - return true - } - // Post-order traversal - start, end := absoluteOffsetsForNode(fset, node) - lastVisitedGinkgoNode := stack[len(stack)-1] - if start != lastVisitedGinkgoNode.Start || end != lastVisitedGinkgoNode.End { - // Node is not a Ginkgo spec or container, so it was not pushed onto the stack, continue - return true - } - stack = stack[0 : len(stack)-1] - return true - }) - if len(root.Nodes) == 0 { - return &outline{[]*ginkgoNode{}}, nil - } - - // Derive the final focused property for all nodes. This must be done - // _before_ propagating the inherited focused property. - root.BackpropagateUnfocus() - // Now, propagate inherited properties, including focused and pending. - root.PropagateInheritedProperties() - - return &outline{root.Nodes}, nil -} - -type outline struct { - Nodes []*ginkgoNode `json:"nodes"` -} - -func (o *outline) MarshalJSON() ([]byte, error) { - return json.Marshal(o.Nodes) -} - -// String returns a CSV-formatted outline. Spec or container are output in -// depth-first order. -func (o *outline) String() string { - return o.StringIndent(0) -} - -// StringIndent returns a CSV-formated outline, but every line is indented by -// one 'width' of spaces for every level of nesting. -func (o *outline) StringIndent(width int) string { - var b strings.Builder - b.WriteString("Name,Text,Start,End,Spec,Focused,Pending,Labels\n") - - currentIndent := 0 - pre := func(n *ginkgoNode) { - b.WriteString(fmt.Sprintf("%*s", currentIndent, "")) - var labels string - if len(n.Labels) == 1 { - labels = n.Labels[0] - } else { - labels = strings.Join(n.Labels, ", ") - } - //enclosing labels in a double quoted comma separate listed so that when inmported into a CSV app the Labels column has comma separate strings - b.WriteString(fmt.Sprintf("%s,%s,%d,%d,%t,%t,%t,\"%s\"\n", n.Name, n.Text, n.Start, n.End, n.Spec, n.Focused, n.Pending, labels)) - currentIndent += width - } - post := func(n *ginkgoNode) { - currentIndent -= width - } - for _, n := range o.Nodes { - n.Walk(pre, post) - } - return b.String() -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline_command.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline_command.go deleted file mode 100644 index 36698d46a..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline_command.go +++ /dev/null @@ -1,98 +0,0 @@ -package outline - -import ( - "encoding/json" - "fmt" - "go/parser" - "go/token" - "os" - - "github.com/onsi/ginkgo/v2/ginkgo/command" - "github.com/onsi/ginkgo/v2/types" -) - -const ( - // indentWidth is the width used by the 'indent' output - indentWidth = 4 - // stdinAlias is a portable alias for stdin. This convention is used in - // other CLIs, e.g., kubectl. - stdinAlias = "-" - usageCommand = "ginkgo outline " -) - -type outlineConfig struct { - Format string -} - -func BuildOutlineCommand() command.Command { - conf := outlineConfig{ - Format: "csv", - } - flags, err := types.NewGinkgoFlagSet( - types.GinkgoFlags{ - {Name: "format", KeyPath: "Format", - Usage: "Format of outline", - UsageArgument: "one of 'csv', 'indent', or 'json'", - UsageDefaultValue: conf.Format, - }, - }, - &conf, - types.GinkgoFlagSections{}, - ) - if err != nil { - panic(err) - } - - return command.Command{ - Name: "outline", - Usage: "ginkgo outline ", - ShortDoc: "Create an outline of Ginkgo symbols for a file", - Documentation: "To read from stdin, use: `ginkgo outline -`", - DocLink: "creating-an-outline-of-specs", - Flags: flags, - Command: func(args []string, _ []string) { - outlineFile(args, conf.Format) - }, - } -} - -func outlineFile(args []string, format string) { - if len(args) != 1 { - command.AbortWithUsage("outline expects exactly one argument") - } - - filename := args[0] - var src *os.File - if filename == stdinAlias { - src = os.Stdin - } else { - var err error - src, err = os.Open(filename) - command.AbortIfError("Failed to open file:", err) - } - - fset := token.NewFileSet() - - parsedSrc, err := parser.ParseFile(fset, filename, src, 0) - command.AbortIfError("Failed to parse source:", err) - - o, err := FromASTFile(fset, parsedSrc) - command.AbortIfError("Failed to create outline:", err) - - var oerr error - switch format { - case "csv": - _, oerr = fmt.Print(o) - case "indent": - _, oerr = fmt.Print(o.StringIndent(indentWidth)) - case "json": - b, err := json.Marshal(o) - if err != nil { - println(fmt.Sprintf("error marshalling to json: %s", err)) - } - _, oerr = fmt.Println(string(b)) - default: - command.AbortWith("Format %s not accepted", format) - } - command.AbortIfError("Failed to write outline:", oerr) -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go deleted file mode 100644 index aaed4d570..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go +++ /dev/null @@ -1,232 +0,0 @@ -package run - -import ( - "fmt" - "os" - "strings" - "time" - - "github.com/onsi/ginkgo/v2/formatter" - "github.com/onsi/ginkgo/v2/ginkgo/command" - "github.com/onsi/ginkgo/v2/ginkgo/internal" - "github.com/onsi/ginkgo/v2/internal/interrupt_handler" - "github.com/onsi/ginkgo/v2/types" -) - -func BuildRunCommand() command.Command { - var suiteConfig = types.NewDefaultSuiteConfig() - var reporterConfig = types.NewDefaultReporterConfig() - var cliConfig = types.NewDefaultCLIConfig() - var goFlagsConfig = types.NewDefaultGoFlagsConfig() - - flags, err := types.BuildRunCommandFlagSet(&suiteConfig, &reporterConfig, &cliConfig, &goFlagsConfig) - if err != nil { - panic(err) - } - - interruptHandler := interrupt_handler.NewInterruptHandler(nil) - interrupt_handler.SwallowSigQuit() - - return command.Command{ - Name: "run", - Flags: flags, - Usage: "ginkgo run -- ", - ShortDoc: "Run the tests in the passed in (or the package in the current directory if left blank)", - Documentation: "Any arguments after -- will be passed to the test.", - DocLink: "running-tests", - Command: func(args []string, additionalArgs []string) { - var errors []error - cliConfig, goFlagsConfig, errors = types.VetAndInitializeCLIAndGoConfig(cliConfig, goFlagsConfig) - command.AbortIfErrors("Ginkgo detected configuration issues:", errors) - - runner := &SpecRunner{ - cliConfig: cliConfig, - goFlagsConfig: goFlagsConfig, - suiteConfig: suiteConfig, - reporterConfig: reporterConfig, - flags: flags, - - interruptHandler: interruptHandler, - } - - runner.RunSpecs(args, additionalArgs) - }, - } -} - -type SpecRunner struct { - suiteConfig types.SuiteConfig - reporterConfig types.ReporterConfig - cliConfig types.CLIConfig - goFlagsConfig types.GoFlagsConfig - flags types.GinkgoFlagSet - - interruptHandler *interrupt_handler.InterruptHandler -} - -func (r *SpecRunner) RunSpecs(args []string, additionalArgs []string) { - suites := internal.FindSuites(args, r.cliConfig, true) - skippedSuites := suites.WithState(internal.TestSuiteStateSkippedByFilter) - suites = suites.WithoutState(internal.TestSuiteStateSkippedByFilter) - - internal.VerifyCLIAndFrameworkVersion(suites) - - if len(skippedSuites) > 0 { - fmt.Println("Will skip:") - for _, skippedSuite := range skippedSuites { - fmt.Println(" " + skippedSuite.Path) - } - } - - if len(skippedSuites) > 0 && len(suites) == 0 { - command.AbortGracefullyWith("All tests skipped! Exiting...") - } - - if len(suites) == 0 { - command.AbortWith("Found no test suites") - } - - if len(suites) > 1 && !r.flags.WasSet("succinct") && r.reporterConfig.Verbosity().LT(types.VerbosityLevelVerbose) { - r.reporterConfig.Succinct = true - } - - t := time.Now() - var endTime time.Time - if r.suiteConfig.Timeout > 0 { - endTime = t.Add(r.suiteConfig.Timeout) - } - - iteration := 0 -OUTER_LOOP: - for { - if !r.flags.WasSet("seed") { - r.suiteConfig.RandomSeed = time.Now().Unix() - } - if r.cliConfig.RandomizeSuites && len(suites) > 1 { - suites = suites.ShuffledCopy(r.suiteConfig.RandomSeed) - } - - opc := internal.NewOrderedParallelCompiler(r.cliConfig.ComputedNumCompilers()) - opc.StartCompiling(suites, r.goFlagsConfig) - - SUITE_LOOP: - for { - suiteIdx, suite := opc.Next() - if suiteIdx >= len(suites) { - break SUITE_LOOP - } - suites[suiteIdx] = suite - - if r.interruptHandler.Status().Interrupted() { - opc.StopAndDrain() - break OUTER_LOOP - } - - if suites[suiteIdx].State.Is(internal.TestSuiteStateSkippedDueToEmptyCompilation) { - fmt.Printf("Skipping %s (no test files)\n", suite.Path) - continue SUITE_LOOP - } - - if suites[suiteIdx].State.Is(internal.TestSuiteStateFailedToCompile) { - fmt.Println(suites[suiteIdx].CompilationError.Error()) - if !r.cliConfig.KeepGoing { - opc.StopAndDrain() - } - continue SUITE_LOOP - } - - if suites.CountWithState(internal.TestSuiteStateFailureStates...) > 0 && !r.cliConfig.KeepGoing { - suites[suiteIdx].State = internal.TestSuiteStateSkippedDueToPriorFailures - opc.StopAndDrain() - continue SUITE_LOOP - } - - if !endTime.IsZero() { - r.suiteConfig.Timeout = endTime.Sub(time.Now()) - if r.suiteConfig.Timeout <= 0 { - suites[suiteIdx].State = internal.TestSuiteStateFailedDueToTimeout - opc.StopAndDrain() - continue SUITE_LOOP - } - } - - suites[suiteIdx] = internal.RunCompiledSuite(suites[suiteIdx], r.suiteConfig, r.reporterConfig, r.cliConfig, r.goFlagsConfig, additionalArgs) - } - - if suites.CountWithState(internal.TestSuiteStateFailureStates...) > 0 { - if iteration > 0 { - fmt.Printf("\nTests failed on attempt #%d\n\n", iteration+1) - } - break OUTER_LOOP - } - - if r.cliConfig.UntilItFails { - fmt.Printf("\nAll tests passed...\nWill keep running them until they fail.\nThis was attempt #%d\n%s\n", iteration+1, orcMessage(iteration+1)) - } else if r.cliConfig.Repeat > 0 && iteration < r.cliConfig.Repeat { - fmt.Printf("\nAll tests passed...\nThis was attempt %d of %d.\n", iteration+1, r.cliConfig.Repeat+1) - } else { - break OUTER_LOOP - } - iteration += 1 - } - - internal.Cleanup(r.goFlagsConfig, suites...) - - messages, err := internal.FinalizeProfilesAndReportsForSuites(suites, r.cliConfig, r.suiteConfig, r.reporterConfig, r.goFlagsConfig) - command.AbortIfError("could not finalize profiles:", err) - for _, message := range messages { - fmt.Println(message) - } - - fmt.Printf("\nGinkgo ran %d %s in %s\n", len(suites), internal.PluralizedWord("suite", "suites", len(suites)), time.Since(t)) - - if suites.CountWithState(internal.TestSuiteStateFailureStates...) == 0 { - if suites.AnyHaveProgrammaticFocus() && strings.TrimSpace(os.Getenv("GINKGO_EDITOR_INTEGRATION")) == "" { - fmt.Printf("Test Suite Passed\n") - fmt.Printf("Detected Programmatic Focus - setting exit status to %d\n", types.GINKGO_FOCUS_EXIT_CODE) - command.Abort(command.AbortDetails{ExitCode: types.GINKGO_FOCUS_EXIT_CODE}) - } else { - fmt.Printf("Test Suite Passed\n") - command.Abort(command.AbortDetails{}) - } - } else { - fmt.Fprintln(formatter.ColorableStdOut, "") - if len(suites) > 1 && suites.CountWithState(internal.TestSuiteStateFailureStates...) > 0 { - fmt.Fprintln(formatter.ColorableStdOut, - internal.FailedSuitesReport(suites, formatter.NewWithNoColorBool(r.reporterConfig.NoColor))) - } - fmt.Printf("Test Suite Failed\n") - command.Abort(command.AbortDetails{ExitCode: 1}) - } -} - -func orcMessage(iteration int) string { - if iteration < 10 { - return "" - } else if iteration < 30 { - return []string{ - "If at first you succeed...", - "...try, try again.", - "Looking good!", - "Still good...", - "I think your tests are fine....", - "Yep, still passing", - "Oh boy, here I go testin' again!", - "Even the gophers are getting bored", - "Did you try -race?", - "Maybe you should stop now?", - "I'm getting tired...", - "What if I just made you a sandwich?", - "Hit ^C, hit ^C, please hit ^C", - "Make it stop. Please!", - "Come on! Enough is enough!", - "Dave, this conversation can serve no purpose anymore. Goodbye.", - "Just what do you think you're doing, Dave? ", - "I, Sisyphus", - "Insanity: doing the same thing over and over again and expecting different results. -Einstein", - "I guess Einstein never tried to churn butter", - }[iteration-10] + "\n" - } else { - return "No, seriously... you can probably stop now.\n" - } -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/unfocus/unfocus_command.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/unfocus/unfocus_command.go deleted file mode 100644 index 7dd294394..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/unfocus/unfocus_command.go +++ /dev/null @@ -1,186 +0,0 @@ -package unfocus - -import ( - "bytes" - "fmt" - "go/ast" - "go/parser" - "go/token" - "io" - "os" - "path/filepath" - "strings" - "sync" - - "github.com/onsi/ginkgo/v2/ginkgo/command" -) - -func BuildUnfocusCommand() command.Command { - return command.Command{ - Name: "unfocus", - Usage: "ginkgo unfocus", - ShortDoc: "Recursively unfocus any focused tests under the current directory", - DocLink: "filtering-specs", - Command: func(_ []string, _ []string) { - unfocusSpecs() - }, - } -} - -func unfocusSpecs() { - fmt.Println("Scanning for focus...") - - goFiles := make(chan string) - go func() { - unfocusDir(goFiles, ".") - close(goFiles) - }() - - const workers = 10 - wg := sync.WaitGroup{} - wg.Add(workers) - - for i := 0; i < workers; i++ { - go func() { - for path := range goFiles { - unfocusFile(path) - } - wg.Done() - }() - } - - wg.Wait() -} - -func unfocusDir(goFiles chan string, path string) { - files, err := os.ReadDir(path) - if err != nil { - fmt.Println(err.Error()) - return - } - - for _, f := range files { - switch { - case f.IsDir() && shouldProcessDir(f.Name()): - unfocusDir(goFiles, filepath.Join(path, f.Name())) - case !f.IsDir() && shouldProcessFile(f.Name()): - goFiles <- filepath.Join(path, f.Name()) - } - } -} - -func shouldProcessDir(basename string) bool { - return basename != "vendor" && !strings.HasPrefix(basename, ".") -} - -func shouldProcessFile(basename string) bool { - return strings.HasSuffix(basename, ".go") -} - -func unfocusFile(path string) { - data, err := os.ReadFile(path) - if err != nil { - fmt.Printf("error reading file '%s': %s\n", path, err.Error()) - return - } - - ast, err := parser.ParseFile(token.NewFileSet(), path, bytes.NewReader(data), parser.ParseComments) - if err != nil { - fmt.Printf("error parsing file '%s': %s\n", path, err.Error()) - return - } - - eliminations := scanForFocus(ast) - if len(eliminations) == 0 { - return - } - - fmt.Printf("...updating %s\n", path) - backup, err := writeBackup(path, data) - if err != nil { - fmt.Printf("error creating backup file: %s\n", err.Error()) - return - } - - if err := updateFile(path, data, eliminations); err != nil { - fmt.Printf("error writing file '%s': %s\n", path, err.Error()) - return - } - - os.Remove(backup) -} - -func writeBackup(path string, data []byte) (string, error) { - t, err := os.CreateTemp(filepath.Dir(path), filepath.Base(path)) - - if err != nil { - return "", fmt.Errorf("error creating temporary file: %w", err) - } - defer t.Close() - - if _, err := io.Copy(t, bytes.NewReader(data)); err != nil { - return "", fmt.Errorf("error writing to temporary file: %w", err) - } - - return t.Name(), nil -} - -func updateFile(path string, data []byte, eliminations [][]int64) error { - to, err := os.Create(path) - if err != nil { - return fmt.Errorf("error opening file for writing '%s': %w\n", path, err) - } - defer to.Close() - - from := bytes.NewReader(data) - var cursor int64 - for _, eliminationRange := range eliminations { - positionToEliminate, lengthToEliminate := eliminationRange[0]-1, eliminationRange[1] - if _, err := io.CopyN(to, from, positionToEliminate-cursor); err != nil { - return fmt.Errorf("error copying data: %w", err) - } - - cursor = positionToEliminate + lengthToEliminate - - if _, err := from.Seek(lengthToEliminate, io.SeekCurrent); err != nil { - return fmt.Errorf("error seeking to position in buffer: %w", err) - } - } - - if _, err := io.Copy(to, from); err != nil { - return fmt.Errorf("error copying end data: %w", err) - } - - return nil -} - -func scanForFocus(file *ast.File) (eliminations [][]int64) { - ast.Inspect(file, func(n ast.Node) bool { - if c, ok := n.(*ast.CallExpr); ok { - if i, ok := c.Fun.(*ast.Ident); ok { - if isFocus(i.Name) { - eliminations = append(eliminations, []int64{int64(i.Pos()), 1}) - } - } - } - - if i, ok := n.(*ast.Ident); ok { - if i.Name == "Focus" { - eliminations = append(eliminations, []int64{int64(i.Pos()), 6}) - } - } - - return true - }) - - return eliminations -} - -func isFocus(name string) bool { - switch name { - case "FDescribe", "FContext", "FIt", "FDescribeTable", "FEntry", "FSpecify", "FWhen": - return true - default: - return false - } -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta.go deleted file mode 100644 index 6c485c5b1..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta.go +++ /dev/null @@ -1,22 +0,0 @@ -package watch - -import "sort" - -type Delta struct { - ModifiedPackages []string - - NewSuites []*Suite - RemovedSuites []*Suite - modifiedSuites []*Suite -} - -type DescendingByDelta []*Suite - -func (a DescendingByDelta) Len() int { return len(a) } -func (a DescendingByDelta) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a DescendingByDelta) Less(i, j int) bool { return a[i].Delta() > a[j].Delta() } - -func (d Delta) ModifiedSuites() []*Suite { - sort.Sort(DescendingByDelta(d.modifiedSuites)) - return d.modifiedSuites -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta_tracker.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta_tracker.go deleted file mode 100644 index 26418ac62..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta_tracker.go +++ /dev/null @@ -1,75 +0,0 @@ -package watch - -import ( - "fmt" - - "regexp" - - "github.com/onsi/ginkgo/v2/ginkgo/internal" -) - -type SuiteErrors map[internal.TestSuite]error - -type DeltaTracker struct { - maxDepth int - watchRegExp *regexp.Regexp - suites map[string]*Suite - packageHashes *PackageHashes -} - -func NewDeltaTracker(maxDepth int, watchRegExp *regexp.Regexp) *DeltaTracker { - return &DeltaTracker{ - maxDepth: maxDepth, - watchRegExp: watchRegExp, - packageHashes: NewPackageHashes(watchRegExp), - suites: map[string]*Suite{}, - } -} - -func (d *DeltaTracker) Delta(suites internal.TestSuites) (delta Delta, errors SuiteErrors) { - errors = SuiteErrors{} - delta.ModifiedPackages = d.packageHashes.CheckForChanges() - - providedSuitePaths := map[string]bool{} - for _, suite := range suites { - providedSuitePaths[suite.Path] = true - } - - d.packageHashes.StartTrackingUsage() - - for _, suite := range d.suites { - if providedSuitePaths[suite.Suite.Path] { - if suite.Delta() > 0 { - delta.modifiedSuites = append(delta.modifiedSuites, suite) - } - } else { - delta.RemovedSuites = append(delta.RemovedSuites, suite) - } - } - - d.packageHashes.StopTrackingUsageAndPrune() - - for _, suite := range suites { - _, ok := d.suites[suite.Path] - if !ok { - s, err := NewSuite(suite, d.maxDepth, d.packageHashes) - if err != nil { - errors[suite] = err - continue - } - d.suites[suite.Path] = s - delta.NewSuites = append(delta.NewSuites, s) - } - } - - return delta, errors -} - -func (d *DeltaTracker) WillRun(suite internal.TestSuite) error { - s, ok := d.suites[suite.Path] - if !ok { - return fmt.Errorf("unknown suite %s", suite.Path) - } - - return s.MarkAsRunAndRecomputedDependencies(d.maxDepth) -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/dependencies.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/dependencies.go deleted file mode 100644 index a34d94354..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/dependencies.go +++ /dev/null @@ -1,92 +0,0 @@ -package watch - -import ( - "go/build" - "regexp" -) - -var ginkgoAndGomegaFilter = regexp.MustCompile(`github\.com/onsi/ginkgo|github\.com/onsi/gomega`) -var ginkgoIntegrationTestFilter = regexp.MustCompile(`github\.com/onsi/ginkgo/integration`) //allow us to integration test this thing - -type Dependencies struct { - deps map[string]int -} - -func NewDependencies(path string, maxDepth int) (Dependencies, error) { - d := Dependencies{ - deps: map[string]int{}, - } - - if maxDepth == 0 { - return d, nil - } - - err := d.seedWithDepsForPackageAtPath(path) - if err != nil { - return d, err - } - - for depth := 1; depth < maxDepth; depth++ { - n := len(d.deps) - d.addDepsForDepth(depth) - if n == len(d.deps) { - break - } - } - - return d, nil -} - -func (d Dependencies) Dependencies() map[string]int { - return d.deps -} - -func (d Dependencies) seedWithDepsForPackageAtPath(path string) error { - pkg, err := build.ImportDir(path, 0) - if err != nil { - return err - } - - d.resolveAndAdd(pkg.Imports, 1) - d.resolveAndAdd(pkg.TestImports, 1) - d.resolveAndAdd(pkg.XTestImports, 1) - - delete(d.deps, pkg.Dir) - return nil -} - -func (d Dependencies) addDepsForDepth(depth int) { - for dep, depDepth := range d.deps { - if depDepth == depth { - d.addDepsForDep(dep, depth+1) - } - } -} - -func (d Dependencies) addDepsForDep(dep string, depth int) { - pkg, err := build.ImportDir(dep, 0) - if err != nil { - println(err.Error()) - return - } - d.resolveAndAdd(pkg.Imports, depth) -} - -func (d Dependencies) resolveAndAdd(deps []string, depth int) { - for _, dep := range deps { - pkg, err := build.Import(dep, ".", 0) - if err != nil { - continue - } - if !pkg.Goroot && (!ginkgoAndGomegaFilter.MatchString(pkg.Dir) || ginkgoIntegrationTestFilter.MatchString(pkg.Dir)) { - d.addDepIfNotPresent(pkg.Dir, depth) - } - } -} - -func (d Dependencies) addDepIfNotPresent(dep string, depth int) { - _, ok := d.deps[dep] - if !ok { - d.deps[dep] = depth - } -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go deleted file mode 100644 index 17d052bdc..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go +++ /dev/null @@ -1,108 +0,0 @@ -package watch - -import ( - "fmt" - "os" - "regexp" - "time" -) - -var goTestRegExp = regexp.MustCompile(`_test\.go$`) - -type PackageHash struct { - CodeModifiedTime time.Time - TestModifiedTime time.Time - Deleted bool - - path string - codeHash string - testHash string - watchRegExp *regexp.Regexp -} - -func NewPackageHash(path string, watchRegExp *regexp.Regexp) *PackageHash { - p := &PackageHash{ - path: path, - watchRegExp: watchRegExp, - } - - p.codeHash, _, p.testHash, _, p.Deleted = p.computeHashes() - - return p -} - -func (p *PackageHash) CheckForChanges() bool { - codeHash, codeModifiedTime, testHash, testModifiedTime, deleted := p.computeHashes() - - if deleted { - if !p.Deleted { - t := time.Now() - p.CodeModifiedTime = t - p.TestModifiedTime = t - } - p.Deleted = true - return true - } - - modified := false - p.Deleted = false - - if p.codeHash != codeHash { - p.CodeModifiedTime = codeModifiedTime - modified = true - } - if p.testHash != testHash { - p.TestModifiedTime = testModifiedTime - modified = true - } - - p.codeHash = codeHash - p.testHash = testHash - return modified -} - -func (p *PackageHash) computeHashes() (codeHash string, codeModifiedTime time.Time, testHash string, testModifiedTime time.Time, deleted bool) { - entries, err := os.ReadDir(p.path) - - if err != nil { - deleted = true - return - } - - for _, entry := range entries { - if entry.IsDir() { - continue - } - - info, err := entry.Info() - if err != nil { - continue - } - - if goTestRegExp.MatchString(info.Name()) { - testHash += p.hashForFileInfo(info) - if info.ModTime().After(testModifiedTime) { - testModifiedTime = info.ModTime() - } - continue - } - - if p.watchRegExp.MatchString(info.Name()) { - codeHash += p.hashForFileInfo(info) - if info.ModTime().After(codeModifiedTime) { - codeModifiedTime = info.ModTime() - } - } - } - - testHash += codeHash - if codeModifiedTime.After(testModifiedTime) { - testModifiedTime = codeModifiedTime - } - - return -} - -func (p *PackageHash) hashForFileInfo(info os.FileInfo) string { - return fmt.Sprintf("%s_%d_%d", info.Name(), info.Size(), info.ModTime().UnixNano()) -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hashes.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hashes.go deleted file mode 100644 index b4892bebf..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hashes.go +++ /dev/null @@ -1,85 +0,0 @@ -package watch - -import ( - "path/filepath" - "regexp" - "sync" -) - -type PackageHashes struct { - PackageHashes map[string]*PackageHash - usedPaths map[string]bool - watchRegExp *regexp.Regexp - lock *sync.Mutex -} - -func NewPackageHashes(watchRegExp *regexp.Regexp) *PackageHashes { - return &PackageHashes{ - PackageHashes: map[string]*PackageHash{}, - usedPaths: nil, - watchRegExp: watchRegExp, - lock: &sync.Mutex{}, - } -} - -func (p *PackageHashes) CheckForChanges() []string { - p.lock.Lock() - defer p.lock.Unlock() - - modified := []string{} - - for _, packageHash := range p.PackageHashes { - if packageHash.CheckForChanges() { - modified = append(modified, packageHash.path) - } - } - - return modified -} - -func (p *PackageHashes) Add(path string) *PackageHash { - p.lock.Lock() - defer p.lock.Unlock() - - path, _ = filepath.Abs(path) - _, ok := p.PackageHashes[path] - if !ok { - p.PackageHashes[path] = NewPackageHash(path, p.watchRegExp) - } - - if p.usedPaths != nil { - p.usedPaths[path] = true - } - return p.PackageHashes[path] -} - -func (p *PackageHashes) Get(path string) *PackageHash { - p.lock.Lock() - defer p.lock.Unlock() - - path, _ = filepath.Abs(path) - if p.usedPaths != nil { - p.usedPaths[path] = true - } - return p.PackageHashes[path] -} - -func (p *PackageHashes) StartTrackingUsage() { - p.lock.Lock() - defer p.lock.Unlock() - - p.usedPaths = map[string]bool{} -} - -func (p *PackageHashes) StopTrackingUsageAndPrune() { - p.lock.Lock() - defer p.lock.Unlock() - - for path := range p.PackageHashes { - if !p.usedPaths[path] { - delete(p.PackageHashes, path) - } - } - - p.usedPaths = nil -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/suite.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/suite.go deleted file mode 100644 index 53272df7e..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/suite.go +++ /dev/null @@ -1,87 +0,0 @@ -package watch - -import ( - "fmt" - "math" - "time" - - "github.com/onsi/ginkgo/v2/ginkgo/internal" -) - -type Suite struct { - Suite internal.TestSuite - RunTime time.Time - Dependencies Dependencies - - sharedPackageHashes *PackageHashes -} - -func NewSuite(suite internal.TestSuite, maxDepth int, sharedPackageHashes *PackageHashes) (*Suite, error) { - deps, err := NewDependencies(suite.Path, maxDepth) - if err != nil { - return nil, err - } - - sharedPackageHashes.Add(suite.Path) - for dep := range deps.Dependencies() { - sharedPackageHashes.Add(dep) - } - - return &Suite{ - Suite: suite, - Dependencies: deps, - - sharedPackageHashes: sharedPackageHashes, - }, nil -} - -func (s *Suite) Delta() float64 { - delta := s.delta(s.Suite.Path, true, 0) * 1000 - for dep, depth := range s.Dependencies.Dependencies() { - delta += s.delta(dep, false, depth) - } - return delta -} - -func (s *Suite) MarkAsRunAndRecomputedDependencies(maxDepth int) error { - s.RunTime = time.Now() - - deps, err := NewDependencies(s.Suite.Path, maxDepth) - if err != nil { - return err - } - - s.sharedPackageHashes.Add(s.Suite.Path) - for dep := range deps.Dependencies() { - s.sharedPackageHashes.Add(dep) - } - - s.Dependencies = deps - - return nil -} - -func (s *Suite) Description() string { - numDeps := len(s.Dependencies.Dependencies()) - pluralizer := "ies" - if numDeps == 1 { - pluralizer = "y" - } - return fmt.Sprintf("%s [%d dependenc%s]", s.Suite.Path, numDeps, pluralizer) -} - -func (s *Suite) delta(packagePath string, includeTests bool, depth int) float64 { - return math.Max(float64(s.dt(packagePath, includeTests)), 0) / float64(depth+1) -} - -func (s *Suite) dt(packagePath string, includeTests bool) time.Duration { - packageHash := s.sharedPackageHashes.Get(packagePath) - var modifiedTime time.Time - if includeTests { - modifiedTime = packageHash.TestModifiedTime - } else { - modifiedTime = packageHash.CodeModifiedTime - } - - return modifiedTime.Sub(s.RunTime) -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go deleted file mode 100644 index bde4193ce..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go +++ /dev/null @@ -1,192 +0,0 @@ -package watch - -import ( - "fmt" - "regexp" - "time" - - "github.com/onsi/ginkgo/v2/formatter" - "github.com/onsi/ginkgo/v2/ginkgo/command" - "github.com/onsi/ginkgo/v2/ginkgo/internal" - "github.com/onsi/ginkgo/v2/internal/interrupt_handler" - "github.com/onsi/ginkgo/v2/types" -) - -func BuildWatchCommand() command.Command { - var suiteConfig = types.NewDefaultSuiteConfig() - var reporterConfig = types.NewDefaultReporterConfig() - var cliConfig = types.NewDefaultCLIConfig() - var goFlagsConfig = types.NewDefaultGoFlagsConfig() - - flags, err := types.BuildWatchCommandFlagSet(&suiteConfig, &reporterConfig, &cliConfig, &goFlagsConfig) - if err != nil { - panic(err) - } - interruptHandler := interrupt_handler.NewInterruptHandler(nil) - interrupt_handler.SwallowSigQuit() - - return command.Command{ - Name: "watch", - Flags: flags, - Usage: "ginkgo watch -- ", - ShortDoc: "Watch the passed in and runs their tests whenever changes occur.", - Documentation: "Any arguments after -- will be passed to the test.", - DocLink: "watching-for-changes", - Command: func(args []string, additionalArgs []string) { - var errors []error - cliConfig, goFlagsConfig, errors = types.VetAndInitializeCLIAndGoConfig(cliConfig, goFlagsConfig) - command.AbortIfErrors("Ginkgo detected configuration issues:", errors) - - watcher := &SpecWatcher{ - cliConfig: cliConfig, - goFlagsConfig: goFlagsConfig, - suiteConfig: suiteConfig, - reporterConfig: reporterConfig, - flags: flags, - - interruptHandler: interruptHandler, - } - - watcher.WatchSpecs(args, additionalArgs) - }, - } -} - -type SpecWatcher struct { - suiteConfig types.SuiteConfig - reporterConfig types.ReporterConfig - cliConfig types.CLIConfig - goFlagsConfig types.GoFlagsConfig - flags types.GinkgoFlagSet - - interruptHandler *interrupt_handler.InterruptHandler -} - -func (w *SpecWatcher) WatchSpecs(args []string, additionalArgs []string) { - suites := internal.FindSuites(args, w.cliConfig, false).WithoutState(internal.TestSuiteStateSkippedByFilter) - - internal.VerifyCLIAndFrameworkVersion(suites) - - if len(suites) == 0 { - command.AbortWith("Found no test suites") - } - - fmt.Printf("Identified %d test %s. Locating dependencies to a depth of %d (this may take a while)...\n", len(suites), internal.PluralizedWord("suite", "suites", len(suites)), w.cliConfig.Depth) - deltaTracker := NewDeltaTracker(w.cliConfig.Depth, regexp.MustCompile(w.cliConfig.WatchRegExp)) - delta, errors := deltaTracker.Delta(suites) - - fmt.Printf("Watching %d %s:\n", len(delta.NewSuites), internal.PluralizedWord("suite", "suites", len(delta.NewSuites))) - for _, suite := range delta.NewSuites { - fmt.Println(" " + suite.Description()) - } - - for suite, err := range errors { - fmt.Printf("Failed to watch %s: %s\n", suite.PackageName, err) - } - - if len(suites) == 1 { - w.updateSeed() - w.compileAndRun(suites[0], additionalArgs) - } - - ticker := time.NewTicker(time.Second) - - for { - select { - case <-ticker.C: - suites := internal.FindSuites(args, w.cliConfig, false).WithoutState(internal.TestSuiteStateSkippedByFilter) - delta, _ := deltaTracker.Delta(suites) - coloredStream := formatter.ColorableStdOut - - suites = internal.TestSuites{} - - if len(delta.NewSuites) > 0 { - fmt.Fprintln(coloredStream, formatter.F("{{green}}Detected %d new %s:{{/}}", len(delta.NewSuites), internal.PluralizedWord("suite", "suites", len(delta.NewSuites)))) - for _, suite := range delta.NewSuites { - suites = append(suites, suite.Suite) - fmt.Fprintln(coloredStream, formatter.Fi(1, "%s", suite.Description())) - } - } - - modifiedSuites := delta.ModifiedSuites() - if len(modifiedSuites) > 0 { - fmt.Fprintln(coloredStream, formatter.F("{{green}}Detected changes in:{{/}}")) - for _, pkg := range delta.ModifiedPackages { - fmt.Fprintln(coloredStream, formatter.Fi(1, "%s", pkg)) - } - fmt.Fprintln(coloredStream, formatter.F("{{green}}Will run %d %s:{{/}}", len(modifiedSuites), internal.PluralizedWord("suite", "suites", len(modifiedSuites)))) - for _, suite := range modifiedSuites { - suites = append(suites, suite.Suite) - fmt.Fprintln(coloredStream, formatter.Fi(1, "%s", suite.Description())) - } - fmt.Fprintln(coloredStream, "") - } - - if len(suites) == 0 { - break - } - - w.updateSeed() - w.computeSuccinctMode(len(suites)) - for idx := range suites { - if w.interruptHandler.Status().Interrupted() { - return - } - deltaTracker.WillRun(suites[idx]) - suites[idx] = w.compileAndRun(suites[idx], additionalArgs) - } - color := "{{green}}" - if suites.CountWithState(internal.TestSuiteStateFailureStates...) > 0 { - color = "{{red}}" - } - fmt.Fprintln(coloredStream, formatter.F(color+"\nDone. Resuming watch...{{/}}")) - - messages, err := internal.FinalizeProfilesAndReportsForSuites(suites, w.cliConfig, w.suiteConfig, w.reporterConfig, w.goFlagsConfig) - command.AbortIfError("could not finalize profiles:", err) - for _, message := range messages { - fmt.Println(message) - } - case <-w.interruptHandler.Status().Channel: - return - } - } -} - -func (w *SpecWatcher) compileAndRun(suite internal.TestSuite, additionalArgs []string) internal.TestSuite { - suite = internal.CompileSuite(suite, w.goFlagsConfig) - if suite.State.Is(internal.TestSuiteStateFailedToCompile) { - fmt.Println(suite.CompilationError.Error()) - return suite - } - if w.interruptHandler.Status().Interrupted() { - return suite - } - suite = internal.RunCompiledSuite(suite, w.suiteConfig, w.reporterConfig, w.cliConfig, w.goFlagsConfig, additionalArgs) - internal.Cleanup(w.goFlagsConfig, suite) - return suite -} - -func (w *SpecWatcher) computeSuccinctMode(numSuites int) { - if w.reporterConfig.Verbosity().GTE(types.VerbosityLevelVerbose) { - w.reporterConfig.Succinct = false - return - } - - if w.flags.WasSet("succinct") { - return - } - - if numSuites == 1 { - w.reporterConfig.Succinct = false - } - - if numSuites > 1 { - w.reporterConfig.Succinct = true - } -} - -func (w *SpecWatcher) updateSeed() { - if !w.flags.WasSet("seed") { - w.suiteConfig.RandomSeed = time.Now().Unix() - } -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/interrupt_handler.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/interrupt_handler.go deleted file mode 100644 index 8ed86111f..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/interrupt_handler.go +++ /dev/null @@ -1,177 +0,0 @@ -package interrupt_handler - -import ( - "os" - "os/signal" - "sync" - "syscall" - "time" - - "github.com/onsi/ginkgo/v2/internal/parallel_support" -) - -var ABORT_POLLING_INTERVAL = 500 * time.Millisecond - -type InterruptCause uint - -const ( - InterruptCauseInvalid InterruptCause = iota - InterruptCauseSignal - InterruptCauseAbortByOtherProcess -) - -type InterruptLevel uint - -const ( - InterruptLevelUninterrupted InterruptLevel = iota - InterruptLevelCleanupAndReport - InterruptLevelReportOnly - InterruptLevelBailOut -) - -func (ic InterruptCause) String() string { - switch ic { - case InterruptCauseSignal: - return "Interrupted by User" - case InterruptCauseAbortByOtherProcess: - return "Interrupted by Other Ginkgo Process" - } - return "INVALID_INTERRUPT_CAUSE" -} - -type InterruptStatus struct { - Channel chan interface{} - Level InterruptLevel - Cause InterruptCause -} - -func (s InterruptStatus) Interrupted() bool { - return s.Level != InterruptLevelUninterrupted -} - -func (s InterruptStatus) Message() string { - return s.Cause.String() -} - -func (s InterruptStatus) ShouldIncludeProgressReport() bool { - return s.Cause != InterruptCauseAbortByOtherProcess -} - -type InterruptHandlerInterface interface { - Status() InterruptStatus -} - -type InterruptHandler struct { - c chan interface{} - lock *sync.Mutex - level InterruptLevel - cause InterruptCause - client parallel_support.Client - stop chan interface{} - signals []os.Signal - requestAbortCheck chan interface{} -} - -func NewInterruptHandler(client parallel_support.Client, signals ...os.Signal) *InterruptHandler { - if len(signals) == 0 { - signals = []os.Signal{os.Interrupt, syscall.SIGTERM} - } - handler := &InterruptHandler{ - c: make(chan interface{}), - lock: &sync.Mutex{}, - stop: make(chan interface{}), - requestAbortCheck: make(chan interface{}), - client: client, - signals: signals, - } - handler.registerForInterrupts() - return handler -} - -func (handler *InterruptHandler) Stop() { - close(handler.stop) -} - -func (handler *InterruptHandler) registerForInterrupts() { - // os signal handling - signalChannel := make(chan os.Signal, 1) - signal.Notify(signalChannel, handler.signals...) - - // cross-process abort handling - var abortChannel chan interface{} - if handler.client != nil { - abortChannel = make(chan interface{}) - go func() { - pollTicker := time.NewTicker(ABORT_POLLING_INTERVAL) - for { - select { - case <-pollTicker.C: - if handler.client.ShouldAbort() { - close(abortChannel) - pollTicker.Stop() - return - } - case <-handler.requestAbortCheck: - if handler.client.ShouldAbort() { - close(abortChannel) - pollTicker.Stop() - return - } - case <-handler.stop: - pollTicker.Stop() - return - } - } - }() - } - - go func(abortChannel chan interface{}) { - var interruptCause InterruptCause - for { - select { - case <-signalChannel: - interruptCause = InterruptCauseSignal - case <-abortChannel: - interruptCause = InterruptCauseAbortByOtherProcess - case <-handler.stop: - signal.Stop(signalChannel) - return - } - abortChannel = nil - - handler.lock.Lock() - oldLevel := handler.level - handler.cause = interruptCause - if handler.level == InterruptLevelUninterrupted { - handler.level = InterruptLevelCleanupAndReport - } else if handler.level == InterruptLevelCleanupAndReport { - handler.level = InterruptLevelReportOnly - } else if handler.level == InterruptLevelReportOnly { - handler.level = InterruptLevelBailOut - } - if handler.level != oldLevel { - close(handler.c) - handler.c = make(chan interface{}) - } - handler.lock.Unlock() - } - }(abortChannel) -} - -func (handler *InterruptHandler) Status() InterruptStatus { - handler.lock.Lock() - status := InterruptStatus{ - Level: handler.level, - Channel: handler.c, - Cause: handler.cause, - } - handler.lock.Unlock() - - if handler.client != nil && handler.client.ShouldAbort() && !status.Interrupted() { - close(handler.requestAbortCheck) - <-status.Channel - return handler.Status() - } - - return status -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_unix.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_unix.go deleted file mode 100644 index bf0de496d..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_unix.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build freebsd || openbsd || netbsd || dragonfly || darwin || linux || solaris -// +build freebsd openbsd netbsd dragonfly darwin linux solaris - -package interrupt_handler - -import ( - "os" - "os/signal" - "syscall" -) - -func SwallowSigQuit() { - c := make(chan os.Signal, 1024) - signal.Notify(c, syscall.SIGQUIT) -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_windows.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_windows.go deleted file mode 100644 index fcf8da833..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_windows.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build windows -// +build windows - -package interrupt_handler - -func SwallowSigQuit() { - //noop -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/client_server.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/client_server.go deleted file mode 100644 index b3cd64292..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/client_server.go +++ /dev/null @@ -1,72 +0,0 @@ -package parallel_support - -import ( - "fmt" - "io" - "os" - "time" - - "github.com/onsi/ginkgo/v2/reporters" - "github.com/onsi/ginkgo/v2/types" -) - -type BeforeSuiteState struct { - Data []byte - State types.SpecState -} - -type ParallelIndexCounter struct { - Index int -} - -var ErrorGone = fmt.Errorf("gone") -var ErrorFailed = fmt.Errorf("failed") -var ErrorEarly = fmt.Errorf("early") - -var POLLING_INTERVAL = 50 * time.Millisecond - -type Server interface { - Start() - Close() - Address() string - RegisterAlive(node int, alive func() bool) - GetSuiteDone() chan interface{} - GetOutputDestination() io.Writer - SetOutputDestination(io.Writer) -} - -type Client interface { - Connect() bool - Close() error - - PostSuiteWillBegin(report types.Report) error - PostDidRun(report types.SpecReport) error - PostSuiteDidEnd(report types.Report) error - PostReportBeforeSuiteCompleted(state types.SpecState) error - BlockUntilReportBeforeSuiteCompleted() (types.SpecState, error) - PostSynchronizedBeforeSuiteCompleted(state types.SpecState, data []byte) error - BlockUntilSynchronizedBeforeSuiteData() (types.SpecState, []byte, error) - BlockUntilNonprimaryProcsHaveFinished() error - BlockUntilAggregatedNonprimaryProcsReport() (types.Report, error) - FetchNextCounter() (int, error) - PostAbort() error - ShouldAbort() bool - PostEmitProgressReport(report types.ProgressReport) error - Write(p []byte) (int, error) -} - -func NewServer(parallelTotal int, reporter reporters.Reporter) (Server, error) { - if os.Getenv("GINKGO_PARALLEL_PROTOCOL") == "HTTP" { - return newHttpServer(parallelTotal, reporter) - } else { - return newRPCServer(parallelTotal, reporter) - } -} - -func NewClient(serverHost string) Client { - if os.Getenv("GINKGO_PARALLEL_PROTOCOL") == "HTTP" { - return newHttpClient(serverHost) - } else { - return newRPCClient(serverHost) - } -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_client.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_client.go deleted file mode 100644 index 6547c7a66..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_client.go +++ /dev/null @@ -1,169 +0,0 @@ -package parallel_support - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" - "time" - - "github.com/onsi/ginkgo/v2/types" -) - -type httpClient struct { - serverHost string -} - -func newHttpClient(serverHost string) *httpClient { - return &httpClient{ - serverHost: serverHost, - } -} - -func (client *httpClient) Connect() bool { - resp, err := http.Get(client.serverHost + "/up") - if err != nil { - return false - } - resp.Body.Close() - return resp.StatusCode == http.StatusOK -} - -func (client *httpClient) Close() error { - return nil -} - -func (client *httpClient) post(path string, data interface{}) error { - var body io.Reader - if data != nil { - encoded, err := json.Marshal(data) - if err != nil { - return err - } - body = bytes.NewBuffer(encoded) - } - resp, err := http.Post(client.serverHost+path, "application/json", body) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("received unexpected status code %d", resp.StatusCode) - } - return nil -} - -func (client *httpClient) poll(path string, data interface{}) error { - for { - resp, err := http.Get(client.serverHost + path) - if err != nil { - return err - } - if resp.StatusCode == http.StatusTooEarly { - resp.Body.Close() - time.Sleep(POLLING_INTERVAL) - continue - } - defer resp.Body.Close() - if resp.StatusCode == http.StatusGone { - return ErrorGone - } - if resp.StatusCode == http.StatusFailedDependency { - return ErrorFailed - } - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("received unexpected status code %d", resp.StatusCode) - } - if data != nil { - return json.NewDecoder(resp.Body).Decode(data) - } - return nil - } -} - -func (client *httpClient) PostSuiteWillBegin(report types.Report) error { - return client.post("/suite-will-begin", report) -} - -func (client *httpClient) PostDidRun(report types.SpecReport) error { - return client.post("/did-run", report) -} - -func (client *httpClient) PostSuiteDidEnd(report types.Report) error { - return client.post("/suite-did-end", report) -} - -func (client *httpClient) PostEmitProgressReport(report types.ProgressReport) error { - return client.post("/progress-report", report) -} - -func (client *httpClient) PostReportBeforeSuiteCompleted(state types.SpecState) error { - return client.post("/report-before-suite-completed", state) -} - -func (client *httpClient) BlockUntilReportBeforeSuiteCompleted() (types.SpecState, error) { - var state types.SpecState - err := client.poll("/report-before-suite-state", &state) - if err == ErrorGone { - return types.SpecStateFailed, nil - } - return state, err -} - -func (client *httpClient) PostSynchronizedBeforeSuiteCompleted(state types.SpecState, data []byte) error { - beforeSuiteState := BeforeSuiteState{ - State: state, - Data: data, - } - return client.post("/before-suite-completed", beforeSuiteState) -} - -func (client *httpClient) BlockUntilSynchronizedBeforeSuiteData() (types.SpecState, []byte, error) { - var beforeSuiteState BeforeSuiteState - err := client.poll("/before-suite-state", &beforeSuiteState) - if err == ErrorGone { - return types.SpecStateInvalid, nil, types.GinkgoErrors.SynchronizedBeforeSuiteDisappearedOnProc1() - } - return beforeSuiteState.State, beforeSuiteState.Data, err -} - -func (client *httpClient) BlockUntilNonprimaryProcsHaveFinished() error { - return client.poll("/have-nonprimary-procs-finished", nil) -} - -func (client *httpClient) BlockUntilAggregatedNonprimaryProcsReport() (types.Report, error) { - var report types.Report - err := client.poll("/aggregated-nonprimary-procs-report", &report) - if err == ErrorGone { - return types.Report{}, types.GinkgoErrors.AggregatedReportUnavailableDueToNodeDisappearing() - } - return report, err -} - -func (client *httpClient) FetchNextCounter() (int, error) { - var counter ParallelIndexCounter - err := client.poll("/counter", &counter) - return counter.Index, err -} - -func (client *httpClient) PostAbort() error { - return client.post("/abort", nil) -} - -func (client *httpClient) ShouldAbort() bool { - err := client.poll("/abort", nil) - if err == ErrorGone { - return true - } - return false -} - -func (client *httpClient) Write(p []byte) (int, error) { - resp, err := http.Post(client.serverHost+"/emit-output", "text/plain;charset=UTF-8 ", bytes.NewReader(p)) - resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return 0, fmt.Errorf("failed to emit output") - } - return len(p), err -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_server.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_server.go deleted file mode 100644 index d2c71ab1b..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_server.go +++ /dev/null @@ -1,242 +0,0 @@ -/* - -The remote package provides the pieces to allow Ginkgo test suites to report to remote listeners. -This is used, primarily, to enable streaming parallel test output but has, in principal, broader applications (e.g. streaming test output to a browser). - -*/ - -package parallel_support - -import ( - "encoding/json" - "io" - "net" - "net/http" - - "github.com/onsi/ginkgo/v2/reporters" - "github.com/onsi/ginkgo/v2/types" -) - -/* -httpServer spins up on an automatically selected port and listens for communication from the forwarding reporter. -It then forwards that communication to attached reporters. -*/ -type httpServer struct { - listener net.Listener - handler *ServerHandler -} - -// Create a new server, automatically selecting a port -func newHttpServer(parallelTotal int, reporter reporters.Reporter) (*httpServer, error) { - listener, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - return nil, err - } - return &httpServer{ - listener: listener, - handler: newServerHandler(parallelTotal, reporter), - }, nil -} - -// Start the server. You don't need to `go s.Start()`, just `s.Start()` -func (server *httpServer) Start() { - httpServer := &http.Server{} - mux := http.NewServeMux() - httpServer.Handler = mux - - //streaming endpoints - mux.HandleFunc("/suite-will-begin", server.specSuiteWillBegin) - mux.HandleFunc("/did-run", server.didRun) - mux.HandleFunc("/suite-did-end", server.specSuiteDidEnd) - mux.HandleFunc("/emit-output", server.emitOutput) - mux.HandleFunc("/progress-report", server.emitProgressReport) - - //synchronization endpoints - mux.HandleFunc("/report-before-suite-completed", server.handleReportBeforeSuiteCompleted) - mux.HandleFunc("/report-before-suite-state", server.handleReportBeforeSuiteState) - mux.HandleFunc("/before-suite-completed", server.handleBeforeSuiteCompleted) - mux.HandleFunc("/before-suite-state", server.handleBeforeSuiteState) - mux.HandleFunc("/have-nonprimary-procs-finished", server.handleHaveNonprimaryProcsFinished) - mux.HandleFunc("/aggregated-nonprimary-procs-report", server.handleAggregatedNonprimaryProcsReport) - mux.HandleFunc("/counter", server.handleCounter) - mux.HandleFunc("/up", server.handleUp) - mux.HandleFunc("/abort", server.handleAbort) - - go httpServer.Serve(server.listener) -} - -// Stop the server -func (server *httpServer) Close() { - server.listener.Close() -} - -// The address the server can be reached it. Pass this into the `ForwardingReporter`. -func (server *httpServer) Address() string { - return "http://" + server.listener.Addr().String() -} - -func (server *httpServer) GetSuiteDone() chan interface{} { - return server.handler.done -} - -func (server *httpServer) GetOutputDestination() io.Writer { - return server.handler.outputDestination -} - -func (server *httpServer) SetOutputDestination(w io.Writer) { - server.handler.outputDestination = w -} - -func (server *httpServer) RegisterAlive(node int, alive func() bool) { - server.handler.registerAlive(node, alive) -} - -// -// Streaming Endpoints -// - -// The server will forward all received messages to Ginkgo reporters registered with `RegisterReporters` -func (server *httpServer) decode(writer http.ResponseWriter, request *http.Request, object interface{}) bool { - defer request.Body.Close() - if json.NewDecoder(request.Body).Decode(object) != nil { - writer.WriteHeader(http.StatusBadRequest) - return false - } - return true -} - -func (server *httpServer) handleError(err error, writer http.ResponseWriter) bool { - if err == nil { - return false - } - switch err { - case ErrorEarly: - writer.WriteHeader(http.StatusTooEarly) - case ErrorGone: - writer.WriteHeader(http.StatusGone) - case ErrorFailed: - writer.WriteHeader(http.StatusFailedDependency) - default: - writer.WriteHeader(http.StatusInternalServerError) - } - return true -} - -func (server *httpServer) specSuiteWillBegin(writer http.ResponseWriter, request *http.Request) { - var report types.Report - if !server.decode(writer, request, &report) { - return - } - - server.handleError(server.handler.SpecSuiteWillBegin(report, voidReceiver), writer) -} - -func (server *httpServer) didRun(writer http.ResponseWriter, request *http.Request) { - var report types.SpecReport - if !server.decode(writer, request, &report) { - return - } - - server.handleError(server.handler.DidRun(report, voidReceiver), writer) -} - -func (server *httpServer) specSuiteDidEnd(writer http.ResponseWriter, request *http.Request) { - var report types.Report - if !server.decode(writer, request, &report) { - return - } - server.handleError(server.handler.SpecSuiteDidEnd(report, voidReceiver), writer) -} - -func (server *httpServer) emitOutput(writer http.ResponseWriter, request *http.Request) { - output, err := io.ReadAll(request.Body) - if err != nil { - writer.WriteHeader(http.StatusInternalServerError) - return - } - var n int - server.handleError(server.handler.EmitOutput(output, &n), writer) -} - -func (server *httpServer) emitProgressReport(writer http.ResponseWriter, request *http.Request) { - var report types.ProgressReport - if !server.decode(writer, request, &report) { - return - } - server.handleError(server.handler.EmitProgressReport(report, voidReceiver), writer) -} - -func (server *httpServer) handleReportBeforeSuiteCompleted(writer http.ResponseWriter, request *http.Request) { - var state types.SpecState - if !server.decode(writer, request, &state) { - return - } - - server.handleError(server.handler.ReportBeforeSuiteCompleted(state, voidReceiver), writer) -} - -func (server *httpServer) handleReportBeforeSuiteState(writer http.ResponseWriter, request *http.Request) { - var state types.SpecState - if server.handleError(server.handler.ReportBeforeSuiteState(voidSender, &state), writer) { - return - } - json.NewEncoder(writer).Encode(state) -} - -func (server *httpServer) handleBeforeSuiteCompleted(writer http.ResponseWriter, request *http.Request) { - var beforeSuiteState BeforeSuiteState - if !server.decode(writer, request, &beforeSuiteState) { - return - } - - server.handleError(server.handler.BeforeSuiteCompleted(beforeSuiteState, voidReceiver), writer) -} - -func (server *httpServer) handleBeforeSuiteState(writer http.ResponseWriter, request *http.Request) { - var beforeSuiteState BeforeSuiteState - if server.handleError(server.handler.BeforeSuiteState(voidSender, &beforeSuiteState), writer) { - return - } - json.NewEncoder(writer).Encode(beforeSuiteState) -} - -func (server *httpServer) handleHaveNonprimaryProcsFinished(writer http.ResponseWriter, request *http.Request) { - if server.handleError(server.handler.HaveNonprimaryProcsFinished(voidSender, voidReceiver), writer) { - return - } - writer.WriteHeader(http.StatusOK) -} - -func (server *httpServer) handleAggregatedNonprimaryProcsReport(writer http.ResponseWriter, request *http.Request) { - var aggregatedReport types.Report - if server.handleError(server.handler.AggregatedNonprimaryProcsReport(voidSender, &aggregatedReport), writer) { - return - } - json.NewEncoder(writer).Encode(aggregatedReport) -} - -func (server *httpServer) handleCounter(writer http.ResponseWriter, request *http.Request) { - var n int - if server.handleError(server.handler.Counter(voidSender, &n), writer) { - return - } - json.NewEncoder(writer).Encode(ParallelIndexCounter{Index: n}) -} - -func (server *httpServer) handleUp(writer http.ResponseWriter, request *http.Request) { - writer.WriteHeader(http.StatusOK) -} - -func (server *httpServer) handleAbort(writer http.ResponseWriter, request *http.Request) { - if request.Method == "GET" { - var shouldAbort bool - server.handler.ShouldAbort(voidSender, &shouldAbort) - if shouldAbort { - writer.WriteHeader(http.StatusGone) - } else { - writer.WriteHeader(http.StatusOK) - } - } else { - server.handler.Abort(voidSender, voidReceiver) - } -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_client.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_client.go deleted file mode 100644 index 59e8e6fd0..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_client.go +++ /dev/null @@ -1,136 +0,0 @@ -package parallel_support - -import ( - "net/rpc" - "time" - - "github.com/onsi/ginkgo/v2/types" -) - -type rpcClient struct { - serverHost string - client *rpc.Client -} - -func newRPCClient(serverHost string) *rpcClient { - return &rpcClient{ - serverHost: serverHost, - } -} - -func (client *rpcClient) Connect() bool { - var err error - if client.client != nil { - return true - } - client.client, err = rpc.DialHTTPPath("tcp", client.serverHost, "/") - if err != nil { - client.client = nil - return false - } - return true -} - -func (client *rpcClient) Close() error { - return client.client.Close() -} - -func (client *rpcClient) poll(method string, data interface{}) error { - for { - err := client.client.Call(method, voidSender, data) - if err == nil { - return nil - } - switch err.Error() { - case ErrorEarly.Error(): - time.Sleep(POLLING_INTERVAL) - case ErrorGone.Error(): - return ErrorGone - case ErrorFailed.Error(): - return ErrorFailed - default: - return err - } - } -} - -func (client *rpcClient) PostSuiteWillBegin(report types.Report) error { - return client.client.Call("Server.SpecSuiteWillBegin", report, voidReceiver) -} - -func (client *rpcClient) PostDidRun(report types.SpecReport) error { - return client.client.Call("Server.DidRun", report, voidReceiver) -} - -func (client *rpcClient) PostSuiteDidEnd(report types.Report) error { - return client.client.Call("Server.SpecSuiteDidEnd", report, voidReceiver) -} - -func (client *rpcClient) Write(p []byte) (int, error) { - var n int - err := client.client.Call("Server.EmitOutput", p, &n) - return n, err -} - -func (client *rpcClient) PostEmitProgressReport(report types.ProgressReport) error { - return client.client.Call("Server.EmitProgressReport", report, voidReceiver) -} - -func (client *rpcClient) PostReportBeforeSuiteCompleted(state types.SpecState) error { - return client.client.Call("Server.ReportBeforeSuiteCompleted", state, voidReceiver) -} - -func (client *rpcClient) BlockUntilReportBeforeSuiteCompleted() (types.SpecState, error) { - var state types.SpecState - err := client.poll("Server.ReportBeforeSuiteState", &state) - if err == ErrorGone { - return types.SpecStateFailed, nil - } - return state, err -} - -func (client *rpcClient) PostSynchronizedBeforeSuiteCompleted(state types.SpecState, data []byte) error { - beforeSuiteState := BeforeSuiteState{ - State: state, - Data: data, - } - return client.client.Call("Server.BeforeSuiteCompleted", beforeSuiteState, voidReceiver) -} - -func (client *rpcClient) BlockUntilSynchronizedBeforeSuiteData() (types.SpecState, []byte, error) { - var beforeSuiteState BeforeSuiteState - err := client.poll("Server.BeforeSuiteState", &beforeSuiteState) - if err == ErrorGone { - return types.SpecStateInvalid, nil, types.GinkgoErrors.SynchronizedBeforeSuiteDisappearedOnProc1() - } - return beforeSuiteState.State, beforeSuiteState.Data, err -} - -func (client *rpcClient) BlockUntilNonprimaryProcsHaveFinished() error { - return client.poll("Server.HaveNonprimaryProcsFinished", voidReceiver) -} - -func (client *rpcClient) BlockUntilAggregatedNonprimaryProcsReport() (types.Report, error) { - var report types.Report - err := client.poll("Server.AggregatedNonprimaryProcsReport", &report) - if err == ErrorGone { - return types.Report{}, types.GinkgoErrors.AggregatedReportUnavailableDueToNodeDisappearing() - } - return report, err -} - -func (client *rpcClient) FetchNextCounter() (int, error) { - var counter int - err := client.client.Call("Server.Counter", voidSender, &counter) - return counter, err -} - -func (client *rpcClient) PostAbort() error { - return client.client.Call("Server.Abort", voidSender, voidReceiver) -} - -func (client *rpcClient) ShouldAbort() bool { - var shouldAbort bool - client.client.Call("Server.ShouldAbort", voidSender, &shouldAbort) - return shouldAbort -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_server.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_server.go deleted file mode 100644 index 2620fd562..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_server.go +++ /dev/null @@ -1,75 +0,0 @@ -/* - -The remote package provides the pieces to allow Ginkgo test suites to report to remote listeners. -This is used, primarily, to enable streaming parallel test output but has, in principal, broader applications (e.g. streaming test output to a browser). - -*/ - -package parallel_support - -import ( - "io" - "net" - "net/http" - "net/rpc" - - "github.com/onsi/ginkgo/v2/reporters" -) - -/* -RPCServer spins up on an automatically selected port and listens for communication from the forwarding reporter. -It then forwards that communication to attached reporters. -*/ -type RPCServer struct { - listener net.Listener - handler *ServerHandler -} - -//Create a new server, automatically selecting a port -func newRPCServer(parallelTotal int, reporter reporters.Reporter) (*RPCServer, error) { - listener, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - return nil, err - } - return &RPCServer{ - listener: listener, - handler: newServerHandler(parallelTotal, reporter), - }, nil -} - -//Start the server. You don't need to `go s.Start()`, just `s.Start()` -func (server *RPCServer) Start() { - rpcServer := rpc.NewServer() - rpcServer.RegisterName("Server", server.handler) //register the handler's methods as the server - - httpServer := &http.Server{} - httpServer.Handler = rpcServer - - go httpServer.Serve(server.listener) -} - -//Stop the server -func (server *RPCServer) Close() { - server.listener.Close() -} - -//The address the server can be reached it. Pass this into the `ForwardingReporter`. -func (server *RPCServer) Address() string { - return server.listener.Addr().String() -} - -func (server *RPCServer) GetSuiteDone() chan interface{} { - return server.handler.done -} - -func (server *RPCServer) GetOutputDestination() io.Writer { - return server.handler.outputDestination -} - -func (server *RPCServer) SetOutputDestination(w io.Writer) { - server.handler.outputDestination = w -} - -func (server *RPCServer) RegisterAlive(node int, alive func() bool) { - server.handler.registerAlive(node, alive) -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/server_handler.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/server_handler.go deleted file mode 100644 index a6d98793e..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/server_handler.go +++ /dev/null @@ -1,234 +0,0 @@ -package parallel_support - -import ( - "io" - "os" - "sync" - - "github.com/onsi/ginkgo/v2/reporters" - "github.com/onsi/ginkgo/v2/types" -) - -type Void struct{} - -var voidReceiver *Void = &Void{} -var voidSender Void - -// ServerHandler is an RPC-compatible handler that is shared between the http server and the rpc server. -// It handles all the business logic to avoid duplication between the two servers - -type ServerHandler struct { - done chan interface{} - outputDestination io.Writer - reporter reporters.Reporter - alives []func() bool - lock *sync.Mutex - beforeSuiteState BeforeSuiteState - reportBeforeSuiteState types.SpecState - parallelTotal int - counter int - counterLock *sync.Mutex - shouldAbort bool - - numSuiteDidBegins int - numSuiteDidEnds int - aggregatedReport types.Report - reportHoldingArea []types.SpecReport -} - -func newServerHandler(parallelTotal int, reporter reporters.Reporter) *ServerHandler { - return &ServerHandler{ - reporter: reporter, - lock: &sync.Mutex{}, - counterLock: &sync.Mutex{}, - alives: make([]func() bool, parallelTotal), - beforeSuiteState: BeforeSuiteState{Data: nil, State: types.SpecStateInvalid}, - - parallelTotal: parallelTotal, - outputDestination: os.Stdout, - done: make(chan interface{}), - } -} - -func (handler *ServerHandler) SpecSuiteWillBegin(report types.Report, _ *Void) error { - handler.lock.Lock() - defer handler.lock.Unlock() - - handler.numSuiteDidBegins += 1 - - // all summaries are identical, so it's fine to simply emit the last one of these - if handler.numSuiteDidBegins == handler.parallelTotal { - handler.reporter.SuiteWillBegin(report) - - for _, summary := range handler.reportHoldingArea { - handler.reporter.WillRun(summary) - handler.reporter.DidRun(summary) - } - - handler.reportHoldingArea = nil - } - - return nil -} - -func (handler *ServerHandler) DidRun(report types.SpecReport, _ *Void) error { - handler.lock.Lock() - defer handler.lock.Unlock() - - if handler.numSuiteDidBegins == handler.parallelTotal { - handler.reporter.WillRun(report) - handler.reporter.DidRun(report) - } else { - handler.reportHoldingArea = append(handler.reportHoldingArea, report) - } - - return nil -} - -func (handler *ServerHandler) SpecSuiteDidEnd(report types.Report, _ *Void) error { - handler.lock.Lock() - defer handler.lock.Unlock() - - handler.numSuiteDidEnds += 1 - if handler.numSuiteDidEnds == 1 { - handler.aggregatedReport = report - } else { - handler.aggregatedReport = handler.aggregatedReport.Add(report) - } - - if handler.numSuiteDidEnds == handler.parallelTotal { - handler.reporter.SuiteDidEnd(handler.aggregatedReport) - close(handler.done) - } - - return nil -} - -func (handler *ServerHandler) EmitOutput(output []byte, n *int) error { - var err error - *n, err = handler.outputDestination.Write(output) - return err -} - -func (handler *ServerHandler) EmitProgressReport(report types.ProgressReport, _ *Void) error { - handler.lock.Lock() - defer handler.lock.Unlock() - handler.reporter.EmitProgressReport(report) - return nil -} - -func (handler *ServerHandler) registerAlive(proc int, alive func() bool) { - handler.lock.Lock() - defer handler.lock.Unlock() - handler.alives[proc-1] = alive -} - -func (handler *ServerHandler) procIsAlive(proc int) bool { - handler.lock.Lock() - defer handler.lock.Unlock() - alive := handler.alives[proc-1] - if alive == nil { - return true - } - return alive() -} - -func (handler *ServerHandler) haveNonprimaryProcsFinished() bool { - for i := 2; i <= handler.parallelTotal; i++ { - if handler.procIsAlive(i) { - return false - } - } - return true -} - -func (handler *ServerHandler) ReportBeforeSuiteCompleted(reportBeforeSuiteState types.SpecState, _ *Void) error { - handler.lock.Lock() - defer handler.lock.Unlock() - handler.reportBeforeSuiteState = reportBeforeSuiteState - - return nil -} - -func (handler *ServerHandler) ReportBeforeSuiteState(_ Void, reportBeforeSuiteState *types.SpecState) error { - proc1IsAlive := handler.procIsAlive(1) - handler.lock.Lock() - defer handler.lock.Unlock() - if handler.reportBeforeSuiteState == types.SpecStateInvalid { - if proc1IsAlive { - return ErrorEarly - } else { - return ErrorGone - } - } - *reportBeforeSuiteState = handler.reportBeforeSuiteState - return nil -} - -func (handler *ServerHandler) BeforeSuiteCompleted(beforeSuiteState BeforeSuiteState, _ *Void) error { - handler.lock.Lock() - defer handler.lock.Unlock() - handler.beforeSuiteState = beforeSuiteState - - return nil -} - -func (handler *ServerHandler) BeforeSuiteState(_ Void, beforeSuiteState *BeforeSuiteState) error { - proc1IsAlive := handler.procIsAlive(1) - handler.lock.Lock() - defer handler.lock.Unlock() - if handler.beforeSuiteState.State == types.SpecStateInvalid { - if proc1IsAlive { - return ErrorEarly - } else { - return ErrorGone - } - } - *beforeSuiteState = handler.beforeSuiteState - return nil -} - -func (handler *ServerHandler) HaveNonprimaryProcsFinished(_ Void, _ *Void) error { - if handler.haveNonprimaryProcsFinished() { - return nil - } else { - return ErrorEarly - } -} - -func (handler *ServerHandler) AggregatedNonprimaryProcsReport(_ Void, report *types.Report) error { - if handler.haveNonprimaryProcsFinished() { - handler.lock.Lock() - defer handler.lock.Unlock() - if handler.numSuiteDidEnds == handler.parallelTotal-1 { - *report = handler.aggregatedReport - return nil - } else { - return ErrorGone - } - } else { - return ErrorEarly - } -} - -func (handler *ServerHandler) Counter(_ Void, counter *int) error { - handler.counterLock.Lock() - defer handler.counterLock.Unlock() - *counter = handler.counter - handler.counter++ - return nil -} - -func (handler *ServerHandler) Abort(_ Void, _ *Void) error { - handler.lock.Lock() - defer handler.lock.Unlock() - handler.shouldAbort = true - return nil -} - -func (handler *ServerHandler) ShouldAbort(_ Void, shouldAbort *bool) error { - handler.lock.Lock() - defer handler.lock.Unlock() - *shouldAbort = handler.shouldAbort - return nil -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go deleted file mode 100644 index 56b7be758..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.go +++ /dev/null @@ -1,759 +0,0 @@ -/* -Ginkgo's Default Reporter - -A number of command line flags are available to tweak Ginkgo's default output. - -These are documented [here](http://onsi.github.io/ginkgo/#running_tests) -*/ -package reporters - -import ( - "fmt" - "io" - "runtime" - "strings" - "sync" - "time" - - "github.com/onsi/ginkgo/v2/formatter" - "github.com/onsi/ginkgo/v2/types" -) - -type DefaultReporter struct { - conf types.ReporterConfig - writer io.Writer - - // managing the emission stream - lastCharWasNewline bool - lastEmissionWasDelimiter bool - - // rendering - specDenoter string - retryDenoter string - formatter formatter.Formatter - - runningInParallel bool - lock *sync.Mutex -} - -func NewDefaultReporterUnderTest(conf types.ReporterConfig, writer io.Writer) *DefaultReporter { - reporter := NewDefaultReporter(conf, writer) - reporter.formatter = formatter.New(formatter.ColorModePassthrough) - - return reporter -} - -func NewDefaultReporter(conf types.ReporterConfig, writer io.Writer) *DefaultReporter { - reporter := &DefaultReporter{ - conf: conf, - writer: writer, - - lastCharWasNewline: true, - lastEmissionWasDelimiter: false, - - specDenoter: "•", - retryDenoter: "↺", - formatter: formatter.NewWithNoColorBool(conf.NoColor), - lock: &sync.Mutex{}, - } - if runtime.GOOS == "windows" { - reporter.specDenoter = "+" - reporter.retryDenoter = "R" - } - - return reporter -} - -/* The Reporter Interface */ - -func (r *DefaultReporter) SuiteWillBegin(report types.Report) { - if r.conf.Verbosity().Is(types.VerbosityLevelSuccinct) { - r.emit(r.f("[%d] {{bold}}%s{{/}} ", report.SuiteConfig.RandomSeed, report.SuiteDescription)) - if len(report.SuiteLabels) > 0 { - r.emit(r.f("{{coral}}[%s]{{/}} ", strings.Join(report.SuiteLabels, ", "))) - } - r.emit(r.f("- %d/%d specs ", report.PreRunStats.SpecsThatWillRun, report.PreRunStats.TotalSpecs)) - if report.SuiteConfig.ParallelTotal > 1 { - r.emit(r.f("- %d procs ", report.SuiteConfig.ParallelTotal)) - } - } else { - banner := r.f("Running Suite: %s - %s", report.SuiteDescription, report.SuitePath) - r.emitBlock(banner) - bannerWidth := len(banner) - if len(report.SuiteLabels) > 0 { - labels := strings.Join(report.SuiteLabels, ", ") - r.emitBlock(r.f("{{coral}}[%s]{{/}} ", labels)) - if len(labels)+2 > bannerWidth { - bannerWidth = len(labels) + 2 - } - } - r.emitBlock(strings.Repeat("=", bannerWidth)) - - out := r.f("Random Seed: {{bold}}%d{{/}}", report.SuiteConfig.RandomSeed) - if report.SuiteConfig.RandomizeAllSpecs { - out += r.f(" - will randomize all specs") - } - r.emitBlock(out) - r.emit("\n") - r.emitBlock(r.f("Will run {{bold}}%d{{/}} of {{bold}}%d{{/}} specs", report.PreRunStats.SpecsThatWillRun, report.PreRunStats.TotalSpecs)) - if report.SuiteConfig.ParallelTotal > 1 { - r.emitBlock(r.f("Running in parallel across {{bold}}%d{{/}} processes", report.SuiteConfig.ParallelTotal)) - } - } -} - -func (r *DefaultReporter) SuiteDidEnd(report types.Report) { - failures := report.SpecReports.WithState(types.SpecStateFailureStates) - if len(failures) > 0 { - r.emitBlock("\n") - if len(failures) > 1 { - r.emitBlock(r.f("{{red}}{{bold}}Summarizing %d Failures:{{/}}", len(failures))) - } else { - r.emitBlock(r.f("{{red}}{{bold}}Summarizing 1 Failure:{{/}}")) - } - for _, specReport := range failures { - highlightColor, heading := "{{red}}", "[FAIL]" - switch specReport.State { - case types.SpecStatePanicked: - highlightColor, heading = "{{magenta}}", "[PANICKED!]" - case types.SpecStateAborted: - highlightColor, heading = "{{coral}}", "[ABORTED]" - case types.SpecStateTimedout: - highlightColor, heading = "{{orange}}", "[TIMEDOUT]" - case types.SpecStateInterrupted: - highlightColor, heading = "{{orange}}", "[INTERRUPTED]" - } - locationBlock := r.codeLocationBlock(specReport, highlightColor, false, true) - r.emitBlock(r.fi(1, highlightColor+"%s{{/}} %s", heading, locationBlock)) - } - } - - //summarize the suite - if r.conf.Verbosity().Is(types.VerbosityLevelSuccinct) && report.SuiteSucceeded { - r.emit(r.f(" {{green}}SUCCESS!{{/}} %s ", report.RunTime)) - return - } - - r.emitBlock("\n") - color, status := "{{green}}{{bold}}", "SUCCESS!" - if !report.SuiteSucceeded { - color, status = "{{red}}{{bold}}", "FAIL!" - } - - specs := report.SpecReports.WithLeafNodeType(types.NodeTypeIt) //exclude any suite setup nodes - r.emitBlock(r.f(color+"Ran %d of %d Specs in %.3f seconds{{/}}", - specs.CountWithState(types.SpecStatePassed)+specs.CountWithState(types.SpecStateFailureStates), - report.PreRunStats.TotalSpecs, - report.RunTime.Seconds()), - ) - - switch len(report.SpecialSuiteFailureReasons) { - case 0: - r.emit(r.f(color+"%s{{/}} -- ", status)) - case 1: - r.emit(r.f(color+"%s - %s{{/}} -- ", status, report.SpecialSuiteFailureReasons[0])) - default: - r.emitBlock(r.f(color+"%s - %s{{/}}\n", status, strings.Join(report.SpecialSuiteFailureReasons, ", "))) - } - - if len(specs) == 0 && report.SpecReports.WithLeafNodeType(types.NodeTypeBeforeSuite|types.NodeTypeSynchronizedBeforeSuite).CountWithState(types.SpecStateFailureStates) > 0 { - r.emit(r.f("{{cyan}}{{bold}}A BeforeSuite node failed so all tests were skipped.{{/}}\n")) - } else { - r.emit(r.f("{{green}}{{bold}}%d Passed{{/}} | ", specs.CountWithState(types.SpecStatePassed))) - r.emit(r.f("{{red}}{{bold}}%d Failed{{/}} | ", specs.CountWithState(types.SpecStateFailureStates))) - if specs.CountOfFlakedSpecs() > 0 { - r.emit(r.f("{{light-yellow}}{{bold}}%d Flaked{{/}} | ", specs.CountOfFlakedSpecs())) - } - if specs.CountOfRepeatedSpecs() > 0 { - r.emit(r.f("{{light-yellow}}{{bold}}%d Repeated{{/}} | ", specs.CountOfRepeatedSpecs())) - } - r.emit(r.f("{{yellow}}{{bold}}%d Pending{{/}} | ", specs.CountWithState(types.SpecStatePending))) - r.emit(r.f("{{cyan}}{{bold}}%d Skipped{{/}}\n", specs.CountWithState(types.SpecStateSkipped))) - } -} - -func (r *DefaultReporter) WillRun(report types.SpecReport) { - v := r.conf.Verbosity() - if v.LT(types.VerbosityLevelVerbose) || report.State.Is(types.SpecStatePending|types.SpecStateSkipped) || report.RunningInParallel { - return - } - - r.emitDelimiter(0) - r.emitBlock(r.f(r.codeLocationBlock(report, "{{/}}", v.Is(types.VerbosityLevelVeryVerbose), false))) -} - -func (r *DefaultReporter) DidRun(report types.SpecReport) { - v := r.conf.Verbosity() - inParallel := report.RunningInParallel - - header := r.specDenoter - if report.LeafNodeType.Is(types.NodeTypesForSuiteLevelNodes) { - header = fmt.Sprintf("[%s]", report.LeafNodeType) - } - highlightColor := r.highlightColorForState(report.State) - - // have we already been streaming the timeline? - timelineHasBeenStreaming := v.GTE(types.VerbosityLevelVerbose) && !inParallel - - // should we show the timeline? - var timeline types.Timeline - showTimeline := !timelineHasBeenStreaming && (v.GTE(types.VerbosityLevelVerbose) || report.Failed()) - if showTimeline { - timeline = report.Timeline().WithoutHiddenReportEntries() - keepVeryVerboseSpecEvents := v.Is(types.VerbosityLevelVeryVerbose) || - (v.Is(types.VerbosityLevelVerbose) && r.conf.ShowNodeEvents) || - (report.Failed() && r.conf.ShowNodeEvents) - if !keepVeryVerboseSpecEvents { - timeline = timeline.WithoutVeryVerboseSpecEvents() - } - if len(timeline) == 0 && report.CapturedGinkgoWriterOutput == "" { - // the timeline is completely empty - don't show it - showTimeline = false - } - if v.LT(types.VerbosityLevelVeryVerbose) && report.CapturedGinkgoWriterOutput == "" && len(timeline) > 0 { - //if we aren't -vv and the timeline only has a single failure, don't show it as it will appear at the end of the report - failure, isFailure := timeline[0].(types.Failure) - if isFailure && (len(timeline) == 1 || (len(timeline) == 2 && failure.AdditionalFailure != nil)) { - showTimeline = false - } - } - } - - // should we have a separate section for always-visible reports? - showSeparateVisibilityAlwaysReportsSection := !timelineHasBeenStreaming && !showTimeline && report.ReportEntries.HasVisibility(types.ReportEntryVisibilityAlways) - - // should we have a separate section for captured stdout/stderr - showSeparateStdSection := inParallel && (report.CapturedStdOutErr != "") - - // given all that - do we have any actual content to show? or are we a single denoter in a stream? - reportHasContent := v.Is(types.VerbosityLevelVeryVerbose) || showTimeline || showSeparateVisibilityAlwaysReportsSection || showSeparateStdSection || report.Failed() || (v.Is(types.VerbosityLevelVerbose) && !report.State.Is(types.SpecStateSkipped)) - - // should we show a runtime? - includeRuntime := !report.State.Is(types.SpecStateSkipped|types.SpecStatePending) || (report.State.Is(types.SpecStateSkipped) && report.Failure.Message != "") - - // should we show the codelocation block? - showCodeLocation := !timelineHasBeenStreaming || !report.State.Is(types.SpecStatePassed) - - switch report.State { - case types.SpecStatePassed: - if report.LeafNodeType.Is(types.NodeTypesForSuiteLevelNodes) && !reportHasContent { - return - } - if report.LeafNodeType.Is(types.NodeTypesForSuiteLevelNodes) { - header = fmt.Sprintf("%s PASSED", header) - } - if report.NumAttempts > 1 && report.MaxFlakeAttempts > 1 { - header, reportHasContent = fmt.Sprintf("%s [FLAKEY TEST - TOOK %d ATTEMPTS TO PASS]", r.retryDenoter, report.NumAttempts), true - } - case types.SpecStatePending: - header = "P" - if v.GT(types.VerbosityLevelSuccinct) { - header, reportHasContent = "P [PENDING]", true - } - case types.SpecStateSkipped: - header = "S" - if v.Is(types.VerbosityLevelVeryVerbose) || (v.Is(types.VerbosityLevelVerbose) && report.Failure.Message != "") { - header, reportHasContent = "S [SKIPPED]", true - } - default: - header = fmt.Sprintf("%s [%s]", header, r.humanReadableState(report.State)) - if report.MaxMustPassRepeatedly > 1 { - header = fmt.Sprintf("%s DURING REPETITION #%d", header, report.NumAttempts) - } - } - - // If we have no content to show, jsut emit the header and return - if !reportHasContent { - r.emit(r.f(highlightColor + header + "{{/}}")) - return - } - - if includeRuntime { - header = r.f("%s [%.3f seconds]", header, report.RunTime.Seconds()) - } - - // Emit header - if !timelineHasBeenStreaming { - r.emitDelimiter(0) - } - r.emitBlock(r.f(highlightColor + header + "{{/}}")) - if showCodeLocation { - r.emitBlock(r.codeLocationBlock(report, highlightColor, v.Is(types.VerbosityLevelVeryVerbose), false)) - } - - //Emit Stdout/Stderr Output - if showSeparateStdSection { - r.emitBlock("\n") - r.emitBlock(r.fi(1, "{{gray}}Captured StdOut/StdErr Output >>{{/}}")) - r.emitBlock(r.fi(1, "%s", report.CapturedStdOutErr)) - r.emitBlock(r.fi(1, "{{gray}}<< Captured StdOut/StdErr Output{{/}}")) - } - - if showSeparateVisibilityAlwaysReportsSection { - r.emitBlock("\n") - r.emitBlock(r.fi(1, "{{gray}}Report Entries >>{{/}}")) - for _, entry := range report.ReportEntries.WithVisibility(types.ReportEntryVisibilityAlways) { - r.emitReportEntry(1, entry) - } - r.emitBlock(r.fi(1, "{{gray}}<< Report Entries{{/}}")) - } - - if showTimeline { - r.emitBlock("\n") - r.emitBlock(r.fi(1, "{{gray}}Timeline >>{{/}}")) - r.emitTimeline(1, report, timeline) - r.emitBlock(r.fi(1, "{{gray}}<< Timeline{{/}}")) - } - - // Emit Failure Message - if !report.Failure.IsZero() && !v.Is(types.VerbosityLevelVeryVerbose) { - r.emitBlock("\n") - r.emitFailure(1, report.State, report.Failure, true) - if len(report.AdditionalFailures) > 0 { - r.emitBlock(r.fi(1, "\nThere were {{bold}}{{red}}additional failures{{/}} detected. To view them in detail run {{bold}}ginkgo -vv{{/}}")) - } - } - - r.emitDelimiter(0) -} - -func (r *DefaultReporter) highlightColorForState(state types.SpecState) string { - switch state { - case types.SpecStatePassed: - return "{{green}}" - case types.SpecStatePending: - return "{{yellow}}" - case types.SpecStateSkipped: - return "{{cyan}}" - case types.SpecStateFailed: - return "{{red}}" - case types.SpecStateTimedout: - return "{{orange}}" - case types.SpecStatePanicked: - return "{{magenta}}" - case types.SpecStateInterrupted: - return "{{orange}}" - case types.SpecStateAborted: - return "{{coral}}" - default: - return "{{gray}}" - } -} - -func (r *DefaultReporter) humanReadableState(state types.SpecState) string { - return strings.ToUpper(state.String()) -} - -func (r *DefaultReporter) emitTimeline(indent uint, report types.SpecReport, timeline types.Timeline) { - isVeryVerbose := r.conf.Verbosity().Is(types.VerbosityLevelVeryVerbose) - gw := report.CapturedGinkgoWriterOutput - cursor := 0 - for _, entry := range timeline { - tl := entry.GetTimelineLocation() - if tl.Offset < len(gw) { - r.emit(r.fi(indent, "%s", gw[cursor:tl.Offset])) - cursor = tl.Offset - } else if cursor < len(gw) { - r.emit(r.fi(indent, "%s", gw[cursor:])) - cursor = len(gw) - } - switch x := entry.(type) { - case types.Failure: - if isVeryVerbose { - r.emitFailure(indent, report.State, x, false) - } else { - r.emitShortFailure(indent, report.State, x) - } - case types.AdditionalFailure: - if isVeryVerbose { - r.emitFailure(indent, x.State, x.Failure, true) - } else { - r.emitShortFailure(indent, x.State, x.Failure) - } - case types.ReportEntry: - r.emitReportEntry(indent, x) - case types.ProgressReport: - r.emitProgressReport(indent, false, x) - case types.SpecEvent: - if isVeryVerbose || !x.IsOnlyVisibleAtVeryVerbose() || r.conf.ShowNodeEvents { - r.emitSpecEvent(indent, x, isVeryVerbose) - } - } - } - if cursor < len(gw) { - r.emit(r.fi(indent, "%s", gw[cursor:])) - } -} - -func (r *DefaultReporter) EmitFailure(state types.SpecState, failure types.Failure) { - if r.conf.Verbosity().Is(types.VerbosityLevelVerbose) { - r.emitShortFailure(1, state, failure) - } else if r.conf.Verbosity().Is(types.VerbosityLevelVeryVerbose) { - r.emitFailure(1, state, failure, true) - } -} - -func (r *DefaultReporter) emitShortFailure(indent uint, state types.SpecState, failure types.Failure) { - r.emitBlock(r.fi(indent, r.highlightColorForState(state)+"[%s]{{/}} in [%s] - %s {{gray}}@ %s{{/}}", - r.humanReadableState(state), - failure.FailureNodeType, - failure.Location, - failure.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT), - )) -} - -func (r *DefaultReporter) emitFailure(indent uint, state types.SpecState, failure types.Failure, includeAdditionalFailure bool) { - highlightColor := r.highlightColorForState(state) - r.emitBlock(r.fi(indent, highlightColor+"[%s] %s{{/}}", r.humanReadableState(state), failure.Message)) - r.emitBlock(r.fi(indent, highlightColor+"In {{bold}}[%s]{{/}}"+highlightColor+" at: {{bold}}%s{{/}} {{gray}}@ %s{{/}}\n", failure.FailureNodeType, failure.Location, failure.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT))) - if failure.ForwardedPanic != "" { - r.emitBlock("\n") - r.emitBlock(r.fi(indent, highlightColor+"%s{{/}}", failure.ForwardedPanic)) - } - - if r.conf.FullTrace || failure.ForwardedPanic != "" { - r.emitBlock("\n") - r.emitBlock(r.fi(indent, highlightColor+"Full Stack Trace{{/}}")) - r.emitBlock(r.fi(indent+1, "%s", failure.Location.FullStackTrace)) - } - - if !failure.ProgressReport.IsZero() { - r.emitBlock("\n") - r.emitProgressReport(indent, false, failure.ProgressReport) - } - - if failure.AdditionalFailure != nil && includeAdditionalFailure { - r.emitBlock("\n") - r.emitFailure(indent, failure.AdditionalFailure.State, failure.AdditionalFailure.Failure, true) - } -} - -func (r *DefaultReporter) EmitProgressReport(report types.ProgressReport) { - r.emitDelimiter(1) - - if report.RunningInParallel { - r.emit(r.fi(1, "{{coral}}Progress Report for Ginkgo Process #{{bold}}%d{{/}}\n", report.ParallelProcess)) - } - shouldEmitGW := report.RunningInParallel || r.conf.Verbosity().LT(types.VerbosityLevelVerbose) - r.emitProgressReport(1, shouldEmitGW, report) - r.emitDelimiter(1) -} - -func (r *DefaultReporter) emitProgressReport(indent uint, emitGinkgoWriterOutput bool, report types.ProgressReport) { - if report.Message != "" { - r.emitBlock(r.fi(indent, report.Message+"\n")) - indent += 1 - } - if report.LeafNodeText != "" { - subjectIndent := indent - if len(report.ContainerHierarchyTexts) > 0 { - r.emit(r.fi(indent, r.cycleJoin(report.ContainerHierarchyTexts, " "))) - r.emit(" ") - subjectIndent = 0 - } - r.emit(r.fi(subjectIndent, "{{bold}}{{orange}}%s{{/}} (Spec Runtime: %s)\n", report.LeafNodeText, report.Time().Sub(report.SpecStartTime).Round(time.Millisecond))) - r.emit(r.fi(indent+1, "{{gray}}%s{{/}}\n", report.LeafNodeLocation)) - indent += 1 - } - if report.CurrentNodeType != types.NodeTypeInvalid { - r.emit(r.fi(indent, "In {{bold}}{{orange}}[%s]{{/}}", report.CurrentNodeType)) - if report.CurrentNodeText != "" && !report.CurrentNodeType.Is(types.NodeTypeIt) { - r.emit(r.f(" {{bold}}{{orange}}%s{{/}}", report.CurrentNodeText)) - } - - r.emit(r.f(" (Node Runtime: %s)\n", report.Time().Sub(report.CurrentNodeStartTime).Round(time.Millisecond))) - r.emit(r.fi(indent+1, "{{gray}}%s{{/}}\n", report.CurrentNodeLocation)) - indent += 1 - } - if report.CurrentStepText != "" { - r.emit(r.fi(indent, "At {{bold}}{{orange}}[By Step] %s{{/}} (Step Runtime: %s)\n", report.CurrentStepText, report.Time().Sub(report.CurrentStepStartTime).Round(time.Millisecond))) - r.emit(r.fi(indent+1, "{{gray}}%s{{/}}\n", report.CurrentStepLocation)) - indent += 1 - } - - if indent > 0 { - indent -= 1 - } - - if emitGinkgoWriterOutput && report.CapturedGinkgoWriterOutput != "" { - r.emit("\n") - r.emitBlock(r.fi(indent, "{{gray}}Begin Captured GinkgoWriter Output >>{{/}}")) - limit, lines := 10, strings.Split(report.CapturedGinkgoWriterOutput, "\n") - if len(lines) <= limit { - r.emitBlock(r.fi(indent+1, "%s", report.CapturedGinkgoWriterOutput)) - } else { - r.emitBlock(r.fi(indent+1, "{{gray}}...{{/}}")) - for _, line := range lines[len(lines)-limit-1:] { - r.emitBlock(r.fi(indent+1, "%s", line)) - } - } - r.emitBlock(r.fi(indent, "{{gray}}<< End Captured GinkgoWriter Output{{/}}")) - } - - if !report.SpecGoroutine().IsZero() { - r.emit("\n") - r.emit(r.fi(indent, "{{bold}}{{underline}}Spec Goroutine{{/}}\n")) - r.emitGoroutines(indent, report.SpecGoroutine()) - } - - if len(report.AdditionalReports) > 0 { - r.emit("\n") - r.emitBlock(r.fi(indent, "{{gray}}Begin Additional Progress Reports >>{{/}}")) - for i, additionalReport := range report.AdditionalReports { - r.emit(r.fi(indent+1, additionalReport)) - if i < len(report.AdditionalReports)-1 { - r.emitBlock(r.fi(indent+1, "{{gray}}%s{{/}}", strings.Repeat("-", 10))) - } - } - r.emitBlock(r.fi(indent, "{{gray}}<< End Additional Progress Reports{{/}}")) - } - - highlightedGoroutines := report.HighlightedGoroutines() - if len(highlightedGoroutines) > 0 { - r.emit("\n") - r.emit(r.fi(indent, "{{bold}}{{underline}}Goroutines of Interest{{/}}\n")) - r.emitGoroutines(indent, highlightedGoroutines...) - } - - otherGoroutines := report.OtherGoroutines() - if len(otherGoroutines) > 0 { - r.emit("\n") - r.emit(r.fi(indent, "{{gray}}{{bold}}{{underline}}Other Goroutines{{/}}\n")) - r.emitGoroutines(indent, otherGoroutines...) - } -} - -func (r *DefaultReporter) EmitReportEntry(entry types.ReportEntry) { - if r.conf.Verbosity().LT(types.VerbosityLevelVerbose) || entry.Visibility == types.ReportEntryVisibilityNever { - return - } - r.emitReportEntry(1, entry) -} - -func (r *DefaultReporter) emitReportEntry(indent uint, entry types.ReportEntry) { - r.emitBlock(r.fi(indent, "{{bold}}"+entry.Name+"{{gray}} "+fmt.Sprintf("- %s @ %s{{/}}", entry.Location, entry.Time.Format(types.GINKGO_TIME_FORMAT)))) - if representation := entry.StringRepresentation(); representation != "" { - r.emitBlock(r.fi(indent+1, representation)) - } -} - -func (r *DefaultReporter) EmitSpecEvent(event types.SpecEvent) { - v := r.conf.Verbosity() - if v.Is(types.VerbosityLevelVeryVerbose) || (v.Is(types.VerbosityLevelVerbose) && (r.conf.ShowNodeEvents || !event.IsOnlyVisibleAtVeryVerbose())) { - r.emitSpecEvent(1, event, r.conf.Verbosity().Is(types.VerbosityLevelVeryVerbose)) - } -} - -func (r *DefaultReporter) emitSpecEvent(indent uint, event types.SpecEvent, includeLocation bool) { - location := "" - if includeLocation { - location = fmt.Sprintf("- %s ", event.CodeLocation.String()) - } - switch event.SpecEventType { - case types.SpecEventInvalid: - return - case types.SpecEventByStart: - r.emitBlock(r.fi(indent, "{{bold}}STEP:{{/}} %s {{gray}}%s@ %s{{/}}", event.Message, location, event.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT))) - case types.SpecEventByEnd: - r.emitBlock(r.fi(indent, "{{bold}}END STEP:{{/}} %s {{gray}}%s@ %s (%s){{/}}", event.Message, location, event.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT), event.Duration.Round(time.Millisecond))) - case types.SpecEventNodeStart: - r.emitBlock(r.fi(indent, "> Enter {{bold}}[%s]{{/}} %s {{gray}}%s@ %s{{/}}", event.NodeType.String(), event.Message, location, event.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT))) - case types.SpecEventNodeEnd: - r.emitBlock(r.fi(indent, "< Exit {{bold}}[%s]{{/}} %s {{gray}}%s@ %s (%s){{/}}", event.NodeType.String(), event.Message, location, event.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT), event.Duration.Round(time.Millisecond))) - case types.SpecEventSpecRepeat: - r.emitBlock(r.fi(indent, "\n{{bold}}Attempt #%d {{green}}Passed{{/}}{{bold}}. Repeating %s{{/}} {{gray}}@ %s{{/}}\n\n", event.Attempt, r.retryDenoter, event.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT))) - case types.SpecEventSpecRetry: - r.emitBlock(r.fi(indent, "\n{{bold}}Attempt #%d {{red}}Failed{{/}}{{bold}}. Retrying %s{{/}} {{gray}}@ %s{{/}}\n\n", event.Attempt, r.retryDenoter, event.TimelineLocation.Time.Format(types.GINKGO_TIME_FORMAT))) - } -} - -func (r *DefaultReporter) emitGoroutines(indent uint, goroutines ...types.Goroutine) { - for idx, g := range goroutines { - color := "{{gray}}" - if g.HasHighlights() { - color = "{{orange}}" - } - r.emit(r.fi(indent, color+"goroutine %d [%s]{{/}}\n", g.ID, g.State)) - for _, fc := range g.Stack { - if fc.Highlight { - r.emit(r.fi(indent, color+"{{bold}}> %s{{/}}\n", fc.Function)) - r.emit(r.fi(indent+2, color+"{{bold}}%s:%d{{/}}\n", fc.Filename, fc.Line)) - r.emitSource(indent+3, fc) - } else { - r.emit(r.fi(indent+1, "{{gray}}%s{{/}}\n", fc.Function)) - r.emit(r.fi(indent+2, "{{gray}}%s:%d{{/}}\n", fc.Filename, fc.Line)) - } - } - - if idx+1 < len(goroutines) { - r.emit("\n") - } - } -} - -func (r *DefaultReporter) emitSource(indent uint, fc types.FunctionCall) { - lines := fc.Source - if len(lines) == 0 { - return - } - - lTrim := 100000 - for _, line := range lines { - lTrimLine := len(line) - len(strings.TrimLeft(line, " \t")) - if lTrimLine < lTrim && len(line) > 0 { - lTrim = lTrimLine - } - } - if lTrim == 100000 { - lTrim = 0 - } - - for idx, line := range lines { - if len(line) > lTrim { - line = line[lTrim:] - } - if idx == fc.SourceHighlight { - r.emit(r.fi(indent, "{{bold}}{{orange}}> %s{{/}}\n", line)) - } else { - r.emit(r.fi(indent, "| %s\n", line)) - } - } -} - -/* Emitting to the writer */ -func (r *DefaultReporter) emit(s string) { - r._emit(s, false, false) -} - -func (r *DefaultReporter) emitBlock(s string) { - r._emit(s, true, false) -} - -func (r *DefaultReporter) emitDelimiter(indent uint) { - r._emit(r.fi(indent, "{{gray}}%s{{/}}", strings.Repeat("-", 30)), true, true) -} - -// a bit ugly - but we're trying to minimize locking on this hot codepath -func (r *DefaultReporter) _emit(s string, block bool, isDelimiter bool) { - if len(s) == 0 { - return - } - r.lock.Lock() - defer r.lock.Unlock() - if isDelimiter && r.lastEmissionWasDelimiter { - return - } - if block && !r.lastCharWasNewline { - r.writer.Write([]byte("\n")) - } - r.lastCharWasNewline = (s[len(s)-1:] == "\n") - r.writer.Write([]byte(s)) - if block && !r.lastCharWasNewline { - r.writer.Write([]byte("\n")) - r.lastCharWasNewline = true - } - r.lastEmissionWasDelimiter = isDelimiter -} - -/* Rendering text */ -func (r *DefaultReporter) f(format string, args ...interface{}) string { - return r.formatter.F(format, args...) -} - -func (r *DefaultReporter) fi(indentation uint, format string, args ...interface{}) string { - return r.formatter.Fi(indentation, format, args...) -} - -func (r *DefaultReporter) cycleJoin(elements []string, joiner string) string { - return r.formatter.CycleJoin(elements, joiner, []string{"{{/}}", "{{gray}}"}) -} - -func (r *DefaultReporter) codeLocationBlock(report types.SpecReport, highlightColor string, veryVerbose bool, usePreciseFailureLocation bool) string { - texts, locations, labels := []string{}, []types.CodeLocation{}, [][]string{} - texts, locations, labels = append(texts, report.ContainerHierarchyTexts...), append(locations, report.ContainerHierarchyLocations...), append(labels, report.ContainerHierarchyLabels...) - - if report.LeafNodeType.Is(types.NodeTypesForSuiteLevelNodes) { - texts = append(texts, r.f("[%s] %s", report.LeafNodeType, report.LeafNodeText)) - } else { - texts = append(texts, r.f(report.LeafNodeText)) - } - labels = append(labels, report.LeafNodeLabels) - locations = append(locations, report.LeafNodeLocation) - - failureLocation := report.Failure.FailureNodeLocation - if usePreciseFailureLocation { - failureLocation = report.Failure.Location - } - - highlightIndex := -1 - switch report.Failure.FailureNodeContext { - case types.FailureNodeAtTopLevel: - texts = append([]string{fmt.Sprintf("TOP-LEVEL [%s]", report.Failure.FailureNodeType)}, texts...) - locations = append([]types.CodeLocation{failureLocation}, locations...) - labels = append([][]string{{}}, labels...) - highlightIndex = 0 - case types.FailureNodeInContainer: - i := report.Failure.FailureNodeContainerIndex - texts[i] = fmt.Sprintf("%s [%s]", texts[i], report.Failure.FailureNodeType) - locations[i] = failureLocation - highlightIndex = i - case types.FailureNodeIsLeafNode: - i := len(texts) - 1 - texts[i] = fmt.Sprintf("[%s] %s", report.LeafNodeType, report.LeafNodeText) - locations[i] = failureLocation - highlightIndex = i - default: - //there is no failure, so we highlight the leaf ndoe - highlightIndex = len(texts) - 1 - } - - out := "" - if veryVerbose { - for i := range texts { - if i == highlightIndex { - out += r.fi(uint(i), highlightColor+"{{bold}}%s{{/}}", texts[i]) - } else { - out += r.fi(uint(i), "%s", texts[i]) - } - if len(labels[i]) > 0 { - out += r.f(" {{coral}}[%s]{{/}}", strings.Join(labels[i], ", ")) - } - out += "\n" - out += r.fi(uint(i), "{{gray}}%s{{/}}\n", locations[i]) - } - } else { - for i := range texts { - style := "{{/}}" - if i%2 == 1 { - style = "{{gray}}" - } - if i == highlightIndex { - style = highlightColor + "{{bold}}" - } - out += r.f(style+"%s", texts[i]) - if i < len(texts)-1 { - out += " " - } else { - out += r.f("{{/}}") - } - } - flattenedLabels := report.Labels() - if len(flattenedLabels) > 0 { - out += r.f(" {{coral}}[%s]{{/}}", strings.Join(flattenedLabels, ", ")) - } - out += "\n" - if usePreciseFailureLocation { - out += r.f("{{gray}}%s{{/}}", failureLocation) - } else { - leafLocation := locations[len(locations)-1] - if (report.Failure.FailureNodeLocation != types.CodeLocation{}) && (report.Failure.FailureNodeLocation != leafLocation) { - out += r.fi(1, highlightColor+"[%s]{{/}} {{gray}}%s{{/}}\n", report.Failure.FailureNodeType, report.Failure.FailureNodeLocation) - out += r.fi(1, "{{gray}}[%s] %s{{/}}", report.LeafNodeType, leafLocation) - } else { - out += r.f("{{gray}}%s{{/}}", leafLocation) - } - } - - } - return out -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/deprecated_reporter.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/deprecated_reporter.go deleted file mode 100644 index 613072ebf..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/deprecated_reporter.go +++ /dev/null @@ -1,149 +0,0 @@ -package reporters - -import ( - "github.com/onsi/ginkgo/v2/config" - "github.com/onsi/ginkgo/v2/types" -) - -// Deprecated: DeprecatedReporter was how Ginkgo V1 provided support for CustomReporters -// this has been removed in V2. -// Please read the documentation at: -// https://onsi.github.io/ginkgo/MIGRATING_TO_V2#removed-custom-reporters -// for Ginkgo's new behavior and for a migration path. -type DeprecatedReporter interface { - SuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) - BeforeSuiteDidRun(setupSummary *types.SetupSummary) - SpecWillRun(specSummary *types.SpecSummary) - SpecDidComplete(specSummary *types.SpecSummary) - AfterSuiteDidRun(setupSummary *types.SetupSummary) - SuiteDidEnd(summary *types.SuiteSummary) -} - -// ReportViaDeprecatedReporter takes a V1 custom reporter and a V2 report and -// calls the custom reporter's methods with appropriately transformed data from the V2 report. -// -// ReportViaDeprecatedReporter should be called in a `ReportAfterSuite()` -// -// Deprecated: ReportViaDeprecatedReporter method exists to help developer bridge between deprecated V1 functionality and the new -// reporting support in V2. It will be removed in a future minor version of Ginkgo. -func ReportViaDeprecatedReporter(reporter DeprecatedReporter, report types.Report) { - conf := config.DeprecatedGinkgoConfigType{ - RandomSeed: report.SuiteConfig.RandomSeed, - RandomizeAllSpecs: report.SuiteConfig.RandomizeAllSpecs, - FocusStrings: report.SuiteConfig.FocusStrings, - SkipStrings: report.SuiteConfig.SkipStrings, - FailOnPending: report.SuiteConfig.FailOnPending, - FailFast: report.SuiteConfig.FailFast, - FlakeAttempts: report.SuiteConfig.FlakeAttempts, - EmitSpecProgress: false, - DryRun: report.SuiteConfig.DryRun, - ParallelNode: report.SuiteConfig.ParallelProcess, - ParallelTotal: report.SuiteConfig.ParallelTotal, - SyncHost: report.SuiteConfig.ParallelHost, - StreamHost: report.SuiteConfig.ParallelHost, - } - - summary := &types.DeprecatedSuiteSummary{ - SuiteDescription: report.SuiteDescription, - SuiteID: report.SuitePath, - - NumberOfSpecsBeforeParallelization: report.PreRunStats.TotalSpecs, - NumberOfTotalSpecs: report.PreRunStats.TotalSpecs, - NumberOfSpecsThatWillBeRun: report.PreRunStats.SpecsThatWillRun, - } - - reporter.SuiteWillBegin(conf, summary) - - for _, spec := range report.SpecReports { - switch spec.LeafNodeType { - case types.NodeTypeBeforeSuite, types.NodeTypeSynchronizedBeforeSuite: - setupSummary := &types.DeprecatedSetupSummary{ - ComponentType: spec.LeafNodeType, - CodeLocation: spec.LeafNodeLocation, - State: spec.State, - RunTime: spec.RunTime, - Failure: failureFor(spec), - CapturedOutput: spec.CombinedOutput(), - SuiteID: report.SuitePath, - } - reporter.BeforeSuiteDidRun(setupSummary) - case types.NodeTypeAfterSuite, types.NodeTypeSynchronizedAfterSuite: - setupSummary := &types.DeprecatedSetupSummary{ - ComponentType: spec.LeafNodeType, - CodeLocation: spec.LeafNodeLocation, - State: spec.State, - RunTime: spec.RunTime, - Failure: failureFor(spec), - CapturedOutput: spec.CombinedOutput(), - SuiteID: report.SuitePath, - } - reporter.AfterSuiteDidRun(setupSummary) - case types.NodeTypeIt: - componentTexts, componentCodeLocations := []string{}, []types.CodeLocation{} - componentTexts = append(componentTexts, spec.ContainerHierarchyTexts...) - componentCodeLocations = append(componentCodeLocations, spec.ContainerHierarchyLocations...) - componentTexts = append(componentTexts, spec.LeafNodeText) - componentCodeLocations = append(componentCodeLocations, spec.LeafNodeLocation) - - specSummary := &types.DeprecatedSpecSummary{ - ComponentTexts: componentTexts, - ComponentCodeLocations: componentCodeLocations, - State: spec.State, - RunTime: spec.RunTime, - Failure: failureFor(spec), - NumberOfSamples: spec.NumAttempts, - CapturedOutput: spec.CombinedOutput(), - SuiteID: report.SuitePath, - } - reporter.SpecWillRun(specSummary) - reporter.SpecDidComplete(specSummary) - - switch spec.State { - case types.SpecStatePending: - summary.NumberOfPendingSpecs += 1 - case types.SpecStateSkipped: - summary.NumberOfSkippedSpecs += 1 - case types.SpecStateFailed, types.SpecStatePanicked, types.SpecStateInterrupted: - summary.NumberOfFailedSpecs += 1 - case types.SpecStatePassed: - summary.NumberOfPassedSpecs += 1 - if spec.NumAttempts > 1 { - summary.NumberOfFlakedSpecs += 1 - } - } - } - } - - summary.SuiteSucceeded = report.SuiteSucceeded - summary.RunTime = report.RunTime - - reporter.SuiteDidEnd(summary) -} - -func failureFor(spec types.SpecReport) types.DeprecatedSpecFailure { - if spec.Failure.IsZero() { - return types.DeprecatedSpecFailure{} - } - - index := 0 - switch spec.Failure.FailureNodeContext { - case types.FailureNodeInContainer: - index = spec.Failure.FailureNodeContainerIndex - case types.FailureNodeAtTopLevel: - index = -1 - case types.FailureNodeIsLeafNode: - index = len(spec.ContainerHierarchyTexts) - 1 - if spec.LeafNodeText != "" { - index += 1 - } - } - - return types.DeprecatedSpecFailure{ - Message: spec.Failure.Message, - Location: spec.Failure.Location, - ForwardedPanic: spec.Failure.ForwardedPanic, - ComponentIndex: index, - ComponentType: spec.Failure.FailureNodeType, - ComponentCodeLocation: spec.Failure.FailureNodeLocation, - } -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/json_report.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/json_report.go deleted file mode 100644 index 5d3e8db99..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/json_report.go +++ /dev/null @@ -1,69 +0,0 @@ -package reporters - -import ( - "encoding/json" - "fmt" - "os" - "path" - - "github.com/onsi/ginkgo/v2/types" -) - -// GenerateJSONReport produces a JSON-formatted report at the passed in destination -func GenerateJSONReport(report types.Report, destination string) error { - if err := os.MkdirAll(path.Dir(destination), 0770); err != nil { - return err - } - f, err := os.Create(destination) - if err != nil { - return err - } - defer f.Close() - enc := json.NewEncoder(f) - enc.SetIndent("", " ") - err = enc.Encode([]types.Report{ - report, - }) - if err != nil { - return err - } - return nil -} - -// MergeJSONReports produces a single JSON-formatted report at the passed in destination by merging the JSON-formatted reports provided in sources -// It skips over reports that fail to decode but reports on them via the returned messages []string -func MergeAndCleanupJSONReports(sources []string, destination string) ([]string, error) { - messages := []string{} - allReports := []types.Report{} - for _, source := range sources { - reports := []types.Report{} - data, err := os.ReadFile(source) - if err != nil { - messages = append(messages, fmt.Sprintf("Could not open %s:\n%s", source, err.Error())) - continue - } - err = json.Unmarshal(data, &reports) - if err != nil { - messages = append(messages, fmt.Sprintf("Could not decode %s:\n%s", source, err.Error())) - continue - } - os.Remove(source) - allReports = append(allReports, reports...) - } - - if err := os.MkdirAll(path.Dir(destination), 0770); err != nil { - return messages, err - } - f, err := os.Create(destination) - if err != nil { - return messages, err - } - defer f.Close() - enc := json.NewEncoder(f) - enc.SetIndent("", " ") - err = enc.Encode(allReports) - if err != nil { - return messages, err - } - return messages, nil -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go deleted file mode 100644 index 43244a9bd..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go +++ /dev/null @@ -1,388 +0,0 @@ -/* - -JUnit XML Reporter for Ginkgo - -For usage instructions: http://onsi.github.io/ginkgo/#generating_junit_xml_output - -The schema used for the generated JUnit xml file was adapted from https://llg.cubic.org/docs/junit/ - -*/ - -package reporters - -import ( - "encoding/xml" - "fmt" - "os" - "path" - "regexp" - "strings" - - "github.com/onsi/ginkgo/v2/config" - "github.com/onsi/ginkgo/v2/types" -) - -type JunitReportConfig struct { - // Spec States for which no timeline should be emitted for system-err - // set this to types.SpecStatePassed|types.SpecStateSkipped|types.SpecStatePending to only match failing specs - OmitTimelinesForSpecState types.SpecState - - // Enable OmitFailureMessageAttr to prevent failure messages appearing in the "message" attribute of the Failure and Error tags - OmitFailureMessageAttr bool - - //Enable OmitCapturedStdOutErr to prevent captured stdout/stderr appearing in system-out - OmitCapturedStdOutErr bool - - // Enable OmitSpecLabels to prevent labels from appearing in the spec name - OmitSpecLabels bool - - // Enable OmitLeafNodeType to prevent the spec leaf node type from appearing in the spec name - OmitLeafNodeType bool - - // Enable OmitSuiteSetupNodes to prevent the creation of testcase entries for setup nodes - OmitSuiteSetupNodes bool -} - -type JUnitTestSuites struct { - XMLName xml.Name `xml:"testsuites"` - // Tests maps onto the total number of specs in all test suites (this includes any suite nodes such as BeforeSuite) - Tests int `xml:"tests,attr"` - // Disabled maps onto specs that are pending and/or skipped - Disabled int `xml:"disabled,attr"` - // Errors maps onto specs that panicked or were interrupted - Errors int `xml:"errors,attr"` - // Failures maps onto specs that failed - Failures int `xml:"failures,attr"` - // Time is the time in seconds to execute all test suites - Time float64 `xml:"time,attr"` - - //The set of all test suites - TestSuites []JUnitTestSuite `xml:"testsuite"` -} - -type JUnitTestSuite struct { - // Name maps onto the description of the test suite - maps onto Report.SuiteDescription - Name string `xml:"name,attr"` - // Package maps onto the absolute path to the test suite - maps onto Report.SuitePath - Package string `xml:"package,attr"` - // Tests maps onto the total number of specs in the test suite (this includes any suite nodes such as BeforeSuite) - Tests int `xml:"tests,attr"` - // Disabled maps onto specs that are pending - Disabled int `xml:"disabled,attr"` - // Skiped maps onto specs that are skipped - Skipped int `xml:"skipped,attr"` - // Errors maps onto specs that panicked or were interrupted - Errors int `xml:"errors,attr"` - // Failures maps onto specs that failed - Failures int `xml:"failures,attr"` - // Time is the time in seconds to execute all the test suite - maps onto Report.RunTime - Time float64 `xml:"time,attr"` - // Timestamp is the ISO 8601 formatted start-time of the suite - maps onto Report.StartTime - Timestamp string `xml:"timestamp,attr"` - - //Properties captures the information stored in the rest of the Report type (including SuiteConfig) as key-value pairs - Properties JUnitProperties `xml:"properties"` - - //TestCases capture the individual specs - TestCases []JUnitTestCase `xml:"testcase"` -} - -type JUnitProperties struct { - Properties []JUnitProperty `xml:"property"` -} - -func (jup JUnitProperties) WithName(name string) string { - for _, property := range jup.Properties { - if property.Name == name { - return property.Value - } - } - return "" -} - -type JUnitProperty struct { - Name string `xml:"name,attr"` - Value string `xml:"value,attr"` -} - -var ownerRE = regexp.MustCompile(`(?i)^owner:(.*)$`) - -type JUnitTestCase struct { - // Name maps onto the full text of the spec - equivalent to "[SpecReport.LeafNodeType] SpecReport.FullText()" - Name string `xml:"name,attr"` - // Classname maps onto the name of the test suite - equivalent to Report.SuiteDescription - Classname string `xml:"classname,attr"` - // Status maps onto the string representation of SpecReport.State - Status string `xml:"status,attr"` - // Time is the time in seconds to execute the spec - maps onto SpecReport.RunTime - Time float64 `xml:"time,attr"` - // Owner is the owner the spec - is set if a label matching Label("owner:X") is provided. The last matching label is used as the owner, thereby allowing specs to override owners specified in container nodes. - Owner string `xml:"owner,attr,omitempty"` - //Skipped is populated with a message if the test was skipped or pending - Skipped *JUnitSkipped `xml:"skipped,omitempty"` - //Error is populated if the test panicked or was interrupted - Error *JUnitError `xml:"error,omitempty"` - //Failure is populated if the test failed - Failure *JUnitFailure `xml:"failure,omitempty"` - //SystemOut maps onto any captured stdout/stderr output - maps onto SpecReport.CapturedStdOutErr - SystemOut string `xml:"system-out,omitempty"` - //SystemOut maps onto any captured GinkgoWriter output - maps onto SpecReport.CapturedGinkgoWriterOutput - SystemErr string `xml:"system-err,omitempty"` -} - -type JUnitSkipped struct { - // Message maps onto "pending" if the test was marked pending, "skipped" if the test was marked skipped, and "skipped - REASON" if the user called Skip(REASON) - Message string `xml:"message,attr"` -} - -type JUnitError struct { - //Message maps onto the panic/exception thrown - equivalent to SpecReport.Failure.ForwardedPanic - or to "interrupted" - Message string `xml:"message,attr"` - //Type is one of "panicked" or "interrupted" - Type string `xml:"type,attr"` - //Description maps onto the captured stack trace for a panic, or the failure message for an interrupt which will include the dump of running goroutines - Description string `xml:",chardata"` -} - -type JUnitFailure struct { - //Message maps onto the failure message - equivalent to SpecReport.Failure.Message - Message string `xml:"message,attr"` - //Type is "failed" - Type string `xml:"type,attr"` - //Description maps onto the location and stack trace of the failure - Description string `xml:",chardata"` -} - -func GenerateJUnitReport(report types.Report, dst string) error { - return GenerateJUnitReportWithConfig(report, dst, JunitReportConfig{}) -} - -func GenerateJUnitReportWithConfig(report types.Report, dst string, config JunitReportConfig) error { - suite := JUnitTestSuite{ - Name: report.SuiteDescription, - Package: report.SuitePath, - Time: report.RunTime.Seconds(), - Timestamp: report.StartTime.Format("2006-01-02T15:04:05"), - Properties: JUnitProperties{ - Properties: []JUnitProperty{ - {"SuiteSucceeded", fmt.Sprintf("%t", report.SuiteSucceeded)}, - {"SuiteHasProgrammaticFocus", fmt.Sprintf("%t", report.SuiteHasProgrammaticFocus)}, - {"SpecialSuiteFailureReason", strings.Join(report.SpecialSuiteFailureReasons, ",")}, - {"SuiteLabels", fmt.Sprintf("[%s]", strings.Join(report.SuiteLabels, ","))}, - {"RandomSeed", fmt.Sprintf("%d", report.SuiteConfig.RandomSeed)}, - {"RandomizeAllSpecs", fmt.Sprintf("%t", report.SuiteConfig.RandomizeAllSpecs)}, - {"LabelFilter", report.SuiteConfig.LabelFilter}, - {"FocusStrings", strings.Join(report.SuiteConfig.FocusStrings, ",")}, - {"SkipStrings", strings.Join(report.SuiteConfig.SkipStrings, ",")}, - {"FocusFiles", strings.Join(report.SuiteConfig.FocusFiles, ";")}, - {"SkipFiles", strings.Join(report.SuiteConfig.SkipFiles, ";")}, - {"FailOnPending", fmt.Sprintf("%t", report.SuiteConfig.FailOnPending)}, - {"FailFast", fmt.Sprintf("%t", report.SuiteConfig.FailFast)}, - {"FlakeAttempts", fmt.Sprintf("%d", report.SuiteConfig.FlakeAttempts)}, - {"DryRun", fmt.Sprintf("%t", report.SuiteConfig.DryRun)}, - {"ParallelTotal", fmt.Sprintf("%d", report.SuiteConfig.ParallelTotal)}, - {"OutputInterceptorMode", report.SuiteConfig.OutputInterceptorMode}, - }, - }, - } - for _, spec := range report.SpecReports { - if config.OmitSuiteSetupNodes && spec.LeafNodeType != types.NodeTypeIt { - continue - } - name := fmt.Sprintf("[%s]", spec.LeafNodeType) - if config.OmitLeafNodeType { - name = "" - } - if spec.FullText() != "" { - name = name + " " + spec.FullText() - } - labels := spec.Labels() - if len(labels) > 0 && !config.OmitSpecLabels { - name = name + " [" + strings.Join(labels, ", ") + "]" - } - owner := "" - for _, label := range labels { - if matches := ownerRE.FindStringSubmatch(label); len(matches) == 2 { - owner = matches[1] - } - } - name = strings.TrimSpace(name) - - test := JUnitTestCase{ - Name: name, - Classname: report.SuiteDescription, - Status: spec.State.String(), - Time: spec.RunTime.Seconds(), - Owner: owner, - } - if !spec.State.Is(config.OmitTimelinesForSpecState) { - test.SystemErr = systemErrForUnstructuredReporters(spec) - } - if !config.OmitCapturedStdOutErr { - test.SystemOut = systemOutForUnstructuredReporters(spec) - } - suite.Tests += 1 - - switch spec.State { - case types.SpecStateSkipped: - message := "skipped" - if spec.Failure.Message != "" { - message += " - " + spec.Failure.Message - } - test.Skipped = &JUnitSkipped{Message: message} - suite.Skipped += 1 - case types.SpecStatePending: - test.Skipped = &JUnitSkipped{Message: "pending"} - suite.Disabled += 1 - case types.SpecStateFailed: - test.Failure = &JUnitFailure{ - Message: spec.Failure.Message, - Type: "failed", - Description: failureDescriptionForUnstructuredReporters(spec), - } - if config.OmitFailureMessageAttr { - test.Failure.Message = "" - } - suite.Failures += 1 - case types.SpecStateTimedout: - test.Failure = &JUnitFailure{ - Message: spec.Failure.Message, - Type: "timedout", - Description: failureDescriptionForUnstructuredReporters(spec), - } - if config.OmitFailureMessageAttr { - test.Failure.Message = "" - } - suite.Failures += 1 - case types.SpecStateInterrupted: - test.Error = &JUnitError{ - Message: spec.Failure.Message, - Type: "interrupted", - Description: failureDescriptionForUnstructuredReporters(spec), - } - if config.OmitFailureMessageAttr { - test.Error.Message = "" - } - suite.Errors += 1 - case types.SpecStateAborted: - test.Failure = &JUnitFailure{ - Message: spec.Failure.Message, - Type: "aborted", - Description: failureDescriptionForUnstructuredReporters(spec), - } - if config.OmitFailureMessageAttr { - test.Failure.Message = "" - } - suite.Errors += 1 - case types.SpecStatePanicked: - test.Error = &JUnitError{ - Message: spec.Failure.ForwardedPanic, - Type: "panicked", - Description: failureDescriptionForUnstructuredReporters(spec), - } - if config.OmitFailureMessageAttr { - test.Error.Message = "" - } - suite.Errors += 1 - } - - suite.TestCases = append(suite.TestCases, test) - } - - junitReport := JUnitTestSuites{ - Tests: suite.Tests, - Disabled: suite.Disabled + suite.Skipped, - Errors: suite.Errors, - Failures: suite.Failures, - Time: suite.Time, - TestSuites: []JUnitTestSuite{suite}, - } - - if err := os.MkdirAll(path.Dir(dst), 0770); err != nil { - return err - } - f, err := os.Create(dst) - if err != nil { - return err - } - f.WriteString(xml.Header) - encoder := xml.NewEncoder(f) - encoder.Indent(" ", " ") - encoder.Encode(junitReport) - - return f.Close() -} - -func MergeAndCleanupJUnitReports(sources []string, dst string) ([]string, error) { - messages := []string{} - mergedReport := JUnitTestSuites{} - for _, source := range sources { - report := JUnitTestSuites{} - f, err := os.Open(source) - if err != nil { - messages = append(messages, fmt.Sprintf("Could not open %s:\n%s", source, err.Error())) - continue - } - err = xml.NewDecoder(f).Decode(&report) - if err != nil { - messages = append(messages, fmt.Sprintf("Could not decode %s:\n%s", source, err.Error())) - continue - } - os.Remove(source) - - mergedReport.Tests += report.Tests - mergedReport.Disabled += report.Disabled - mergedReport.Errors += report.Errors - mergedReport.Failures += report.Failures - mergedReport.Time += report.Time - mergedReport.TestSuites = append(mergedReport.TestSuites, report.TestSuites...) - } - - if err := os.MkdirAll(path.Dir(dst), 0770); err != nil { - return messages, err - } - f, err := os.Create(dst) - if err != nil { - return messages, err - } - f.WriteString(xml.Header) - encoder := xml.NewEncoder(f) - encoder.Indent(" ", " ") - encoder.Encode(mergedReport) - - return messages, f.Close() -} - -func failureDescriptionForUnstructuredReporters(spec types.SpecReport) string { - out := &strings.Builder{} - NewDefaultReporter(types.ReporterConfig{NoColor: true, VeryVerbose: true}, out).emitFailure(0, spec.State, spec.Failure, true) - if len(spec.AdditionalFailures) > 0 { - out.WriteString("\nThere were additional failures detected after the initial failure. These are visible in the timeline\n") - } - return out.String() -} - -func systemErrForUnstructuredReporters(spec types.SpecReport) string { - return RenderTimeline(spec, true) -} - -func RenderTimeline(spec types.SpecReport, noColor bool) string { - out := &strings.Builder{} - NewDefaultReporter(types.ReporterConfig{NoColor: noColor, VeryVerbose: true}, out).emitTimeline(0, spec, spec.Timeline()) - return out.String() -} - -func systemOutForUnstructuredReporters(spec types.SpecReport) string { - return spec.CapturedStdOutErr -} - -// Deprecated JUnitReporter (so folks can still compile their suites) -type JUnitReporter struct{} - -func NewJUnitReporter(_ string) *JUnitReporter { return &JUnitReporter{} } -func (reporter *JUnitReporter) SuiteWillBegin(_ config.GinkgoConfigType, _ *types.SuiteSummary) {} -func (reporter *JUnitReporter) BeforeSuiteDidRun(_ *types.SetupSummary) {} -func (reporter *JUnitReporter) SpecWillRun(_ *types.SpecSummary) {} -func (reporter *JUnitReporter) SpecDidComplete(_ *types.SpecSummary) {} -func (reporter *JUnitReporter) AfterSuiteDidRun(_ *types.SetupSummary) {} -func (reporter *JUnitReporter) SuiteDidEnd(_ *types.SuiteSummary) {} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/reporter.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/reporter.go deleted file mode 100644 index 5e726c464..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/reporter.go +++ /dev/null @@ -1,29 +0,0 @@ -package reporters - -import ( - "github.com/onsi/ginkgo/v2/types" -) - -type Reporter interface { - SuiteWillBegin(report types.Report) - WillRun(report types.SpecReport) - DidRun(report types.SpecReport) - SuiteDidEnd(report types.Report) - - //Timeline emission - EmitFailure(state types.SpecState, failure types.Failure) - EmitProgressReport(progressReport types.ProgressReport) - EmitReportEntry(entry types.ReportEntry) - EmitSpecEvent(event types.SpecEvent) -} - -type NoopReporter struct{} - -func (n NoopReporter) SuiteWillBegin(report types.Report) {} -func (n NoopReporter) WillRun(report types.SpecReport) {} -func (n NoopReporter) DidRun(report types.SpecReport) {} -func (n NoopReporter) SuiteDidEnd(report types.Report) {} -func (n NoopReporter) EmitFailure(state types.SpecState, failure types.Failure) {} -func (n NoopReporter) EmitProgressReport(progressReport types.ProgressReport) {} -func (n NoopReporter) EmitReportEntry(entry types.ReportEntry) {} -func (n NoopReporter) EmitSpecEvent(event types.SpecEvent) {} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/teamcity_report.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/teamcity_report.go deleted file mode 100644 index e990ad82e..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/reporters/teamcity_report.go +++ /dev/null @@ -1,105 +0,0 @@ -/* - -TeamCity Reporter for Ginkgo - -Makes use of TeamCity's support for Service Messages -http://confluence.jetbrains.com/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingTests -*/ - -package reporters - -import ( - "fmt" - "os" - "path" - "strings" - - "github.com/onsi/ginkgo/v2/types" -) - -func tcEscape(s string) string { - s = strings.ReplaceAll(s, "|", "||") - s = strings.ReplaceAll(s, "'", "|'") - s = strings.ReplaceAll(s, "\n", "|n") - s = strings.ReplaceAll(s, "\r", "|r") - s = strings.ReplaceAll(s, "[", "|[") - s = strings.ReplaceAll(s, "]", "|]") - return s -} - -func GenerateTeamcityReport(report types.Report, dst string) error { - if err := os.MkdirAll(path.Dir(dst), 0770); err != nil { - return err - } - f, err := os.Create(dst) - if err != nil { - return err - } - - name := report.SuiteDescription - labels := report.SuiteLabels - if len(labels) > 0 { - name = name + " [" + strings.Join(labels, ", ") + "]" - } - fmt.Fprintf(f, "##teamcity[testSuiteStarted name='%s']\n", tcEscape(name)) - for _, spec := range report.SpecReports { - name := fmt.Sprintf("[%s]", spec.LeafNodeType) - if spec.FullText() != "" { - name = name + " " + spec.FullText() - } - labels := spec.Labels() - if len(labels) > 0 { - name = name + " [" + strings.Join(labels, ", ") + "]" - } - - name = tcEscape(name) - fmt.Fprintf(f, "##teamcity[testStarted name='%s']\n", name) - switch spec.State { - case types.SpecStatePending: - fmt.Fprintf(f, "##teamcity[testIgnored name='%s' message='pending']\n", name) - case types.SpecStateSkipped: - message := "skipped" - if spec.Failure.Message != "" { - message += " - " + spec.Failure.Message - } - fmt.Fprintf(f, "##teamcity[testIgnored name='%s' message='%s']\n", name, tcEscape(message)) - case types.SpecStateFailed: - details := failureDescriptionForUnstructuredReporters(spec) - fmt.Fprintf(f, "##teamcity[testFailed name='%s' message='failed - %s' details='%s']\n", name, tcEscape(spec.Failure.Message), tcEscape(details)) - case types.SpecStatePanicked: - details := failureDescriptionForUnstructuredReporters(spec) - fmt.Fprintf(f, "##teamcity[testFailed name='%s' message='panicked - %s' details='%s']\n", name, tcEscape(spec.Failure.ForwardedPanic), tcEscape(details)) - case types.SpecStateTimedout: - details := failureDescriptionForUnstructuredReporters(spec) - fmt.Fprintf(f, "##teamcity[testFailed name='%s' message='timedout - %s' details='%s']\n", name, tcEscape(spec.Failure.Message), tcEscape(details)) - case types.SpecStateInterrupted: - details := failureDescriptionForUnstructuredReporters(spec) - fmt.Fprintf(f, "##teamcity[testFailed name='%s' message='interrupted - %s' details='%s']\n", name, tcEscape(spec.Failure.Message), tcEscape(details)) - case types.SpecStateAborted: - details := failureDescriptionForUnstructuredReporters(spec) - fmt.Fprintf(f, "##teamcity[testFailed name='%s' message='aborted - %s' details='%s']\n", name, tcEscape(spec.Failure.Message), tcEscape(details)) - } - - fmt.Fprintf(f, "##teamcity[testStdOut name='%s' out='%s']\n", name, tcEscape(systemOutForUnstructuredReporters(spec))) - fmt.Fprintf(f, "##teamcity[testStdErr name='%s' out='%s']\n", name, tcEscape(systemErrForUnstructuredReporters(spec))) - fmt.Fprintf(f, "##teamcity[testFinished name='%s' duration='%d']\n", name, int(spec.RunTime.Seconds()*1000.0)) - } - fmt.Fprintf(f, "##teamcity[testSuiteFinished name='%s']\n", tcEscape(report.SuiteDescription)) - - return f.Close() -} - -func MergeAndCleanupTeamcityReports(sources []string, dst string) ([]string, error) { - messages := []string{} - merged := []byte{} - for _, source := range sources { - data, err := os.ReadFile(source) - if err != nil { - messages = append(messages, fmt.Sprintf("Could not open %s:\n%s", source, err.Error())) - continue - } - os.Remove(source) - merged = append(merged, data...) - } - return messages, os.WriteFile(dst, merged, 0666) -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/code_location.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/code_location.go deleted file mode 100644 index 57e87517e..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/code_location.go +++ /dev/null @@ -1,159 +0,0 @@ -package types - -import ( - "fmt" - "os" - "regexp" - "runtime" - "runtime/debug" - "strings" - "sync" -) - -type CodeLocation struct { - FileName string `json:",omitempty"` - LineNumber int `json:",omitempty"` - FullStackTrace string `json:",omitempty"` - CustomMessage string `json:",omitempty"` -} - -func (codeLocation CodeLocation) String() string { - if codeLocation.CustomMessage != "" { - return codeLocation.CustomMessage - } - return fmt.Sprintf("%s:%d", codeLocation.FileName, codeLocation.LineNumber) -} - -func (codeLocation CodeLocation) ContentsOfLine() string { - if codeLocation.CustomMessage != "" { - return "" - } - contents, err := os.ReadFile(codeLocation.FileName) - if err != nil { - return "" - } - lines := strings.Split(string(contents), "\n") - if len(lines) < codeLocation.LineNumber { - return "" - } - return lines[codeLocation.LineNumber-1] -} - -type codeLocationLocator struct { - pcs map[uintptr]bool - helpers map[string]bool - lock *sync.Mutex -} - -func (c *codeLocationLocator) addHelper(pc uintptr) { - c.lock.Lock() - defer c.lock.Unlock() - - if c.pcs[pc] { - return - } - c.lock.Unlock() - f := runtime.FuncForPC(pc) - c.lock.Lock() - if f == nil { - return - } - c.helpers[f.Name()] = true - c.pcs[pc] = true -} - -func (c *codeLocationLocator) hasHelper(name string) bool { - c.lock.Lock() - defer c.lock.Unlock() - return c.helpers[name] -} - -func (c *codeLocationLocator) getCodeLocation(skip int) CodeLocation { - pc := make([]uintptr, 40) - n := runtime.Callers(skip+2, pc) - if n == 0 { - return CodeLocation{} - } - pc = pc[:n] - frames := runtime.CallersFrames(pc) - for { - frame, more := frames.Next() - if !c.hasHelper(frame.Function) { - return CodeLocation{FileName: frame.File, LineNumber: frame.Line} - } - if !more { - break - } - } - return CodeLocation{} -} - -var clLocator = &codeLocationLocator{ - pcs: map[uintptr]bool{}, - helpers: map[string]bool{}, - lock: &sync.Mutex{}, -} - -// MarkAsHelper is used by GinkgoHelper to mark the caller (appropriately offset by skip)as a helper. You can use this directly if you need to provide an optional `skip` to mark functions further up the call stack as helpers. -func MarkAsHelper(optionalSkip ...int) { - skip := 1 - if len(optionalSkip) > 0 { - skip += optionalSkip[0] - } - pc, _, _, ok := runtime.Caller(skip) - if ok { - clLocator.addHelper(pc) - } -} - -func NewCustomCodeLocation(message string) CodeLocation { - return CodeLocation{ - CustomMessage: message, - } -} - -func NewCodeLocation(skip int) CodeLocation { - return clLocator.getCodeLocation(skip + 1) -} - -func NewCodeLocationWithStackTrace(skip int) CodeLocation { - cl := clLocator.getCodeLocation(skip + 1) - cl.FullStackTrace = PruneStack(string(debug.Stack()), skip+1) - return cl -} - -// PruneStack removes references to functions that are internal to Ginkgo -// and the Go runtime from a stack string and a certain number of stack entries -// at the beginning of the stack. The stack string has the format -// as returned by runtime/debug.Stack. The leading goroutine information is -// optional and always removed if present. Beware that runtime/debug.Stack -// adds itself as first entry, so typically skip must be >= 1 to remove that -// entry. -func PruneStack(fullStackTrace string, skip int) string { - stack := strings.Split(fullStackTrace, "\n") - // Ensure that the even entries are the method names and the - // odd entries the source code information. - if len(stack) > 0 && strings.HasPrefix(stack[0], "goroutine ") { - // Ignore "goroutine 29 [running]:" line. - stack = stack[1:] - } - // The "+1" is for skipping over the initial entry, which is - // runtime/debug.Stack() itself. - if len(stack) > 2*(skip+1) { - stack = stack[2*(skip+1):] - } - prunedStack := []string{} - if os.Getenv("GINKGO_PRUNE_STACK") == "FALSE" { - prunedStack = stack - } else { - re := regexp.MustCompile(`\/ginkgo\/|\/pkg\/testing\/|\/pkg\/runtime\/`) - for i := 0; i < len(stack)/2; i++ { - // We filter out based on the source code file name. - if !re.MatchString(stack[i*2+1]) { - prunedStack = append(prunedStack, stack[i*2]) - prunedStack = append(prunedStack, stack[i*2+1]) - } - } - } - return strings.Join(prunedStack, "\n") -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/config.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/config.go deleted file mode 100644 index c88fc85a7..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/config.go +++ /dev/null @@ -1,758 +0,0 @@ -/* -Ginkgo accepts a number of configuration options. -These are documented [here](http://onsi.github.io/ginkgo/#the-ginkgo-cli) -*/ - -package types - -import ( - "flag" - "os" - "path/filepath" - "runtime" - "strconv" - "strings" - "time" -) - -// Configuration controlling how an individual test suite is run -type SuiteConfig struct { - RandomSeed int64 - RandomizeAllSpecs bool - FocusStrings []string - SkipStrings []string - FocusFiles []string - SkipFiles []string - LabelFilter string - FailOnPending bool - FailFast bool - FlakeAttempts int - MustPassRepeatedly int - DryRun bool - PollProgressAfter time.Duration - PollProgressInterval time.Duration - Timeout time.Duration - EmitSpecProgress bool // this is deprecated but its removal is causing compile issue for some users that were setting it manually - OutputInterceptorMode string - SourceRoots []string - GracePeriod time.Duration - - ParallelProcess int - ParallelTotal int - ParallelHost string -} - -func NewDefaultSuiteConfig() SuiteConfig { - return SuiteConfig{ - RandomSeed: time.Now().Unix(), - Timeout: time.Hour, - ParallelProcess: 1, - ParallelTotal: 1, - GracePeriod: 30 * time.Second, - } -} - -type VerbosityLevel uint - -const ( - VerbosityLevelSuccinct VerbosityLevel = iota - VerbosityLevelNormal - VerbosityLevelVerbose - VerbosityLevelVeryVerbose -) - -func (vl VerbosityLevel) GT(comp VerbosityLevel) bool { - return vl > comp -} - -func (vl VerbosityLevel) GTE(comp VerbosityLevel) bool { - return vl >= comp -} - -func (vl VerbosityLevel) Is(comp VerbosityLevel) bool { - return vl == comp -} - -func (vl VerbosityLevel) LTE(comp VerbosityLevel) bool { - return vl <= comp -} - -func (vl VerbosityLevel) LT(comp VerbosityLevel) bool { - return vl < comp -} - -// Configuration for Ginkgo's reporter -type ReporterConfig struct { - NoColor bool - Succinct bool - Verbose bool - VeryVerbose bool - FullTrace bool - ShowNodeEvents bool - - JSONReport string - JUnitReport string - TeamcityReport string -} - -func (rc ReporterConfig) Verbosity() VerbosityLevel { - if rc.Succinct { - return VerbosityLevelSuccinct - } else if rc.Verbose { - return VerbosityLevelVerbose - } else if rc.VeryVerbose { - return VerbosityLevelVeryVerbose - } - return VerbosityLevelNormal -} - -func (rc ReporterConfig) WillGenerateReport() bool { - return rc.JSONReport != "" || rc.JUnitReport != "" || rc.TeamcityReport != "" -} - -func NewDefaultReporterConfig() ReporterConfig { - return ReporterConfig{} -} - -// Configuration for the Ginkgo CLI -type CLIConfig struct { - //for build, run, and watch - Recurse bool - SkipPackage string - RequireSuite bool - NumCompilers int - - //for run and watch only - Procs int - Parallel bool - AfterRunHook string - OutputDir string - KeepSeparateCoverprofiles bool - KeepSeparateReports bool - - //for run only - KeepGoing bool - UntilItFails bool - Repeat int - RandomizeSuites bool - - //for watch only - Depth int - WatchRegExp string -} - -func NewDefaultCLIConfig() CLIConfig { - return CLIConfig{ - Depth: 1, - WatchRegExp: `\.go$`, - } -} - -func (g CLIConfig) ComputedProcs() int { - if g.Procs > 0 { - return g.Procs - } - - n := 1 - if g.Parallel { - n = runtime.NumCPU() - if n > 4 { - n = n - 1 - } - } - return n -} - -func (g CLIConfig) ComputedNumCompilers() int { - if g.NumCompilers > 0 { - return g.NumCompilers - } - - return runtime.NumCPU() -} - -// Configuration for the Ginkgo CLI capturing available go flags -// A subset of Go flags are exposed by Ginkgo. Some are available at compile time (e.g. ginkgo build) and others only at run time (e.g. ginkgo run - which has both build and run time flags). -// More details can be found at: -// https://docs.google.com/spreadsheets/d/1zkp-DS4hU4sAJl5eHh1UmgwxCPQhf3s5a8fbiOI8tJU/ -type GoFlagsConfig struct { - //build-time flags for code-and-performance analysis - Race bool - Cover bool - CoverMode string - CoverPkg string - Vet string - - //run-time flags for code-and-performance analysis - BlockProfile string - BlockProfileRate int - CoverProfile string - CPUProfile string - MemProfile string - MemProfileRate int - MutexProfile string - MutexProfileFraction int - Trace string - - //build-time flags for building - A bool - ASMFlags string - BuildMode string - Compiler string - GCCGoFlags string - GCFlags string - InstallSuffix string - LDFlags string - LinkShared bool - Mod string - N bool - ModFile string - ModCacheRW bool - MSan bool - PkgDir string - Tags string - TrimPath bool - ToolExec string - Work bool - X bool -} - -func NewDefaultGoFlagsConfig() GoFlagsConfig { - return GoFlagsConfig{} -} - -func (g GoFlagsConfig) BinaryMustBePreserved() bool { - return g.BlockProfile != "" || g.CPUProfile != "" || g.MemProfile != "" || g.MutexProfile != "" -} - -// Configuration that were deprecated in 2.0 -type deprecatedConfig struct { - DebugParallel bool - NoisySkippings bool - NoisyPendings bool - RegexScansFilePath bool - SlowSpecThresholdWithFLoatUnits float64 - Stream bool - Notify bool - EmitSpecProgress bool - SlowSpecThreshold time.Duration - AlwaysEmitGinkgoWriter bool -} - -// Flags - -// Flags sections used by both the CLI and the Ginkgo test process -var FlagSections = GinkgoFlagSections{ - {Key: "multiple-suites", Style: "{{dark-green}}", Heading: "Running Multiple Test Suites"}, - {Key: "order", Style: "{{green}}", Heading: "Controlling Test Order"}, - {Key: "parallel", Style: "{{yellow}}", Heading: "Controlling Test Parallelism"}, - {Key: "low-level-parallel", Style: "{{yellow}}", Heading: "Controlling Test Parallelism", - Description: "These are set by the Ginkgo CLI, {{red}}{{bold}}do not set them manually{{/}} via go test.\nUse ginkgo -p or ginkgo -procs=N instead."}, - {Key: "filter", Style: "{{cyan}}", Heading: "Filtering Tests"}, - {Key: "failure", Style: "{{red}}", Heading: "Failure Handling"}, - {Key: "output", Style: "{{magenta}}", Heading: "Controlling Output Formatting"}, - {Key: "code-and-coverage-analysis", Style: "{{orange}}", Heading: "Code and Coverage Analysis"}, - {Key: "performance-analysis", Style: "{{coral}}", Heading: "Performance Analysis"}, - {Key: "debug", Style: "{{blue}}", Heading: "Debugging Tests", - Description: "In addition to these flags, Ginkgo supports a few debugging environment variables. To change the parallel server protocol set {{blue}}GINKGO_PARALLEL_PROTOCOL{{/}} to {{bold}}HTTP{{/}}. To avoid pruning callstacks set {{blue}}GINKGO_PRUNE_STACK{{/}} to {{bold}}FALSE{{/}}."}, - {Key: "watch", Style: "{{light-yellow}}", Heading: "Controlling Ginkgo Watch"}, - {Key: "misc", Style: "{{light-gray}}", Heading: "Miscellaneous"}, - {Key: "go-build", Style: "{{light-gray}}", Heading: "Go Build Flags", Succinct: true, - Description: "These flags are inherited from go build. Run {{bold}}ginkgo help build{{/}} for more detailed flag documentation."}, -} - -// SuiteConfigFlags provides flags for the Ginkgo test process, and CLI -var SuiteConfigFlags = GinkgoFlags{ - {KeyPath: "S.RandomSeed", Name: "seed", SectionKey: "order", UsageDefaultValue: "randomly generated by Ginkgo", - Usage: "The seed used to randomize the spec suite."}, - {KeyPath: "S.RandomizeAllSpecs", Name: "randomize-all", SectionKey: "order", DeprecatedName: "randomizeAllSpecs", DeprecatedDocLink: "changed-command-line-flags", - Usage: "If set, ginkgo will randomize all specs together. By default, ginkgo only randomizes the top level Describe, Context and When containers."}, - - {KeyPath: "S.FailOnPending", Name: "fail-on-pending", SectionKey: "failure", DeprecatedName: "failOnPending", DeprecatedDocLink: "changed-command-line-flags", - Usage: "If set, ginkgo will mark the test suite as failed if any specs are pending."}, - {KeyPath: "S.FailFast", Name: "fail-fast", SectionKey: "failure", DeprecatedName: "failFast", DeprecatedDocLink: "changed-command-line-flags", - Usage: "If set, ginkgo will stop running a test suite after a failure occurs."}, - {KeyPath: "S.FlakeAttempts", Name: "flake-attempts", SectionKey: "failure", UsageDefaultValue: "0 - failed tests are not retried", DeprecatedName: "flakeAttempts", DeprecatedDocLink: "changed-command-line-flags", - Usage: "Make up to this many attempts to run each spec. If any of the attempts succeed, the suite will not be failed."}, - - {KeyPath: "S.DryRun", Name: "dry-run", SectionKey: "debug", DeprecatedName: "dryRun", DeprecatedDocLink: "changed-command-line-flags", - Usage: "If set, ginkgo will walk the test hierarchy without actually running anything. Best paired with -v."}, - {KeyPath: "S.PollProgressAfter", Name: "poll-progress-after", SectionKey: "debug", UsageDefaultValue: "0", - Usage: "Emit node progress reports periodically if node hasn't completed after this duration."}, - {KeyPath: "S.PollProgressInterval", Name: "poll-progress-interval", SectionKey: "debug", UsageDefaultValue: "10s", - Usage: "The rate at which to emit node progress reports after poll-progress-after has elapsed."}, - {KeyPath: "S.SourceRoots", Name: "source-root", SectionKey: "debug", - Usage: "The location to look for source code when generating progress reports. You can pass multiple --source-root flags."}, - {KeyPath: "S.Timeout", Name: "timeout", SectionKey: "debug", UsageDefaultValue: "1h", - Usage: "Test suite fails if it does not complete within the specified timeout."}, - {KeyPath: "S.GracePeriod", Name: "grace-period", SectionKey: "debug", UsageDefaultValue: "30s", - Usage: "When interrupted, Ginkgo will wait for GracePeriod for the current running node to exit before moving on to the next one."}, - {KeyPath: "S.OutputInterceptorMode", Name: "output-interceptor-mode", SectionKey: "debug", UsageArgument: "dup, swap, or none", - Usage: "If set, ginkgo will use the specified output interception strategy when running in parallel. Defaults to dup on unix and swap on windows."}, - - {KeyPath: "S.LabelFilter", Name: "label-filter", SectionKey: "filter", UsageArgument: "expression", - Usage: "If set, ginkgo will only run specs with labels that match the label-filter. The passed-in expression can include boolean operations (!, &&, ||, ','), groupings via '()', and regular expressions '/regexp/'. e.g. '(cat || dog) && !fruit'"}, - {KeyPath: "S.FocusStrings", Name: "focus", SectionKey: "filter", - Usage: "If set, ginkgo will only run specs that match this regular expression. Can be specified multiple times, values are ORed."}, - {KeyPath: "S.SkipStrings", Name: "skip", SectionKey: "filter", - Usage: "If set, ginkgo will only run specs that do not match this regular expression. Can be specified multiple times, values are ORed."}, - {KeyPath: "S.FocusFiles", Name: "focus-file", SectionKey: "filter", UsageArgument: "file (regexp) | file:line | file:lineA-lineB | file:line,line,line", - Usage: "If set, ginkgo will only run specs in matching files. Can be specified multiple times, values are ORed."}, - {KeyPath: "S.SkipFiles", Name: "skip-file", SectionKey: "filter", UsageArgument: "file (regexp) | file:line | file:lineA-lineB | file:line,line,line", - Usage: "If set, ginkgo will skip specs in matching files. Can be specified multiple times, values are ORed."}, - - {KeyPath: "D.RegexScansFilePath", DeprecatedName: "regexScansFilePath", DeprecatedDocLink: "removed--regexscansfilepath", DeprecatedVersion: "2.0.0"}, - {KeyPath: "D.DebugParallel", DeprecatedName: "debug", DeprecatedDocLink: "removed--debug", DeprecatedVersion: "2.0.0"}, - {KeyPath: "D.EmitSpecProgress", DeprecatedName: "progress", SectionKey: "debug", - DeprecatedVersion: "2.5.0", Usage: ". The functionality provided by --progress was confusing and is no longer needed. Use --show-node-events instead to see node entry and exit events included in the timeline of failed and verbose specs. Or you can run with -vv to always see all node events. Lastly, --poll-progress-after and the PollProgressAfter decorator now provide a better mechanism for debugging specs that tend to get stuck."}, -} - -// ParallelConfigFlags provides flags for the Ginkgo test process (not the CLI) -var ParallelConfigFlags = GinkgoFlags{ - {KeyPath: "S.ParallelProcess", Name: "parallel.process", SectionKey: "low-level-parallel", UsageDefaultValue: "1", - Usage: "This worker process's (one-indexed) process number. For running specs in parallel."}, - {KeyPath: "S.ParallelTotal", Name: "parallel.total", SectionKey: "low-level-parallel", UsageDefaultValue: "1", - Usage: "The total number of worker processes. For running specs in parallel."}, - {KeyPath: "S.ParallelHost", Name: "parallel.host", SectionKey: "low-level-parallel", UsageDefaultValue: "set by Ginkgo CLI", - Usage: "The address for the server that will synchronize the processes."}, -} - -// ReporterConfigFlags provides flags for the Ginkgo test process, and CLI -var ReporterConfigFlags = GinkgoFlags{ - {KeyPath: "R.NoColor", Name: "no-color", SectionKey: "output", DeprecatedName: "noColor", DeprecatedDocLink: "changed-command-line-flags", - Usage: "If set, suppress color output in default reporter."}, - {KeyPath: "R.Verbose", Name: "v", SectionKey: "output", - Usage: "If set, emits more output including GinkgoWriter contents."}, - {KeyPath: "R.VeryVerbose", Name: "vv", SectionKey: "output", - Usage: "If set, emits with maximal verbosity - includes skipped and pending tests."}, - {KeyPath: "R.Succinct", Name: "succinct", SectionKey: "output", - Usage: "If set, default reporter prints out a very succinct report"}, - {KeyPath: "R.FullTrace", Name: "trace", SectionKey: "output", - Usage: "If set, default reporter prints out the full stack trace when a failure occurs"}, - {KeyPath: "R.ShowNodeEvents", Name: "show-node-events", SectionKey: "output", - Usage: "If set, default reporter prints node > Enter and < Exit events when specs fail"}, - - {KeyPath: "R.JSONReport", Name: "json-report", UsageArgument: "filename.json", SectionKey: "output", - Usage: "If set, Ginkgo will generate a JSON-formatted test report at the specified location."}, - {KeyPath: "R.JUnitReport", Name: "junit-report", UsageArgument: "filename.xml", SectionKey: "output", DeprecatedName: "reportFile", DeprecatedDocLink: "improved-reporting-infrastructure", - Usage: "If set, Ginkgo will generate a conformant junit test report in the specified file."}, - {KeyPath: "R.TeamcityReport", Name: "teamcity-report", UsageArgument: "filename", SectionKey: "output", - Usage: "If set, Ginkgo will generate a Teamcity-formatted test report at the specified location."}, - - {KeyPath: "D.SlowSpecThresholdWithFLoatUnits", DeprecatedName: "slowSpecThreshold", DeprecatedDocLink: "changed--slowspecthreshold", - Usage: "use --slow-spec-threshold instead and pass in a duration string (e.g. '5s', not '5.0')"}, - {KeyPath: "D.NoisyPendings", DeprecatedName: "noisyPendings", DeprecatedDocLink: "removed--noisypendings-and--noisyskippings", DeprecatedVersion: "2.0.0"}, - {KeyPath: "D.NoisySkippings", DeprecatedName: "noisySkippings", DeprecatedDocLink: "removed--noisypendings-and--noisyskippings", DeprecatedVersion: "2.0.0"}, - {KeyPath: "D.SlowSpecThreshold", DeprecatedName: "slow-spec-threshold", SectionKey: "output", Usage: "--slow-spec-threshold has been deprecated and will be removed in a future version of Ginkgo. This feature has proved to be more noisy than useful. You can use --poll-progress-after, instead, to get more actionable feedback about potentially slow specs and understand where they might be getting stuck.", DeprecatedVersion: "2.5.0"}, - {KeyPath: "D.AlwaysEmitGinkgoWriter", DeprecatedName: "always-emit-ginkgo-writer", SectionKey: "output", Usage: " - use -v instead, or one of Ginkgo's machine-readable report formats to get GinkgoWriter output for passing specs."}, -} - -// BuildTestSuiteFlagSet attaches to the CommandLine flagset and provides flags for the Ginkgo test process -func BuildTestSuiteFlagSet(suiteConfig *SuiteConfig, reporterConfig *ReporterConfig) (GinkgoFlagSet, error) { - flags := SuiteConfigFlags.CopyAppend(ParallelConfigFlags...).CopyAppend(ReporterConfigFlags...) - flags = flags.WithPrefix("ginkgo") - bindings := map[string]interface{}{ - "S": suiteConfig, - "R": reporterConfig, - "D": &deprecatedConfig{}, - } - extraGoFlagsSection := GinkgoFlagSection{Style: "{{gray}}", Heading: "Go test flags"} - - return NewAttachedGinkgoFlagSet(flag.CommandLine, flags, bindings, FlagSections, extraGoFlagsSection) -} - -// VetConfig validates that the Ginkgo test process' configuration is sound -func VetConfig(flagSet GinkgoFlagSet, suiteConfig SuiteConfig, reporterConfig ReporterConfig) []error { - errors := []error{} - - if flagSet.WasSet("count") || flagSet.WasSet("test.count") { - flag := flagSet.Lookup("count") - if flag == nil { - flag = flagSet.Lookup("test.count") - } - count, err := strconv.Atoi(flag.Value.String()) - if err != nil || count != 1 { - errors = append(errors, GinkgoErrors.InvalidGoFlagCount()) - } - } - - if flagSet.WasSet("parallel") || flagSet.WasSet("test.parallel") { - errors = append(errors, GinkgoErrors.InvalidGoFlagParallel()) - } - - if suiteConfig.ParallelTotal < 1 { - errors = append(errors, GinkgoErrors.InvalidParallelTotalConfiguration()) - } - - if suiteConfig.ParallelProcess > suiteConfig.ParallelTotal || suiteConfig.ParallelProcess < 1 { - errors = append(errors, GinkgoErrors.InvalidParallelProcessConfiguration()) - } - - if suiteConfig.ParallelTotal > 1 && suiteConfig.ParallelHost == "" { - errors = append(errors, GinkgoErrors.MissingParallelHostConfiguration()) - } - - if suiteConfig.DryRun && suiteConfig.ParallelTotal > 1 { - errors = append(errors, GinkgoErrors.DryRunInParallelConfiguration()) - } - - if suiteConfig.GracePeriod <= 0 { - errors = append(errors, GinkgoErrors.GracePeriodCannotBeZero()) - } - - if len(suiteConfig.FocusFiles) > 0 { - _, err := ParseFileFilters(suiteConfig.FocusFiles) - if err != nil { - errors = append(errors, err) - } - } - - if len(suiteConfig.SkipFiles) > 0 { - _, err := ParseFileFilters(suiteConfig.SkipFiles) - if err != nil { - errors = append(errors, err) - } - } - - if suiteConfig.LabelFilter != "" { - _, err := ParseLabelFilter(suiteConfig.LabelFilter) - if err != nil { - errors = append(errors, err) - } - } - - switch strings.ToLower(suiteConfig.OutputInterceptorMode) { - case "", "dup", "swap", "none": - default: - errors = append(errors, GinkgoErrors.InvalidOutputInterceptorModeConfiguration(suiteConfig.OutputInterceptorMode)) - } - - numVerbosity := 0 - for _, v := range []bool{reporterConfig.Succinct, reporterConfig.Verbose, reporterConfig.VeryVerbose} { - if v { - numVerbosity++ - } - } - if numVerbosity > 1 { - errors = append(errors, GinkgoErrors.ConflictingVerbosityConfiguration()) - } - - return errors -} - -// GinkgoCLISharedFlags provides flags shared by the Ginkgo CLI's build, watch, and run commands -var GinkgoCLISharedFlags = GinkgoFlags{ - {KeyPath: "C.Recurse", Name: "r", SectionKey: "multiple-suites", - Usage: "If set, ginkgo finds and runs test suites under the current directory recursively."}, - {KeyPath: "C.SkipPackage", Name: "skip-package", SectionKey: "multiple-suites", DeprecatedName: "skipPackage", DeprecatedDocLink: "changed-command-line-flags", - UsageArgument: "comma-separated list of packages", - Usage: "A comma-separated list of package names to be skipped. If any part of the package's path matches, that package is ignored."}, - {KeyPath: "C.RequireSuite", Name: "require-suite", SectionKey: "failure", DeprecatedName: "requireSuite", DeprecatedDocLink: "changed-command-line-flags", - Usage: "If set, Ginkgo fails if there are ginkgo tests in a directory but no invocation of RunSpecs."}, - {KeyPath: "C.NumCompilers", Name: "compilers", SectionKey: "multiple-suites", UsageDefaultValue: "0 (will autodetect)", - Usage: "When running multiple packages, the number of concurrent compilations to perform."}, -} - -// GinkgoCLIRunAndWatchFlags provides flags shared by the Ginkgo CLI's build and watch commands (but not run) -var GinkgoCLIRunAndWatchFlags = GinkgoFlags{ - {KeyPath: "C.Procs", Name: "procs", SectionKey: "parallel", UsageDefaultValue: "1 (run in series)", - Usage: "The number of parallel test nodes to run."}, - {KeyPath: "C.Procs", Name: "nodes", SectionKey: "parallel", UsageDefaultValue: "1 (run in series)", - Usage: "--nodes is an alias for --procs"}, - {KeyPath: "C.Parallel", Name: "p", SectionKey: "parallel", - Usage: "If set, ginkgo will run in parallel with an auto-detected number of nodes."}, - {KeyPath: "C.AfterRunHook", Name: "after-run-hook", SectionKey: "misc", DeprecatedName: "afterSuiteHook", DeprecatedDocLink: "changed-command-line-flags", - Usage: "Command to run when a test suite completes."}, - {KeyPath: "C.OutputDir", Name: "output-dir", SectionKey: "output", UsageArgument: "directory", DeprecatedName: "outputdir", DeprecatedDocLink: "improved-profiling-support", - Usage: "A location to place all generated profiles and reports."}, - {KeyPath: "C.KeepSeparateCoverprofiles", Name: "keep-separate-coverprofiles", SectionKey: "code-and-coverage-analysis", - Usage: "If set, Ginkgo does not merge coverprofiles into one monolithic coverprofile. The coverprofiles will remain in their respective package directories or in -output-dir if set."}, - {KeyPath: "C.KeepSeparateReports", Name: "keep-separate-reports", SectionKey: "output", - Usage: "If set, Ginkgo does not merge per-suite reports (e.g. -json-report) into one monolithic report for the entire testrun. The reports will remain in their respective package directories or in -output-dir if set."}, - - {KeyPath: "D.Stream", DeprecatedName: "stream", DeprecatedDocLink: "removed--stream", DeprecatedVersion: "2.0.0"}, - {KeyPath: "D.Notify", DeprecatedName: "notify", DeprecatedDocLink: "removed--notify", DeprecatedVersion: "2.0.0"}, -} - -// GinkgoCLIRunFlags provides flags for Ginkgo CLI's run command that aren't shared by any other commands -var GinkgoCLIRunFlags = GinkgoFlags{ - {KeyPath: "C.KeepGoing", Name: "keep-going", SectionKey: "multiple-suites", DeprecatedName: "keepGoing", DeprecatedDocLink: "changed-command-line-flags", - Usage: "If set, failures from earlier test suites do not prevent later test suites from running."}, - {KeyPath: "C.UntilItFails", Name: "until-it-fails", SectionKey: "debug", DeprecatedName: "untilItFails", DeprecatedDocLink: "changed-command-line-flags", - Usage: "If set, ginkgo will keep rerunning test suites until a failure occurs."}, - {KeyPath: "C.Repeat", Name: "repeat", SectionKey: "debug", UsageArgument: "n", UsageDefaultValue: "0 - i.e. no repetition, run only once", - Usage: "The number of times to re-run a test-suite. Useful for debugging flaky tests. If set to N the suite will be run N+1 times and will be required to pass each time."}, - {KeyPath: "C.RandomizeSuites", Name: "randomize-suites", SectionKey: "order", DeprecatedName: "randomizeSuites", DeprecatedDocLink: "changed-command-line-flags", - Usage: "If set, ginkgo will randomize the order in which test suites run."}, -} - -// GinkgoCLIRunFlags provides flags for Ginkgo CLI's watch command that aren't shared by any other commands -var GinkgoCLIWatchFlags = GinkgoFlags{ - {KeyPath: "C.Depth", Name: "depth", SectionKey: "watch", - Usage: "Ginkgo will watch dependencies down to this depth in the dependency tree."}, - {KeyPath: "C.WatchRegExp", Name: "watch-regexp", SectionKey: "watch", DeprecatedName: "watchRegExp", DeprecatedDocLink: "changed-command-line-flags", - UsageArgument: "Regular Expression", - UsageDefaultValue: `\.go$`, - Usage: "Only files matching this regular expression will be watched for changes."}, -} - -// GoBuildFlags provides flags for the Ginkgo CLI build, run, and watch commands that capture go's build-time flags. These are passed to go test -c by the ginkgo CLI -var GoBuildFlags = GinkgoFlags{ - {KeyPath: "Go.Race", Name: "race", SectionKey: "code-and-coverage-analysis", - Usage: "enable data race detection. Supported only on linux/amd64, freebsd/amd64, darwin/amd64, windows/amd64, linux/ppc64le and linux/arm64 (only for 48-bit VMA)."}, - {KeyPath: "Go.Vet", Name: "vet", UsageArgument: "list", SectionKey: "code-and-coverage-analysis", - Usage: `Configure the invocation of "go vet" during "go test" to use the comma-separated list of vet checks. If list is empty, "go test" runs "go vet" with a curated list of checks believed to be always worth addressing. If list is "off", "go test" does not run "go vet" at all. Available checks can be found by running 'go doc cmd/vet'`}, - {KeyPath: "Go.Cover", Name: "cover", SectionKey: "code-and-coverage-analysis", - Usage: "Enable coverage analysis. Note that because coverage works by annotating the source code before compilation, compilation and test failures with coverage enabled may report line numbers that don't correspond to the original sources."}, - {KeyPath: "Go.CoverMode", Name: "covermode", UsageArgument: "set,count,atomic", SectionKey: "code-and-coverage-analysis", - Usage: `Set the mode for coverage analysis for the package[s] being tested. 'set': does this statement run? 'count': how many times does this statement run? 'atomic': like count, but correct in multithreaded tests and more expensive (must use atomic with -race). Sets -cover`}, - {KeyPath: "Go.CoverPkg", Name: "coverpkg", UsageArgument: "pattern1,pattern2,pattern3", SectionKey: "code-and-coverage-analysis", - Usage: "Apply coverage analysis in each test to packages matching the patterns. The default is for each test to analyze only the package being tested. See 'go help packages' for a description of package patterns. Sets -cover."}, - - {KeyPath: "Go.A", Name: "a", SectionKey: "go-build", - Usage: "force rebuilding of packages that are already up-to-date."}, - {KeyPath: "Go.ASMFlags", Name: "asmflags", UsageArgument: "'[pattern=]arg list'", SectionKey: "go-build", - Usage: "arguments to pass on each go tool asm invocation."}, - {KeyPath: "Go.BuildMode", Name: "buildmode", UsageArgument: "mode", SectionKey: "go-build", - Usage: "build mode to use. See 'go help buildmode' for more."}, - {KeyPath: "Go.Compiler", Name: "compiler", UsageArgument: "name", SectionKey: "go-build", - Usage: "name of compiler to use, as in runtime.Compiler (gccgo or gc)."}, - {KeyPath: "Go.GCCGoFlags", Name: "gccgoflags", UsageArgument: "'[pattern=]arg list'", SectionKey: "go-build", - Usage: "arguments to pass on each gccgo compiler/linker invocation."}, - {KeyPath: "Go.GCFlags", Name: "gcflags", UsageArgument: "'[pattern=]arg list'", SectionKey: "go-build", - Usage: "arguments to pass on each go tool compile invocation."}, - {KeyPath: "Go.InstallSuffix", Name: "installsuffix", SectionKey: "go-build", - Usage: "a suffix to use in the name of the package installation directory, in order to keep output separate from default builds. If using the -race flag, the install suffix is automatically set to raceor, if set explicitly, has _race appended to it. Likewise for the -msan flag. Using a -buildmode option that requires non-default compile flags has a similar effect."}, - {KeyPath: "Go.LDFlags", Name: "ldflags", UsageArgument: "'[pattern=]arg list'", SectionKey: "go-build", - Usage: "arguments to pass on each go tool link invocation."}, - {KeyPath: "Go.LinkShared", Name: "linkshared", SectionKey: "go-build", - Usage: "build code that will be linked against shared libraries previously created with -buildmode=shared."}, - {KeyPath: "Go.Mod", Name: "mod", UsageArgument: "mode (readonly, vendor, or mod)", SectionKey: "go-build", - Usage: "module download mode to use: readonly, vendor, or mod. See 'go help modules' for more."}, - {KeyPath: "Go.ModCacheRW", Name: "modcacherw", SectionKey: "go-build", - Usage: "leave newly-created directories in the module cache read-write instead of making them read-only."}, - {KeyPath: "Go.ModFile", Name: "modfile", UsageArgument: "file", SectionKey: "go-build", - Usage: `in module aware mode, read (and possibly write) an alternate go.mod file instead of the one in the module root directory. A file named go.mod must still be present in order to determine the module root directory, but it is not accessed. When -modfile is specified, an alternate go.sum file is also used: its path is derived from the -modfile flag by trimming the ".mod" extension and appending ".sum".`}, - {KeyPath: "Go.MSan", Name: "msan", SectionKey: "go-build", - Usage: "enable interoperation with memory sanitizer. Supported only on linux/amd64, linux/arm64 and only with Clang/LLVM as the host C compiler. On linux/arm64, pie build mode will be used."}, - {KeyPath: "Go.N", Name: "n", SectionKey: "go-build", - Usage: "print the commands but do not run them."}, - {KeyPath: "Go.PkgDir", Name: "pkgdir", UsageArgument: "dir", SectionKey: "go-build", - Usage: "install and load all packages from dir instead of the usual locations. For example, when building with a non-standard configuration, use -pkgdir to keep generated packages in a separate location."}, - {KeyPath: "Go.Tags", Name: "tags", UsageArgument: "tag,list", SectionKey: "go-build", - Usage: "a comma-separated list of build tags to consider satisfied during the build. For more information about build tags, see the description of build constraints in the documentation for the go/build package. (Earlier versions of Go used a space-separated list, and that form is deprecated but still recognized.)"}, - {KeyPath: "Go.TrimPath", Name: "trimpath", SectionKey: "go-build", - Usage: `remove all file system paths from the resulting executable. Instead of absolute file system paths, the recorded file names will begin with either "go" (for the standard library), or a module path@version (when using modules), or a plain import path (when using GOPATH).`}, - {KeyPath: "Go.ToolExec", Name: "toolexec", UsageArgument: "'cmd args'", SectionKey: "go-build", - Usage: "a program to use to invoke toolchain programs like vet and asm. For example, instead of running asm, the go command will run cmd args /path/to/asm '."}, - {KeyPath: "Go.Work", Name: "work", SectionKey: "go-build", - Usage: "print the name of the temporary work directory and do not delete it when exiting."}, - {KeyPath: "Go.X", Name: "x", SectionKey: "go-build", - Usage: "print the commands."}, -} - -// GoRunFlags provides flags for the Ginkgo CLI run, and watch commands that capture go's run-time flags. These are passed to the compiled test binary by the ginkgo CLI -var GoRunFlags = GinkgoFlags{ - {KeyPath: "Go.CoverProfile", Name: "coverprofile", UsageArgument: "file", SectionKey: "code-and-coverage-analysis", - Usage: `Write a coverage profile to the file after all tests have passed. Sets -cover.`}, - {KeyPath: "Go.BlockProfile", Name: "blockprofile", UsageArgument: "file", SectionKey: "performance-analysis", - Usage: `Write a goroutine blocking profile to the specified file when all tests are complete. Preserves test binary.`}, - {KeyPath: "Go.BlockProfileRate", Name: "blockprofilerate", UsageArgument: "rate", SectionKey: "performance-analysis", - Usage: `Control the detail provided in goroutine blocking profiles by calling runtime.SetBlockProfileRate with rate. See 'go doc runtime.SetBlockProfileRate'. The profiler aims to sample, on average, one blocking event every n nanoseconds the program spends blocked. By default, if -test.blockprofile is set without this flag, all blocking events are recorded, equivalent to -test.blockprofilerate=1.`}, - {KeyPath: "Go.CPUProfile", Name: "cpuprofile", UsageArgument: "file", SectionKey: "performance-analysis", - Usage: `Write a CPU profile to the specified file before exiting. Preserves test binary.`}, - {KeyPath: "Go.MemProfile", Name: "memprofile", UsageArgument: "file", SectionKey: "performance-analysis", - Usage: `Write an allocation profile to the file after all tests have passed. Preserves test binary.`}, - {KeyPath: "Go.MemProfileRate", Name: "memprofilerate", UsageArgument: "rate", SectionKey: "performance-analysis", - Usage: `Enable more precise (and expensive) memory allocation profiles by setting runtime.MemProfileRate. See 'go doc runtime.MemProfileRate'. To profile all memory allocations, use -test.memprofilerate=1.`}, - {KeyPath: "Go.MutexProfile", Name: "mutexprofile", UsageArgument: "file", SectionKey: "performance-analysis", - Usage: `Write a mutex contention profile to the specified file when all tests are complete. Preserves test binary.`}, - {KeyPath: "Go.MutexProfileFraction", Name: "mutexprofilefraction", UsageArgument: "n", SectionKey: "performance-analysis", - Usage: `if >= 0, calls runtime.SetMutexProfileFraction() Sample 1 in n stack traces of goroutines holding a contended mutex.`}, - {KeyPath: "Go.Trace", Name: "execution-trace", UsageArgument: "file", ExportAs: "trace", SectionKey: "performance-analysis", - Usage: `Write an execution trace to the specified file before exiting.`}, -} - -// VetAndInitializeCLIAndGoConfig validates that the Ginkgo CLI's configuration is sound -// It returns a potentially mutated copy of the config that rationalizes the configuration to ensure consistency for downstream consumers -func VetAndInitializeCLIAndGoConfig(cliConfig CLIConfig, goFlagsConfig GoFlagsConfig) (CLIConfig, GoFlagsConfig, []error) { - errors := []error{} - - if cliConfig.Repeat > 0 && cliConfig.UntilItFails { - errors = append(errors, GinkgoErrors.BothRepeatAndUntilItFails()) - } - - //initialize the output directory - if cliConfig.OutputDir != "" { - err := os.MkdirAll(cliConfig.OutputDir, 0777) - if err != nil { - errors = append(errors, err) - } - } - - //ensure cover mode is configured appropriately - if goFlagsConfig.CoverMode != "" || goFlagsConfig.CoverPkg != "" || goFlagsConfig.CoverProfile != "" { - goFlagsConfig.Cover = true - } - if goFlagsConfig.Cover && goFlagsConfig.CoverProfile == "" { - goFlagsConfig.CoverProfile = "coverprofile.out" - } - - return cliConfig, goFlagsConfig, errors -} - -// GenerateGoTestCompileArgs is used by the Ginkgo CLI to generate command line arguments to pass to the go test -c command when compiling the test -func GenerateGoTestCompileArgs(goFlagsConfig GoFlagsConfig, destination string, packageToBuild string, pathToInvocationPath string) ([]string, error) { - // if the user has set the CoverProfile run-time flag make sure to set the build-time cover flag to make sure - // the built test binary can generate a coverprofile - if goFlagsConfig.CoverProfile != "" { - goFlagsConfig.Cover = true - } - - if goFlagsConfig.CoverPkg != "" { - coverPkgs := strings.Split(goFlagsConfig.CoverPkg, ",") - adjustedCoverPkgs := make([]string, len(coverPkgs)) - for i, coverPkg := range coverPkgs { - coverPkg = strings.Trim(coverPkg, " ") - if strings.HasPrefix(coverPkg, "./") { - // this is a relative coverPkg - we need to reroot it - adjustedCoverPkgs[i] = "./" + filepath.Join(pathToInvocationPath, strings.TrimPrefix(coverPkg, "./")) - } else { - // this is a package name - don't touch it - adjustedCoverPkgs[i] = coverPkg - } - } - goFlagsConfig.CoverPkg = strings.Join(adjustedCoverPkgs, ",") - } - - args := []string{"test", "-c", "-o", destination, packageToBuild} - goArgs, err := GenerateFlagArgs( - GoBuildFlags, - map[string]interface{}{ - "Go": &goFlagsConfig, - }, - ) - - if err != nil { - return []string{}, err - } - args = append(args, goArgs...) - return args, nil -} - -// GenerateGinkgoTestRunArgs is used by the Ginkgo CLI to generate command line arguments to pass to the compiled Ginkgo test binary -func GenerateGinkgoTestRunArgs(suiteConfig SuiteConfig, reporterConfig ReporterConfig, goFlagsConfig GoFlagsConfig) ([]string, error) { - var flags GinkgoFlags - flags = SuiteConfigFlags.WithPrefix("ginkgo") - flags = flags.CopyAppend(ParallelConfigFlags.WithPrefix("ginkgo")...) - flags = flags.CopyAppend(ReporterConfigFlags.WithPrefix("ginkgo")...) - flags = flags.CopyAppend(GoRunFlags.WithPrefix("test")...) - bindings := map[string]interface{}{ - "S": &suiteConfig, - "R": &reporterConfig, - "Go": &goFlagsConfig, - } - - return GenerateFlagArgs(flags, bindings) -} - -// GenerateGoTestRunArgs is used by the Ginkgo CLI to generate command line arguments to pass to the compiled non-Ginkgo test binary -func GenerateGoTestRunArgs(goFlagsConfig GoFlagsConfig) ([]string, error) { - flags := GoRunFlags.WithPrefix("test") - bindings := map[string]interface{}{ - "Go": &goFlagsConfig, - } - - args, err := GenerateFlagArgs(flags, bindings) - if err != nil { - return args, err - } - args = append(args, "--test.v") - return args, nil -} - -// BuildRunCommandFlagSet builds the FlagSet for the `ginkgo run` command -func BuildRunCommandFlagSet(suiteConfig *SuiteConfig, reporterConfig *ReporterConfig, cliConfig *CLIConfig, goFlagsConfig *GoFlagsConfig) (GinkgoFlagSet, error) { - flags := SuiteConfigFlags - flags = flags.CopyAppend(ReporterConfigFlags...) - flags = flags.CopyAppend(GinkgoCLISharedFlags...) - flags = flags.CopyAppend(GinkgoCLIRunAndWatchFlags...) - flags = flags.CopyAppend(GinkgoCLIRunFlags...) - flags = flags.CopyAppend(GoBuildFlags...) - flags = flags.CopyAppend(GoRunFlags...) - - bindings := map[string]interface{}{ - "S": suiteConfig, - "R": reporterConfig, - "C": cliConfig, - "Go": goFlagsConfig, - "D": &deprecatedConfig{}, - } - - return NewGinkgoFlagSet(flags, bindings, FlagSections) -} - -// BuildWatchCommandFlagSet builds the FlagSet for the `ginkgo watch` command -func BuildWatchCommandFlagSet(suiteConfig *SuiteConfig, reporterConfig *ReporterConfig, cliConfig *CLIConfig, goFlagsConfig *GoFlagsConfig) (GinkgoFlagSet, error) { - flags := SuiteConfigFlags - flags = flags.CopyAppend(ReporterConfigFlags...) - flags = flags.CopyAppend(GinkgoCLISharedFlags...) - flags = flags.CopyAppend(GinkgoCLIRunAndWatchFlags...) - flags = flags.CopyAppend(GinkgoCLIWatchFlags...) - flags = flags.CopyAppend(GoBuildFlags...) - flags = flags.CopyAppend(GoRunFlags...) - - bindings := map[string]interface{}{ - "S": suiteConfig, - "R": reporterConfig, - "C": cliConfig, - "Go": goFlagsConfig, - "D": &deprecatedConfig{}, - } - - return NewGinkgoFlagSet(flags, bindings, FlagSections) -} - -// BuildBuildCommandFlagSet builds the FlagSet for the `ginkgo build` command -func BuildBuildCommandFlagSet(cliConfig *CLIConfig, goFlagsConfig *GoFlagsConfig) (GinkgoFlagSet, error) { - flags := GinkgoCLISharedFlags - flags = flags.CopyAppend(GoBuildFlags...) - - bindings := map[string]interface{}{ - "C": cliConfig, - "Go": goFlagsConfig, - "D": &deprecatedConfig{}, - } - - flagSections := make(GinkgoFlagSections, len(FlagSections)) - copy(flagSections, FlagSections) - for i := range flagSections { - if flagSections[i].Key == "multiple-suites" { - flagSections[i].Heading = "Building Multiple Suites" - } - if flagSections[i].Key == "go-build" { - flagSections[i] = GinkgoFlagSection{Key: "go-build", Style: "{{/}}", Heading: "Go Build Flags", - Description: "These flags are inherited from go build."} - } - } - - return NewGinkgoFlagSet(flags, bindings, flagSections) -} - -func BuildLabelsCommandFlagSet(cliConfig *CLIConfig) (GinkgoFlagSet, error) { - flags := GinkgoCLISharedFlags.SubsetWithNames("r", "skip-package") - - bindings := map[string]interface{}{ - "C": cliConfig, - } - - flagSections := make(GinkgoFlagSections, len(FlagSections)) - copy(flagSections, FlagSections) - for i := range flagSections { - if flagSections[i].Key == "multiple-suites" { - flagSections[i].Heading = "Fetching Labels from Multiple Suites" - } - } - - return NewGinkgoFlagSet(flags, bindings, flagSections) -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/deprecated_types.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/deprecated_types.go deleted file mode 100644 index 17922304b..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/deprecated_types.go +++ /dev/null @@ -1,141 +0,0 @@ -package types - -import ( - "strconv" - "time" -) - -/* - A set of deprecations to make the transition from v1 to v2 easier for users who have written custom reporters. -*/ - -type SuiteSummary = DeprecatedSuiteSummary -type SetupSummary = DeprecatedSetupSummary -type SpecSummary = DeprecatedSpecSummary -type SpecMeasurement = DeprecatedSpecMeasurement -type SpecComponentType = NodeType -type SpecFailure = DeprecatedSpecFailure - -var ( - SpecComponentTypeInvalid = NodeTypeInvalid - SpecComponentTypeContainer = NodeTypeContainer - SpecComponentTypeIt = NodeTypeIt - SpecComponentTypeBeforeEach = NodeTypeBeforeEach - SpecComponentTypeJustBeforeEach = NodeTypeJustBeforeEach - SpecComponentTypeAfterEach = NodeTypeAfterEach - SpecComponentTypeJustAfterEach = NodeTypeJustAfterEach - SpecComponentTypeBeforeSuite = NodeTypeBeforeSuite - SpecComponentTypeSynchronizedBeforeSuite = NodeTypeSynchronizedBeforeSuite - SpecComponentTypeAfterSuite = NodeTypeAfterSuite - SpecComponentTypeSynchronizedAfterSuite = NodeTypeSynchronizedAfterSuite -) - -type DeprecatedSuiteSummary struct { - SuiteDescription string - SuiteSucceeded bool - SuiteID string - - NumberOfSpecsBeforeParallelization int - NumberOfTotalSpecs int - NumberOfSpecsThatWillBeRun int - NumberOfPendingSpecs int - NumberOfSkippedSpecs int - NumberOfPassedSpecs int - NumberOfFailedSpecs int - NumberOfFlakedSpecs int - RunTime time.Duration -} - -type DeprecatedSetupSummary struct { - ComponentType SpecComponentType - CodeLocation CodeLocation - - State SpecState - RunTime time.Duration - Failure SpecFailure - - CapturedOutput string - SuiteID string -} - -type DeprecatedSpecSummary struct { - ComponentTexts []string - ComponentCodeLocations []CodeLocation - - State SpecState - RunTime time.Duration - Failure SpecFailure - IsMeasurement bool - NumberOfSamples int - Measurements map[string]*DeprecatedSpecMeasurement - - CapturedOutput string - SuiteID string -} - -func (s DeprecatedSpecSummary) HasFailureState() bool { - return s.State.Is(SpecStateFailureStates) -} - -func (s DeprecatedSpecSummary) TimedOut() bool { - return false -} - -func (s DeprecatedSpecSummary) Panicked() bool { - return s.State == SpecStatePanicked -} - -func (s DeprecatedSpecSummary) Failed() bool { - return s.State == SpecStateFailed -} - -func (s DeprecatedSpecSummary) Passed() bool { - return s.State == SpecStatePassed -} - -func (s DeprecatedSpecSummary) Skipped() bool { - return s.State == SpecStateSkipped -} - -func (s DeprecatedSpecSummary) Pending() bool { - return s.State == SpecStatePending -} - -type DeprecatedSpecFailure struct { - Message string - Location CodeLocation - ForwardedPanic string - - ComponentIndex int - ComponentType SpecComponentType - ComponentCodeLocation CodeLocation -} - -type DeprecatedSpecMeasurement struct { - Name string - Info interface{} - Order int - - Results []float64 - - Smallest float64 - Largest float64 - Average float64 - StdDeviation float64 - - SmallestLabel string - LargestLabel string - AverageLabel string - Units string - Precision int -} - -func (s DeprecatedSpecMeasurement) PrecisionFmt() string { - if s.Precision == 0 { - return "%f" - } - - str := strconv.Itoa(s.Precision) - - return "%." + str + "f" -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/deprecation_support.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/deprecation_support.go deleted file mode 100644 index e2519f673..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/deprecation_support.go +++ /dev/null @@ -1,177 +0,0 @@ -package types - -import ( - "os" - "strconv" - "strings" - "sync" - "unicode" - - "github.com/onsi/ginkgo/v2/formatter" -) - -type Deprecation struct { - Message string - DocLink string - Version string -} - -type deprecations struct{} - -var Deprecations = deprecations{} - -func (d deprecations) CustomReporter() Deprecation { - return Deprecation{ - Message: "Support for custom reporters has been removed in V2. Please read the documentation linked to below for Ginkgo's new behavior and for a migration path:", - DocLink: "removed-custom-reporters", - Version: "1.16.0", - } -} - -func (d deprecations) Async() Deprecation { - return Deprecation{ - Message: "You are passing a Done channel to a test node to test asynchronous behavior. This is deprecated in Ginkgo V2. Your test will run synchronously and the timeout will be ignored.", - DocLink: "removed-async-testing", - Version: "1.16.0", - } -} - -func (d deprecations) Measure() Deprecation { - return Deprecation{ - Message: "Measure is deprecated and has been removed from Ginkgo V2. Any Measure tests in your spec will not run. Please migrate to gomega/gmeasure.", - DocLink: "removed-measure", - Version: "1.16.3", - } -} - -func (d deprecations) ParallelNode() Deprecation { - return Deprecation{ - Message: "GinkgoParallelNode is deprecated and will be removed in Ginkgo V2. Please use GinkgoParallelProcess instead.", - DocLink: "renamed-ginkgoparallelnode", - Version: "1.16.4", - } -} - -func (d deprecations) CurrentGinkgoTestDescription() Deprecation { - return Deprecation{ - Message: "CurrentGinkgoTestDescription() is deprecated in Ginkgo V2. Use CurrentSpecReport() instead.", - DocLink: "changed-currentginkgotestdescription", - Version: "1.16.0", - } -} - -func (d deprecations) Convert() Deprecation { - return Deprecation{ - Message: "The convert command is deprecated in Ginkgo V2", - DocLink: "removed-ginkgo-convert", - Version: "1.16.0", - } -} - -func (d deprecations) Blur() Deprecation { - return Deprecation{ - Message: "The blur command is deprecated in Ginkgo V2. Use 'ginkgo unfocus' instead.", - Version: "1.16.0", - } -} - -func (d deprecations) Nodot() Deprecation { - return Deprecation{ - Message: "The nodot command is deprecated in Ginkgo V2. Please either dot-import Ginkgo or use the package identifier in your code to references objects and types provided by Ginkgo and Gomega.", - DocLink: "removed-ginkgo-nodot", - Version: "1.16.0", - } -} - -func (d deprecations) SuppressProgressReporting() Deprecation { - return Deprecation{ - Message: "Improvements to how reporters emit timeline information means that SuppressProgressReporting is no longer necessary and has been deprecated.", - Version: "2.5.0", - } -} - -type DeprecationTracker struct { - deprecations map[Deprecation][]CodeLocation - lock *sync.Mutex -} - -func NewDeprecationTracker() *DeprecationTracker { - return &DeprecationTracker{ - deprecations: map[Deprecation][]CodeLocation{}, - lock: &sync.Mutex{}, - } -} - -func (d *DeprecationTracker) TrackDeprecation(deprecation Deprecation, cl ...CodeLocation) { - ackVersion := os.Getenv("ACK_GINKGO_DEPRECATIONS") - if deprecation.Version != "" && ackVersion != "" { - ack := ParseSemVer(ackVersion) - version := ParseSemVer(deprecation.Version) - if ack.GreaterThanOrEqualTo(version) { - return - } - } - - d.lock.Lock() - defer d.lock.Unlock() - if len(cl) == 1 { - d.deprecations[deprecation] = append(d.deprecations[deprecation], cl[0]) - } else { - d.deprecations[deprecation] = []CodeLocation{} - } -} - -func (d *DeprecationTracker) DidTrackDeprecations() bool { - d.lock.Lock() - defer d.lock.Unlock() - return len(d.deprecations) > 0 -} - -func (d *DeprecationTracker) DeprecationsReport() string { - d.lock.Lock() - defer d.lock.Unlock() - out := formatter.F("{{light-yellow}}You're using deprecated Ginkgo functionality:{{/}}\n") - out += formatter.F("{{light-yellow}}============================================={{/}}\n") - for deprecation, locations := range d.deprecations { - out += formatter.Fi(1, "{{yellow}}"+deprecation.Message+"{{/}}\n") - if deprecation.DocLink != "" { - out += formatter.Fi(1, "{{bold}}Learn more at:{{/}} {{cyan}}{{underline}}https://onsi.github.io/ginkgo/MIGRATING_TO_V2#%s{{/}}\n", deprecation.DocLink) - } - for _, location := range locations { - out += formatter.Fi(2, "{{gray}}%s{{/}}\n", location) - } - } - out += formatter.F("\n{{gray}}To silence deprecations that can be silenced set the following environment variable:{{/}}\n") - out += formatter.Fi(1, "{{gray}}ACK_GINKGO_DEPRECATIONS=%s{{/}}\n", VERSION) - return out -} - -type SemVer struct { - Major int - Minor int - Patch int -} - -func (s SemVer) GreaterThanOrEqualTo(o SemVer) bool { - return (s.Major > o.Major) || - (s.Major == o.Major && s.Minor > o.Minor) || - (s.Major == o.Major && s.Minor == o.Minor && s.Patch >= o.Patch) -} - -func ParseSemVer(semver string) SemVer { - out := SemVer{} - semver = strings.TrimFunc(semver, func(r rune) bool { - return !(unicode.IsNumber(r) || r == '.') - }) - components := strings.Split(semver, ".") - if len(components) > 0 { - out.Major, _ = strconv.Atoi(components[0]) - } - if len(components) > 1 { - out.Minor, _ = strconv.Atoi(components[1]) - } - if len(components) > 2 { - out.Patch, _ = strconv.Atoi(components[2]) - } - return out -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/enum_support.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/enum_support.go deleted file mode 100644 index 1d96ae028..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/enum_support.go +++ /dev/null @@ -1,43 +0,0 @@ -package types - -import "encoding/json" - -type EnumSupport struct { - toString map[uint]string - toEnum map[string]uint - maxEnum uint -} - -func NewEnumSupport(toString map[uint]string) EnumSupport { - toEnum, maxEnum := map[string]uint{}, uint(0) - for k, v := range toString { - toEnum[v] = k - if maxEnum < k { - maxEnum = k - } - } - return EnumSupport{toString: toString, toEnum: toEnum, maxEnum: maxEnum} -} - -func (es EnumSupport) String(e uint) string { - if e > es.maxEnum { - return es.toString[0] - } - return es.toString[e] -} - -func (es EnumSupport) UnmarshJSON(b []byte) (uint, error) { - var dec string - if err := json.Unmarshal(b, &dec); err != nil { - return 0, err - } - out := es.toEnum[dec] // if we miss we get 0 which is what we want anyway - return out, nil -} - -func (es EnumSupport) MarshJSON(e uint) ([]byte, error) { - if e == 0 || e > es.maxEnum { - return json.Marshal(nil) - } - return json.Marshal(es.toString[e]) -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/errors.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/errors.go deleted file mode 100644 index 6bb72d00c..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/errors.go +++ /dev/null @@ -1,639 +0,0 @@ -package types - -import ( - "fmt" - "reflect" - "strings" - - "github.com/onsi/ginkgo/v2/formatter" -) - -type GinkgoError struct { - Heading string - Message string - DocLink string - CodeLocation CodeLocation -} - -func (g GinkgoError) Error() string { - out := formatter.F("{{bold}}{{red}}%s{{/}}\n", g.Heading) - if (g.CodeLocation != CodeLocation{}) { - contentsOfLine := strings.TrimLeft(g.CodeLocation.ContentsOfLine(), "\t ") - if contentsOfLine != "" { - out += formatter.F("{{light-gray}}%s{{/}}\n", contentsOfLine) - } - out += formatter.F("{{gray}}%s{{/}}\n", g.CodeLocation) - } - if g.Message != "" { - out += formatter.Fiw(1, formatter.COLS, g.Message) - out += "\n\n" - } - if g.DocLink != "" { - out += formatter.Fiw(1, formatter.COLS, "{{bold}}Learn more at:{{/}} {{cyan}}{{underline}}http://onsi.github.io/ginkgo/#%s{{/}}\n", g.DocLink) - } - - return out -} - -type ginkgoErrors struct{} - -var GinkgoErrors = ginkgoErrors{} - -func (g ginkgoErrors) UncaughtGinkgoPanic(cl CodeLocation) error { - return GinkgoError{ - Heading: "Your Test Panicked", - Message: `When you, or your assertion library, calls Ginkgo's Fail(), -Ginkgo panics to prevent subsequent assertions from running. - -Normally Ginkgo rescues this panic so you shouldn't see it. - -However, if you make an assertion in a goroutine, Ginkgo can't capture the panic. -To circumvent this, you should call - - defer GinkgoRecover() - -at the top of the goroutine that caused this panic. - -Alternatively, you may have made an assertion outside of a Ginkgo -leaf node (e.g. in a container node or some out-of-band function) - please move your assertion to -an appropriate Ginkgo node (e.g. a BeforeSuite, BeforeEach, It, etc...).`, - DocLink: "mental-model-how-ginkgo-handles-failure", - CodeLocation: cl, - } -} - -func (g ginkgoErrors) RerunningSuite() error { - return GinkgoError{ - Heading: "Rerunning Suite", - Message: formatter.F(`It looks like you are calling RunSpecs more than once. Ginkgo does not support rerunning suites. If you want to rerun a suite try {{bold}}ginkgo --repeat=N{{/}} or {{bold}}ginkgo --until-it-fails{{/}}`), - DocLink: "repeating-spec-runs-and-managing-flaky-specs", - } -} - -/* Tree construction errors */ - -func (g ginkgoErrors) PushingNodeInRunPhase(nodeType NodeType, cl CodeLocation) error { - return GinkgoError{ - Heading: "Ginkgo detected an issue with your spec structure", - Message: formatter.F( - `It looks like you are trying to add a {{bold}}[%s]{{/}} node -to the Ginkgo spec tree in a leaf node {{bold}}after{{/}} the specs started running. - -To enable randomization and parallelization Ginkgo requires the spec tree -to be fully constructed up front. In practice, this means that you can -only create nodes like {{bold}}[%s]{{/}} at the top-level or within the -body of a {{bold}}Describe{{/}}, {{bold}}Context{{/}}, or {{bold}}When{{/}}.`, nodeType, nodeType), - CodeLocation: cl, - DocLink: "mental-model-how-ginkgo-traverses-the-spec-hierarchy", - } -} - -func (g ginkgoErrors) CaughtPanicDuringABuildPhase(caughtPanic interface{}, cl CodeLocation) error { - return GinkgoError{ - Heading: "Assertion or Panic detected during tree construction", - Message: formatter.F( - `Ginkgo detected a panic while constructing the spec tree. -You may be trying to make an assertion in the body of a container node -(i.e. {{bold}}Describe{{/}}, {{bold}}Context{{/}}, or {{bold}}When{{/}}). - -Please ensure all assertions are inside leaf nodes such as {{bold}}BeforeEach{{/}}, -{{bold}}It{{/}}, etc. - -{{bold}}Here's the content of the panic that was caught:{{/}} -%v`, caughtPanic), - CodeLocation: cl, - DocLink: "no-assertions-in-container-nodes", - } -} - -func (g ginkgoErrors) SuiteNodeInNestedContext(nodeType NodeType, cl CodeLocation) error { - docLink := "suite-setup-and-cleanup-beforesuite-and-aftersuite" - if nodeType.Is(NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite) { - docLink = "reporting-nodes---reportbeforesuite-and-reportaftersuite" - } - - return GinkgoError{ - Heading: "Ginkgo detected an issue with your spec structure", - Message: formatter.F( - `It looks like you are trying to add a {{bold}}[%s]{{/}} node within a container node. - -{{bold}}%s{{/}} can only be called at the top level.`, nodeType, nodeType), - CodeLocation: cl, - DocLink: docLink, - } -} - -func (g ginkgoErrors) SuiteNodeDuringRunPhase(nodeType NodeType, cl CodeLocation) error { - docLink := "suite-setup-and-cleanup-beforesuite-and-aftersuite" - if nodeType.Is(NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite) { - docLink = "reporting-nodes---reportbeforesuite-and-reportaftersuite" - } - - return GinkgoError{ - Heading: "Ginkgo detected an issue with your spec structure", - Message: formatter.F( - `It looks like you are trying to add a {{bold}}[%s]{{/}} node within a leaf node after the spec started running. - -{{bold}}%s{{/}} can only be called at the top level.`, nodeType, nodeType), - CodeLocation: cl, - DocLink: docLink, - } -} - -func (g ginkgoErrors) MultipleBeforeSuiteNodes(nodeType NodeType, cl CodeLocation, earlierNodeType NodeType, earlierCodeLocation CodeLocation) error { - return ginkgoErrorMultipleSuiteNodes("setup", nodeType, cl, earlierNodeType, earlierCodeLocation) -} - -func (g ginkgoErrors) MultipleAfterSuiteNodes(nodeType NodeType, cl CodeLocation, earlierNodeType NodeType, earlierCodeLocation CodeLocation) error { - return ginkgoErrorMultipleSuiteNodes("teardown", nodeType, cl, earlierNodeType, earlierCodeLocation) -} - -func ginkgoErrorMultipleSuiteNodes(setupOrTeardown string, nodeType NodeType, cl CodeLocation, earlierNodeType NodeType, earlierCodeLocation CodeLocation) error { - return GinkgoError{ - Heading: "Ginkgo detected an issue with your spec structure", - Message: formatter.F( - `It looks like you are trying to add a {{bold}}[%s]{{/}} node but -you already have a {{bold}}[%s]{{/}} node defined at: {{gray}}%s{{/}}. - -Ginkgo only allows you to define one suite %s node.`, nodeType, earlierNodeType, earlierCodeLocation, setupOrTeardown), - CodeLocation: cl, - DocLink: "suite-setup-and-cleanup-beforesuite-and-aftersuite", - } -} - -/* Decorator errors */ -func (g ginkgoErrors) InvalidDecoratorForNodeType(cl CodeLocation, nodeType NodeType, decorator string) error { - return GinkgoError{ - Heading: "Invalid Decorator", - Message: formatter.F(`[%s] node cannot be passed a(n) '%s' decorator`, nodeType, decorator), - CodeLocation: cl, - DocLink: "node-decorators-overview", - } -} - -func (g ginkgoErrors) InvalidDeclarationOfFocusedAndPending(cl CodeLocation, nodeType NodeType) error { - return GinkgoError{ - Heading: "Invalid Combination of Decorators: Focused and Pending", - Message: formatter.F(`[%s] node was decorated with both Focus and Pending. At most one is allowed.`, nodeType), - CodeLocation: cl, - DocLink: "node-decorators-overview", - } -} - -func (g ginkgoErrors) InvalidDeclarationOfFlakeAttemptsAndMustPassRepeatedly(cl CodeLocation, nodeType NodeType) error { - return GinkgoError{ - Heading: "Invalid Combination of Decorators: FlakeAttempts and MustPassRepeatedly", - Message: formatter.F(`[%s] node was decorated with both FlakeAttempts and MustPassRepeatedly. At most one is allowed.`, nodeType), - CodeLocation: cl, - DocLink: "node-decorators-overview", - } -} - -func (g ginkgoErrors) UnknownDecorator(cl CodeLocation, nodeType NodeType, decorator interface{}) error { - return GinkgoError{ - Heading: "Unknown Decorator", - Message: formatter.F(`[%s] node was passed an unknown decorator: '%#v'`, nodeType, decorator), - CodeLocation: cl, - DocLink: "node-decorators-overview", - } -} - -func (g ginkgoErrors) InvalidBodyTypeForContainer(t reflect.Type, cl CodeLocation, nodeType NodeType) error { - return GinkgoError{ - Heading: "Invalid Function", - Message: formatter.F(`[%s] node must be passed {{bold}}func(){{/}} - i.e. functions that take nothing and return nothing. You passed {{bold}}%s{{/}} instead.`, nodeType, t), - CodeLocation: cl, - DocLink: "node-decorators-overview", - } -} - -func (g ginkgoErrors) InvalidBodyType(t reflect.Type, cl CodeLocation, nodeType NodeType) error { - mustGet := "{{bold}}func(){{/}}, {{bold}}func(ctx SpecContext){{/}}, or {{bold}}func(ctx context.Context){{/}}" - if nodeType.Is(NodeTypeContainer) { - mustGet = "{{bold}}func(){{/}}" - } - return GinkgoError{ - Heading: "Invalid Function", - Message: formatter.F(`[%s] node must be passed `+mustGet+`. -You passed {{bold}}%s{{/}} instead.`, nodeType, t), - CodeLocation: cl, - DocLink: "node-decorators-overview", - } -} - -func (g ginkgoErrors) InvalidBodyTypeForSynchronizedBeforeSuiteProc1(t reflect.Type, cl CodeLocation) error { - mustGet := "{{bold}}func() []byte{{/}}, {{bold}}func(ctx SpecContext) []byte{{/}}, or {{bold}}func(ctx context.Context) []byte{{/}}, {{bold}}func(){{/}}, {{bold}}func(ctx SpecContext){{/}}, or {{bold}}func(ctx context.Context){{/}}" - return GinkgoError{ - Heading: "Invalid Function", - Message: formatter.F(`[SynchronizedBeforeSuite] node must be passed `+mustGet+` for its first function. -You passed {{bold}}%s{{/}} instead.`, t), - CodeLocation: cl, - DocLink: "node-decorators-overview", - } -} - -func (g ginkgoErrors) InvalidBodyTypeForSynchronizedBeforeSuiteAllProcs(t reflect.Type, cl CodeLocation) error { - mustGet := "{{bold}}func(){{/}}, {{bold}}func(ctx SpecContext){{/}}, or {{bold}}func(ctx context.Context){{/}}, {{bold}}func([]byte){{/}}, {{bold}}func(ctx SpecContext, []byte){{/}}, or {{bold}}func(ctx context.Context, []byte){{/}}" - return GinkgoError{ - Heading: "Invalid Function", - Message: formatter.F(`[SynchronizedBeforeSuite] node must be passed `+mustGet+` for its second function. -You passed {{bold}}%s{{/}} instead.`, t), - CodeLocation: cl, - DocLink: "node-decorators-overview", - } -} - -func (g ginkgoErrors) MultipleBodyFunctions(cl CodeLocation, nodeType NodeType) error { - return GinkgoError{ - Heading: "Multiple Functions", - Message: formatter.F(`[%s] node must be passed a single function - but more than one was passed in.`, nodeType), - CodeLocation: cl, - DocLink: "node-decorators-overview", - } -} - -func (g ginkgoErrors) MissingBodyFunction(cl CodeLocation, nodeType NodeType) error { - return GinkgoError{ - Heading: "Missing Functions", - Message: formatter.F(`[%s] node must be passed a single function - but none was passed in.`, nodeType), - CodeLocation: cl, - DocLink: "node-decorators-overview", - } -} - -func (g ginkgoErrors) InvalidTimeoutOrGracePeriodForNonContextNode(cl CodeLocation, nodeType NodeType) error { - return GinkgoError{ - Heading: "Invalid NodeTimeout SpecTimeout, or GracePeriod", - Message: formatter.F(`[%s] was passed NodeTimeout, SpecTimeout, or GracePeriod but does not have a callback that accepts a {{bold}}SpecContext{{/}} or {{bold}}context.Context{{/}}. You must accept a context to enable timeouts and grace periods`, nodeType), - CodeLocation: cl, - DocLink: "spec-timeouts-and-interruptible-nodes", - } -} - -func (g ginkgoErrors) InvalidTimeoutOrGracePeriodForNonContextCleanupNode(cl CodeLocation) error { - return GinkgoError{ - Heading: "Invalid NodeTimeout SpecTimeout, or GracePeriod", - Message: formatter.F(`[DeferCleanup] was passed NodeTimeout or GracePeriod but does not have a callback that accepts a {{bold}}SpecContext{{/}} or {{bold}}context.Context{{/}}. You must accept a context to enable timeouts and grace periods`), - CodeLocation: cl, - DocLink: "spec-timeouts-and-interruptible-nodes", - } -} - -/* Ordered Container errors */ -func (g ginkgoErrors) InvalidSerialNodeInNonSerialOrderedContainer(cl CodeLocation, nodeType NodeType) error { - return GinkgoError{ - Heading: "Invalid Serial Node in Non-Serial Ordered Container", - Message: formatter.F(`[%s] node was decorated with Serial but occurs in an Ordered container that is not marked Serial. Move the Serial decorator to the outer-most Ordered container to mark all ordered specs within the container as serial.`, nodeType), - CodeLocation: cl, - DocLink: "node-decorators-overview", - } -} - -func (g ginkgoErrors) SetupNodeNotInOrderedContainer(cl CodeLocation, nodeType NodeType) error { - return GinkgoError{ - Heading: "Setup Node not in Ordered Container", - Message: fmt.Sprintf("[%s] setup nodes must appear inside an Ordered container. They cannot be nested within other containers, even containers in an ordered container.", nodeType), - CodeLocation: cl, - DocLink: "ordered-containers", - } -} - -func (g ginkgoErrors) InvalidContinueOnFailureDecoration(cl CodeLocation) error { - return GinkgoError{ - Heading: "ContinueOnFailure not decorating an outermost Ordered Container", - Message: "ContinueOnFailure can only decorate an Ordered container, and this Ordered container must be the outermost Ordered container.", - CodeLocation: cl, - DocLink: "ordered-containers", - } -} - -/* DeferCleanup errors */ -func (g ginkgoErrors) DeferCleanupInvalidFunction(cl CodeLocation) error { - return GinkgoError{ - Heading: "DeferCleanup requires a valid function", - Message: "You must pass DeferCleanup a function to invoke. This function must return zero or one values - if it does return, it must return an error. The function can take arbitrarily many arguments and you should provide these to DeferCleanup to pass along to the function.", - CodeLocation: cl, - DocLink: "cleaning-up-our-cleanup-code-defercleanup", - } -} - -func (g ginkgoErrors) PushingCleanupNodeDuringTreeConstruction(cl CodeLocation) error { - return GinkgoError{ - Heading: "DeferCleanup must be called inside a setup or subject node", - Message: "You must call DeferCleanup inside a setup node (e.g. BeforeEach, BeforeSuite, AfterAll...) or a subject node (i.e. It). You can't call DeferCleanup at the top-level or in a container node - use the After* family of setup nodes instead.", - CodeLocation: cl, - DocLink: "cleaning-up-our-cleanup-code-defercleanup", - } -} - -func (g ginkgoErrors) PushingCleanupInReportingNode(cl CodeLocation, nodeType NodeType) error { - return GinkgoError{ - Heading: fmt.Sprintf("DeferCleanup cannot be called in %s", nodeType), - Message: "Please inline your cleanup code - Ginkgo won't run cleanup code after a Reporting node.", - CodeLocation: cl, - DocLink: "cleaning-up-our-cleanup-code-defercleanup", - } -} - -func (g ginkgoErrors) PushingCleanupInCleanupNode(cl CodeLocation) error { - return GinkgoError{ - Heading: "DeferCleanup cannot be called in a DeferCleanup callback", - Message: "Please inline your cleanup code - Ginkgo doesn't let you call DeferCleanup from within DeferCleanup", - CodeLocation: cl, - DocLink: "cleaning-up-our-cleanup-code-defercleanup", - } -} - -/* ReportEntry errors */ -func (g ginkgoErrors) TooManyReportEntryValues(cl CodeLocation, arg interface{}) error { - return GinkgoError{ - Heading: "Too Many ReportEntry Values", - Message: formatter.F(`{{bold}}AddGinkgoReport{{/}} can only be given one value. Got unexpected value: %#v`, arg), - CodeLocation: cl, - DocLink: "attaching-data-to-reports", - } -} - -func (g ginkgoErrors) AddReportEntryNotDuringRunPhase(cl CodeLocation) error { - return GinkgoError{ - Heading: "Ginkgo detected an issue with your spec structure", - Message: formatter.F(`It looks like you are calling {{bold}}AddGinkgoReport{{/}} outside of a running spec. Make sure you call {{bold}}AddGinkgoReport{{/}} inside a runnable node such as It or BeforeEach and not inside the body of a container such as Describe or Context.`), - CodeLocation: cl, - DocLink: "attaching-data-to-reports", - } -} - -/* By errors */ -func (g ginkgoErrors) ByNotDuringRunPhase(cl CodeLocation) error { - return GinkgoError{ - Heading: "Ginkgo detected an issue with your spec structure", - Message: formatter.F(`It looks like you are calling {{bold}}By{{/}} outside of a running spec. Make sure you call {{bold}}By{{/}} inside a runnable node such as It or BeforeEach and not inside the body of a container such as Describe or Context.`), - CodeLocation: cl, - DocLink: "documenting-complex-specs-by", - } -} - -/* FileFilter and SkipFilter errors */ -func (g ginkgoErrors) InvalidFileFilter(filter string) error { - return GinkgoError{ - Heading: "Invalid File Filter", - Message: fmt.Sprintf(`The provided file filter: "%s" is invalid. File filters must have the format "file", "file:lines" where "file" is a regular expression that will match against the file path and lines is a comma-separated list of integers (e.g. file:1,5,7) or line-ranges (e.g. file:1-3,5-9) or both (e.g. file:1,5-9)`, filter), - DocLink: "filtering-specs", - } -} - -func (g ginkgoErrors) InvalidFileFilterRegularExpression(filter string, err error) error { - return GinkgoError{ - Heading: "Invalid File Filter Regular Expression", - Message: fmt.Sprintf(`The provided file filter: "%s" included an invalid regular expression. regexp.Compile error: %s`, filter, err), - DocLink: "filtering-specs", - } -} - -/* Label Errors */ -func (g ginkgoErrors) SyntaxErrorParsingLabelFilter(input string, location int, error string) error { - var message string - if location >= 0 { - for i, r := range input { - if i == location { - message += "{{red}}{{bold}}{{underline}}" - } - message += string(r) - if i == location { - message += "{{/}}" - } - } - } else { - message = input - } - message += "\n" + error - return GinkgoError{ - Heading: "Syntax Error Parsing Label Filter", - Message: message, - DocLink: "spec-labels", - } -} - -func (g ginkgoErrors) InvalidLabel(label string, cl CodeLocation) error { - return GinkgoError{ - Heading: "Invalid Label", - Message: fmt.Sprintf("'%s' is an invalid label. Labels cannot contain of the following characters: '&|!,()/'", label), - CodeLocation: cl, - DocLink: "spec-labels", - } -} - -func (g ginkgoErrors) InvalidEmptyLabel(cl CodeLocation) error { - return GinkgoError{ - Heading: "Invalid Empty Label", - Message: "Labels cannot be empty", - CodeLocation: cl, - DocLink: "spec-labels", - } -} - -/* Table errors */ -func (g ginkgoErrors) MultipleEntryBodyFunctionsForTable(cl CodeLocation) error { - return GinkgoError{ - Heading: "DescribeTable passed multiple functions", - Message: "It looks like you are passing multiple functions into DescribeTable. Only one function can be passed in. This function will be called for each Entry in the table.", - CodeLocation: cl, - DocLink: "table-specs", - } -} - -func (g ginkgoErrors) InvalidEntryDescription(cl CodeLocation) error { - return GinkgoError{ - Heading: "Invalid Entry description", - Message: "Entry description functions must be a string, a function that accepts the entry parameters and returns a string, or nil.", - CodeLocation: cl, - DocLink: "table-specs", - } -} - -func (g ginkgoErrors) MissingParametersForTableFunction(cl CodeLocation) error { - return GinkgoError{ - Heading: "No parameters have been passed to the Table Function", - Message: "The Table Function expected at least 1 parameter", - CodeLocation: cl, - DocLink: "table-specs", - } -} - -func (g ginkgoErrors) IncorrectParameterTypeForTable(i int, name string, cl CodeLocation) error { - return GinkgoError{ - Heading: "DescribeTable passed incorrect parameter type", - Message: fmt.Sprintf("Parameter #%d passed to DescribeTable is of incorrect type <%s>", i, name), - CodeLocation: cl, - DocLink: "table-specs", - } -} - -func (g ginkgoErrors) TooFewParametersToTableFunction(expected, actual int, kind string, cl CodeLocation) error { - return GinkgoError{ - Heading: fmt.Sprintf("Too few parameters passed in to %s", kind), - Message: fmt.Sprintf("The %s expected %d parameters but you passed in %d", kind, expected, actual), - CodeLocation: cl, - DocLink: "table-specs", - } -} - -func (g ginkgoErrors) TooManyParametersToTableFunction(expected, actual int, kind string, cl CodeLocation) error { - return GinkgoError{ - Heading: fmt.Sprintf("Too many parameters passed in to %s", kind), - Message: fmt.Sprintf("The %s expected %d parameters but you passed in %d", kind, expected, actual), - CodeLocation: cl, - DocLink: "table-specs", - } -} - -func (g ginkgoErrors) IncorrectParameterTypeToTableFunction(i int, expected, actual reflect.Type, kind string, cl CodeLocation) error { - return GinkgoError{ - Heading: fmt.Sprintf("Incorrect parameters type passed to %s", kind), - Message: fmt.Sprintf("The %s expected parameter #%d to be of type <%s> but you passed in <%s>", kind, i, expected, actual), - CodeLocation: cl, - DocLink: "table-specs", - } -} - -func (g ginkgoErrors) IncorrectVariadicParameterTypeToTableFunction(expected, actual reflect.Type, kind string, cl CodeLocation) error { - return GinkgoError{ - Heading: fmt.Sprintf("Incorrect parameters type passed to %s", kind), - Message: fmt.Sprintf("The %s expected its variadic parameters to be of type <%s> but you passed in <%s>", kind, expected, actual), - CodeLocation: cl, - DocLink: "table-specs", - } -} - -func (g ginkgoErrors) ContextsCannotBeUsedInSubtreeTables(cl CodeLocation) error { - return GinkgoError{ - Heading: "Contexts cannot be used in subtree tables", - Message: "You''ve defined a subtree body function that accepts a context but did not provide one in the table entry. Ginkgo SpecContexts can only be passed in to subject and setup nodes - so if you are trying to implement a spec timeout you should request a context in the It function within your subtree body function, not in the subtree body function itself.", - CodeLocation: cl, - DocLink: "table-specs", - } -} - -/* Parallel Synchronization errors */ - -func (g ginkgoErrors) AggregatedReportUnavailableDueToNodeDisappearing() error { - return GinkgoError{ - Heading: "Test Report unavailable because a Ginkgo parallel process disappeared", - Message: "The aggregated report could not be fetched for a ReportAfterSuite node. A Ginkgo parallel process disappeared before it could finish reporting.", - } -} - -func (g ginkgoErrors) SynchronizedBeforeSuiteFailedOnProc1() error { - return GinkgoError{ - Heading: "SynchronizedBeforeSuite failed on Ginkgo parallel process #1", - Message: "The first SynchronizedBeforeSuite function running on Ginkgo parallel process #1 failed. This suite will now abort.", - } -} - -func (g ginkgoErrors) SynchronizedBeforeSuiteDisappearedOnProc1() error { - return GinkgoError{ - Heading: "Process #1 disappeared before SynchronizedBeforeSuite could report back", - Message: "Ginkgo parallel process #1 disappeared before the first SynchronizedBeforeSuite function completed. This suite will now abort.", - } -} - -/* Configuration errors */ - -func (g ginkgoErrors) UnknownTypePassedToRunSpecs(value interface{}) error { - return GinkgoError{ - Heading: "Unknown Type passed to RunSpecs", - Message: fmt.Sprintf("RunSpecs() accepts labels, and configuration of type types.SuiteConfig and/or types.ReporterConfig.\n You passed in: %v", value), - } -} - -var sharedParallelErrorMessage = "It looks like you are trying to run specs in parallel with go test.\nThis is unsupported and you should use the ginkgo CLI instead." - -func (g ginkgoErrors) InvalidParallelTotalConfiguration() error { - return GinkgoError{ - Heading: "-ginkgo.parallel.total must be >= 1", - Message: sharedParallelErrorMessage, - DocLink: "spec-parallelization", - } -} - -func (g ginkgoErrors) InvalidParallelProcessConfiguration() error { - return GinkgoError{ - Heading: "-ginkgo.parallel.process is one-indexed and must be <= ginkgo.parallel.total", - Message: sharedParallelErrorMessage, - DocLink: "spec-parallelization", - } -} - -func (g ginkgoErrors) MissingParallelHostConfiguration() error { - return GinkgoError{ - Heading: "-ginkgo.parallel.host is missing", - Message: sharedParallelErrorMessage, - DocLink: "spec-parallelization", - } -} - -func (g ginkgoErrors) UnreachableParallelHost(host string) error { - return GinkgoError{ - Heading: "Could not reach ginkgo.parallel.host:" + host, - Message: sharedParallelErrorMessage, - DocLink: "spec-parallelization", - } -} - -func (g ginkgoErrors) DryRunInParallelConfiguration() error { - return GinkgoError{ - Heading: "Ginkgo only performs -dryRun in serial mode.", - Message: "Please try running ginkgo -dryRun again, but without -p or -procs to ensure the suite is running in series.", - } -} - -func (g ginkgoErrors) GracePeriodCannotBeZero() error { - return GinkgoError{ - Heading: "Ginkgo requires a positive --grace-period.", - Message: "Please set --grace-period to a positive duration. The default is 30s.", - } -} - -func (g ginkgoErrors) ConflictingVerbosityConfiguration() error { - return GinkgoError{ - Heading: "Conflicting reporter verbosity settings.", - Message: "You can't set more than one of -v, -vv and --succinct. Please pick one!", - } -} - -func (g ginkgoErrors) InvalidOutputInterceptorModeConfiguration(value string) error { - return GinkgoError{ - Heading: fmt.Sprintf("Invalid value '%s' for --output-interceptor-mode.", value), - Message: "You must choose one of 'dup', 'swap', or 'none'.", - } -} - -func (g ginkgoErrors) InvalidGoFlagCount() error { - return GinkgoError{ - Heading: "Use of go test -count", - Message: "Ginkgo does not support using go test -count to rerun suites. Only -count=1 is allowed. To repeat suite runs, please use the ginkgo cli and `ginkgo -until-it-fails` or `ginkgo -repeat=N`.", - } -} - -func (g ginkgoErrors) InvalidGoFlagParallel() error { - return GinkgoError{ - Heading: "Use of go test -parallel", - Message: "Go test's implementation of parallelization does not actually parallelize Ginkgo specs. Please use the ginkgo cli and `ginkgo -p` or `ginkgo -procs=N` instead.", - } -} - -func (g ginkgoErrors) BothRepeatAndUntilItFails() error { - return GinkgoError{ - Heading: "--repeat and --until-it-fails are both set", - Message: "--until-it-fails directs Ginkgo to rerun specs indefinitely until they fail. --repeat directs Ginkgo to rerun specs a set number of times. You can't set both... which would you like?", - } -} - -/* Stack-Trace parsing errors */ - -func (g ginkgoErrors) FailedToParseStackTrace(message string) error { - return GinkgoError{ - Heading: "Failed to Parse Stack Trace", - Message: message, - } -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/file_filter.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/file_filter.go deleted file mode 100644 index cc21df71e..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/file_filter.go +++ /dev/null @@ -1,106 +0,0 @@ -package types - -import ( - "regexp" - "strconv" - "strings" -) - -func ParseFileFilters(filters []string) (FileFilters, error) { - ffs := FileFilters{} - for _, filter := range filters { - ff := FileFilter{} - if filter == "" { - return nil, GinkgoErrors.InvalidFileFilter(filter) - } - components := strings.Split(filter, ":") - if !(len(components) == 1 || len(components) == 2) { - return nil, GinkgoErrors.InvalidFileFilter(filter) - } - - var err error - ff.Filename, err = regexp.Compile(components[0]) - if err != nil { - return nil, err - } - if len(components) == 2 { - lineFilters := strings.Split(components[1], ",") - for _, lineFilter := range lineFilters { - components := strings.Split(lineFilter, "-") - if len(components) == 1 { - line, err := strconv.Atoi(strings.TrimSpace(components[0])) - if err != nil { - return nil, GinkgoErrors.InvalidFileFilter(filter) - } - ff.LineFilters = append(ff.LineFilters, LineFilter{line, line + 1}) - } else if len(components) == 2 { - line1, err := strconv.Atoi(strings.TrimSpace(components[0])) - if err != nil { - return nil, GinkgoErrors.InvalidFileFilter(filter) - } - line2, err := strconv.Atoi(strings.TrimSpace(components[1])) - if err != nil { - return nil, GinkgoErrors.InvalidFileFilter(filter) - } - ff.LineFilters = append(ff.LineFilters, LineFilter{line1, line2}) - } else { - return nil, GinkgoErrors.InvalidFileFilter(filter) - } - } - } - ffs = append(ffs, ff) - } - return ffs, nil -} - -type FileFilter struct { - Filename *regexp.Regexp - LineFilters LineFilters -} - -func (f FileFilter) Matches(locations []CodeLocation) bool { - for _, location := range locations { - if f.Filename.MatchString(location.FileName) && - f.LineFilters.Matches(location.LineNumber) { - return true - } - - } - return false -} - -type FileFilters []FileFilter - -func (ffs FileFilters) Matches(locations []CodeLocation) bool { - for _, ff := range ffs { - if ff.Matches(locations) { - return true - } - } - - return false -} - -type LineFilter struct { - Min int - Max int -} - -func (lf LineFilter) Matches(line int) bool { - return lf.Min <= line && line < lf.Max -} - -type LineFilters []LineFilter - -func (lfs LineFilters) Matches(line int) bool { - if len(lfs) == 0 { - return true - } - - for _, lf := range lfs { - if lf.Matches(line) { - return true - } - } - return false -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/flags.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/flags.go deleted file mode 100644 index 9186ae873..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/flags.go +++ /dev/null @@ -1,489 +0,0 @@ -package types - -import ( - "flag" - "fmt" - "io" - "reflect" - "strings" - "time" - - "github.com/onsi/ginkgo/v2/formatter" -) - -type GinkgoFlag struct { - Name string - KeyPath string - SectionKey string - - Usage string - UsageArgument string - UsageDefaultValue string - - DeprecatedName string - DeprecatedDocLink string - DeprecatedVersion string - - ExportAs string -} - -type GinkgoFlags []GinkgoFlag - -func (f GinkgoFlags) CopyAppend(flags ...GinkgoFlag) GinkgoFlags { - out := GinkgoFlags{} - out = append(out, f...) - out = append(out, flags...) - return out -} - -func (f GinkgoFlags) WithPrefix(prefix string) GinkgoFlags { - if prefix == "" { - return f - } - out := GinkgoFlags{} - for _, flag := range f { - if flag.Name != "" { - flag.Name = prefix + "." + flag.Name - } - if flag.DeprecatedName != "" { - flag.DeprecatedName = prefix + "." + flag.DeprecatedName - } - if flag.ExportAs != "" { - flag.ExportAs = prefix + "." + flag.ExportAs - } - out = append(out, flag) - } - return out -} - -func (f GinkgoFlags) SubsetWithNames(names ...string) GinkgoFlags { - out := GinkgoFlags{} - for _, flag := range f { - for _, name := range names { - if flag.Name == name { - out = append(out, flag) - break - } - } - } - return out -} - -type GinkgoFlagSection struct { - Key string - Style string - Succinct bool - Heading string - Description string -} - -type GinkgoFlagSections []GinkgoFlagSection - -func (gfs GinkgoFlagSections) Lookup(key string) (GinkgoFlagSection, bool) { - for _, section := range gfs { - if section.Key == key { - return section, true - } - } - - return GinkgoFlagSection{}, false -} - -type GinkgoFlagSet struct { - flags GinkgoFlags - bindings interface{} - - sections GinkgoFlagSections - extraGoFlagsSection GinkgoFlagSection - - flagSet *flag.FlagSet -} - -// Call NewGinkgoFlagSet to create GinkgoFlagSet that creates and binds to it's own *flag.FlagSet -func NewGinkgoFlagSet(flags GinkgoFlags, bindings interface{}, sections GinkgoFlagSections) (GinkgoFlagSet, error) { - return bindFlagSet(GinkgoFlagSet{ - flags: flags, - bindings: bindings, - sections: sections, - }, nil) -} - -// Call NewGinkgoFlagSet to create GinkgoFlagSet that extends an existing *flag.FlagSet -func NewAttachedGinkgoFlagSet(flagSet *flag.FlagSet, flags GinkgoFlags, bindings interface{}, sections GinkgoFlagSections, extraGoFlagsSection GinkgoFlagSection) (GinkgoFlagSet, error) { - return bindFlagSet(GinkgoFlagSet{ - flags: flags, - bindings: bindings, - sections: sections, - extraGoFlagsSection: extraGoFlagsSection, - }, flagSet) -} - -func bindFlagSet(f GinkgoFlagSet, flagSet *flag.FlagSet) (GinkgoFlagSet, error) { - if flagSet == nil { - f.flagSet = flag.NewFlagSet("", flag.ContinueOnError) - //suppress all output as Ginkgo is responsible for formatting usage - f.flagSet.SetOutput(io.Discard) - } else { - f.flagSet = flagSet - //we're piggybacking on an existing flagset (typically go test) so we have limited control - //on user feedback - f.flagSet.Usage = f.substituteUsage - } - - for _, flag := range f.flags { - name := flag.Name - - deprecatedUsage := "[DEPRECATED]" - deprecatedName := flag.DeprecatedName - if name != "" { - deprecatedUsage = fmt.Sprintf("[DEPRECATED] use --%s instead", name) - } else if flag.Usage != "" { - deprecatedUsage += " " + flag.Usage - } - - value, ok := valueAtKeyPath(f.bindings, flag.KeyPath) - if !ok { - return GinkgoFlagSet{}, fmt.Errorf("could not load KeyPath: %s", flag.KeyPath) - } - - iface, addr := value.Interface(), value.Addr().Interface() - - switch value.Type() { - case reflect.TypeOf(string("")): - if name != "" { - f.flagSet.StringVar(addr.(*string), name, iface.(string), flag.Usage) - } - if deprecatedName != "" { - f.flagSet.StringVar(addr.(*string), deprecatedName, iface.(string), deprecatedUsage) - } - case reflect.TypeOf(int64(0)): - if name != "" { - f.flagSet.Int64Var(addr.(*int64), name, iface.(int64), flag.Usage) - } - if deprecatedName != "" { - f.flagSet.Int64Var(addr.(*int64), deprecatedName, iface.(int64), deprecatedUsage) - } - case reflect.TypeOf(float64(0)): - if name != "" { - f.flagSet.Float64Var(addr.(*float64), name, iface.(float64), flag.Usage) - } - if deprecatedName != "" { - f.flagSet.Float64Var(addr.(*float64), deprecatedName, iface.(float64), deprecatedUsage) - } - case reflect.TypeOf(int(0)): - if name != "" { - f.flagSet.IntVar(addr.(*int), name, iface.(int), flag.Usage) - } - if deprecatedName != "" { - f.flagSet.IntVar(addr.(*int), deprecatedName, iface.(int), deprecatedUsage) - } - case reflect.TypeOf(bool(true)): - if name != "" { - f.flagSet.BoolVar(addr.(*bool), name, iface.(bool), flag.Usage) - } - if deprecatedName != "" { - f.flagSet.BoolVar(addr.(*bool), deprecatedName, iface.(bool), deprecatedUsage) - } - case reflect.TypeOf(time.Duration(0)): - if name != "" { - f.flagSet.DurationVar(addr.(*time.Duration), name, iface.(time.Duration), flag.Usage) - } - if deprecatedName != "" { - f.flagSet.DurationVar(addr.(*time.Duration), deprecatedName, iface.(time.Duration), deprecatedUsage) - } - - case reflect.TypeOf([]string{}): - if name != "" { - f.flagSet.Var(stringSliceVar{value}, name, flag.Usage) - } - if deprecatedName != "" { - f.flagSet.Var(stringSliceVar{value}, deprecatedName, deprecatedUsage) - } - default: - return GinkgoFlagSet{}, fmt.Errorf("unsupported type %T", iface) - } - } - - return f, nil -} - -func (f GinkgoFlagSet) IsZero() bool { - return f.flagSet == nil -} - -func (f GinkgoFlagSet) WasSet(name string) bool { - found := false - f.flagSet.Visit(func(f *flag.Flag) { - if f.Name == name { - found = true - } - }) - - return found -} - -func (f GinkgoFlagSet) Lookup(name string) *flag.Flag { - return f.flagSet.Lookup(name) -} - -func (f GinkgoFlagSet) Parse(args []string) ([]string, error) { - if f.IsZero() { - return args, nil - } - err := f.flagSet.Parse(args) - if err != nil { - return []string{}, err - } - return f.flagSet.Args(), nil -} - -func (f GinkgoFlagSet) ValidateDeprecations(deprecationTracker *DeprecationTracker) { - if f.IsZero() { - return - } - f.flagSet.Visit(func(flag *flag.Flag) { - for _, ginkgoFlag := range f.flags { - if ginkgoFlag.DeprecatedName != "" && strings.HasSuffix(flag.Name, ginkgoFlag.DeprecatedName) { - message := fmt.Sprintf("--%s is deprecated", ginkgoFlag.DeprecatedName) - if ginkgoFlag.Name != "" { - message = fmt.Sprintf("--%s is deprecated, use --%s instead", ginkgoFlag.DeprecatedName, ginkgoFlag.Name) - } else if ginkgoFlag.Usage != "" { - message += " " + ginkgoFlag.Usage - } - - deprecationTracker.TrackDeprecation(Deprecation{ - Message: message, - DocLink: ginkgoFlag.DeprecatedDocLink, - Version: ginkgoFlag.DeprecatedVersion, - }) - } - } - }) -} - -func (f GinkgoFlagSet) Usage() string { - if f.IsZero() { - return "" - } - groupedFlags := map[GinkgoFlagSection]GinkgoFlags{} - ungroupedFlags := GinkgoFlags{} - managedFlags := map[string]bool{} - extraGoFlags := []*flag.Flag{} - - for _, flag := range f.flags { - managedFlags[flag.Name] = true - managedFlags[flag.DeprecatedName] = true - - if flag.Name == "" { - continue - } - - section, ok := f.sections.Lookup(flag.SectionKey) - if ok { - groupedFlags[section] = append(groupedFlags[section], flag) - } else { - ungroupedFlags = append(ungroupedFlags, flag) - } - } - - f.flagSet.VisitAll(func(flag *flag.Flag) { - if !managedFlags[flag.Name] { - extraGoFlags = append(extraGoFlags, flag) - } - }) - - out := "" - for _, section := range f.sections { - flags := groupedFlags[section] - if len(flags) == 0 { - continue - } - out += f.usageForSection(section) - if section.Succinct { - succinctFlags := []string{} - for _, flag := range flags { - if flag.Name != "" { - succinctFlags = append(succinctFlags, fmt.Sprintf("--%s", flag.Name)) - } - } - out += formatter.Fiw(1, formatter.COLS, section.Style+strings.Join(succinctFlags, ", ")+"{{/}}\n") - } else { - for _, flag := range flags { - out += f.usageForFlag(flag, section.Style) - } - } - out += "\n" - } - if len(ungroupedFlags) > 0 { - for _, flag := range ungroupedFlags { - out += f.usageForFlag(flag, "") - } - out += "\n" - } - if len(extraGoFlags) > 0 { - out += f.usageForSection(f.extraGoFlagsSection) - for _, goFlag := range extraGoFlags { - out += f.usageForGoFlag(goFlag) - } - } - - return out -} - -func (f GinkgoFlagSet) substituteUsage() { - fmt.Fprintln(f.flagSet.Output(), f.Usage()) -} - -func valueAtKeyPath(root interface{}, keyPath string) (reflect.Value, bool) { - if len(keyPath) == 0 { - return reflect.Value{}, false - } - - val := reflect.ValueOf(root) - components := strings.Split(keyPath, ".") - for _, component := range components { - val = reflect.Indirect(val) - switch val.Kind() { - case reflect.Map: - val = val.MapIndex(reflect.ValueOf(component)) - if val.Kind() == reflect.Interface { - val = reflect.ValueOf(val.Interface()) - } - case reflect.Struct: - val = val.FieldByName(component) - default: - return reflect.Value{}, false - } - if (val == reflect.Value{}) { - return reflect.Value{}, false - } - } - - return val, true -} - -func (f GinkgoFlagSet) usageForSection(section GinkgoFlagSection) string { - out := formatter.F(section.Style + "{{bold}}{{underline}}" + section.Heading + "{{/}}\n") - if section.Description != "" { - out += formatter.Fiw(0, formatter.COLS, section.Description+"\n") - } - return out -} - -func (f GinkgoFlagSet) usageForFlag(flag GinkgoFlag, style string) string { - argument := flag.UsageArgument - defValue := flag.UsageDefaultValue - if argument == "" { - value, _ := valueAtKeyPath(f.bindings, flag.KeyPath) - switch value.Type() { - case reflect.TypeOf(string("")): - argument = "string" - case reflect.TypeOf(int64(0)), reflect.TypeOf(int(0)): - argument = "int" - case reflect.TypeOf(time.Duration(0)): - argument = "duration" - case reflect.TypeOf(float64(0)): - argument = "float" - case reflect.TypeOf([]string{}): - argument = "string" - } - } - if argument != "" { - argument = "[" + argument + "] " - } - if defValue != "" { - defValue = fmt.Sprintf("(default: %s)", defValue) - } - hyphens := "--" - if len(flag.Name) == 1 { - hyphens = "-" - } - - out := formatter.Fi(1, style+"%s%s{{/}} %s{{gray}}%s{{/}}\n", hyphens, flag.Name, argument, defValue) - out += formatter.Fiw(2, formatter.COLS, "{{light-gray}}%s{{/}}\n", flag.Usage) - return out -} - -func (f GinkgoFlagSet) usageForGoFlag(goFlag *flag.Flag) string { - //Taken directly from the flag package - out := fmt.Sprintf(" -%s", goFlag.Name) - name, usage := flag.UnquoteUsage(goFlag) - if len(name) > 0 { - out += " " + name - } - if len(out) <= 4 { - out += "\t" - } else { - out += "\n \t" - } - out += strings.ReplaceAll(usage, "\n", "\n \t") - out += "\n" - return out -} - -type stringSliceVar struct { - slice reflect.Value -} - -func (ssv stringSliceVar) String() string { return "" } -func (ssv stringSliceVar) Set(s string) error { - ssv.slice.Set(reflect.AppendSlice(ssv.slice, reflect.ValueOf([]string{s}))) - return nil -} - -//given a set of GinkgoFlags and bindings, generate flag arguments suitable to be passed to an application with that set of flags configured. -func GenerateFlagArgs(flags GinkgoFlags, bindings interface{}) ([]string, error) { - result := []string{} - for _, flag := range flags { - name := flag.ExportAs - if name == "" { - name = flag.Name - } - if name == "" { - continue - } - - value, ok := valueAtKeyPath(bindings, flag.KeyPath) - if !ok { - return []string{}, fmt.Errorf("could not load KeyPath: %s", flag.KeyPath) - } - - iface := value.Interface() - switch value.Type() { - case reflect.TypeOf(string("")): - if iface.(string) != "" { - result = append(result, fmt.Sprintf("--%s=%s", name, iface)) - } - case reflect.TypeOf(int64(0)): - if iface.(int64) != 0 { - result = append(result, fmt.Sprintf("--%s=%d", name, iface)) - } - case reflect.TypeOf(float64(0)): - if iface.(float64) != 0 { - result = append(result, fmt.Sprintf("--%s=%f", name, iface)) - } - case reflect.TypeOf(int(0)): - if iface.(int) != 0 { - result = append(result, fmt.Sprintf("--%s=%d", name, iface)) - } - case reflect.TypeOf(bool(true)): - if iface.(bool) { - result = append(result, fmt.Sprintf("--%s", name)) - } - case reflect.TypeOf(time.Duration(0)): - if iface.(time.Duration) != time.Duration(0) { - result = append(result, fmt.Sprintf("--%s=%s", name, iface)) - } - - case reflect.TypeOf([]string{}): - strings := iface.([]string) - for _, s := range strings { - result = append(result, fmt.Sprintf("--%s=%s", name, s)) - } - default: - return []string{}, fmt.Errorf("unsupported type %T", iface) - } - } - - return result, nil -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/label_filter.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/label_filter.go deleted file mode 100644 index b0d3b651e..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/label_filter.go +++ /dev/null @@ -1,358 +0,0 @@ -package types - -import ( - "fmt" - "regexp" - "strings" -) - -var DEBUG_LABEL_FILTER_PARSING = false - -type LabelFilter func([]string) bool - -func matchLabelAction(label string) LabelFilter { - expected := strings.ToLower(label) - return func(labels []string) bool { - for i := range labels { - if strings.ToLower(labels[i]) == expected { - return true - } - } - return false - } -} - -func matchLabelRegexAction(regex *regexp.Regexp) LabelFilter { - return func(labels []string) bool { - for i := range labels { - if regex.MatchString(labels[i]) { - return true - } - } - return false - } -} - -func notAction(filter LabelFilter) LabelFilter { - return func(labels []string) bool { return !filter(labels) } -} - -func andAction(a, b LabelFilter) LabelFilter { - return func(labels []string) bool { return a(labels) && b(labels) } -} - -func orAction(a, b LabelFilter) LabelFilter { - return func(labels []string) bool { return a(labels) || b(labels) } -} - -type lfToken uint - -const ( - lfTokenInvalid lfToken = iota - - lfTokenRoot - lfTokenOpenGroup - lfTokenCloseGroup - lfTokenNot - lfTokenAnd - lfTokenOr - lfTokenRegexp - lfTokenLabel - lfTokenEOF -) - -func (l lfToken) Precedence() int { - switch l { - case lfTokenRoot, lfTokenOpenGroup: - return 0 - case lfTokenOr: - return 1 - case lfTokenAnd: - return 2 - case lfTokenNot: - return 3 - } - return -1 -} - -func (l lfToken) String() string { - switch l { - case lfTokenRoot: - return "ROOT" - case lfTokenOpenGroup: - return "(" - case lfTokenCloseGroup: - return ")" - case lfTokenNot: - return "!" - case lfTokenAnd: - return "&&" - case lfTokenOr: - return "||" - case lfTokenRegexp: - return "/regexp/" - case lfTokenLabel: - return "label" - case lfTokenEOF: - return "EOF" - } - return "INVALID" -} - -type treeNode struct { - token lfToken - location int - value string - - parent *treeNode - leftNode *treeNode - rightNode *treeNode -} - -func (tn *treeNode) setRightNode(node *treeNode) { - tn.rightNode = node - node.parent = tn -} - -func (tn *treeNode) setLeftNode(node *treeNode) { - tn.leftNode = node - node.parent = tn -} - -func (tn *treeNode) firstAncestorWithPrecedenceLEQ(precedence int) *treeNode { - if tn.token.Precedence() <= precedence { - return tn - } - return tn.parent.firstAncestorWithPrecedenceLEQ(precedence) -} - -func (tn *treeNode) firstUnmatchedOpenNode() *treeNode { - if tn.token == lfTokenOpenGroup { - return tn - } - if tn.parent == nil { - return nil - } - return tn.parent.firstUnmatchedOpenNode() -} - -func (tn *treeNode) constructLabelFilter(input string) (LabelFilter, error) { - switch tn.token { - case lfTokenOpenGroup: - return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, tn.location, "Mismatched '(' - could not find matching ')'.") - case lfTokenLabel: - return matchLabelAction(tn.value), nil - case lfTokenRegexp: - re, err := regexp.Compile(tn.value) - if err != nil { - return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, tn.location, fmt.Sprintf("RegExp compilation error: %s", err)) - } - return matchLabelRegexAction(re), nil - } - - if tn.rightNode == nil { - return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, -1, "Unexpected EOF.") - } - rightLF, err := tn.rightNode.constructLabelFilter(input) - if err != nil { - return nil, err - } - - switch tn.token { - case lfTokenRoot, lfTokenCloseGroup: - return rightLF, nil - case lfTokenNot: - return notAction(rightLF), nil - } - - if tn.leftNode == nil { - return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, tn.location, fmt.Sprintf("Malformed tree - '%s' is missing left operand.", tn.token)) - } - leftLF, err := tn.leftNode.constructLabelFilter(input) - if err != nil { - return nil, err - } - - switch tn.token { - case lfTokenAnd: - return andAction(leftLF, rightLF), nil - case lfTokenOr: - return orAction(leftLF, rightLF), nil - } - - return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, tn.location, fmt.Sprintf("Invalid token '%s'.", tn.token)) -} - -func (tn *treeNode) tokenString() string { - out := fmt.Sprintf("<%s", tn.token) - if tn.value != "" { - out += " | " + tn.value - } - out += ">" - return out -} - -func (tn *treeNode) toString(indent int) string { - out := tn.tokenString() + "\n" - if tn.leftNode != nil { - out += fmt.Sprintf("%s |_(L)_%s", strings.Repeat(" ", indent), tn.leftNode.toString(indent+1)) - } - if tn.rightNode != nil { - out += fmt.Sprintf("%s |_(R)_%s", strings.Repeat(" ", indent), tn.rightNode.toString(indent+1)) - } - return out -} - -func tokenize(input string) func() (*treeNode, error) { - runes, i := []rune(input), 0 - - peekIs := func(r rune) bool { - if i+1 < len(runes) { - return runes[i+1] == r - } - return false - } - - consumeUntil := func(cutset string) (string, int) { - j := i - for ; j < len(runes); j++ { - if strings.IndexRune(cutset, runes[j]) >= 0 { - break - } - } - return string(runes[i:j]), j - i - } - - return func() (*treeNode, error) { - for i < len(runes) && runes[i] == ' ' { - i += 1 - } - - if i >= len(runes) { - return &treeNode{token: lfTokenEOF}, nil - } - - node := &treeNode{location: i} - switch runes[i] { - case '&': - if !peekIs('&') { - return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, i, "Invalid token '&'. Did you mean '&&'?") - } - i += 2 - node.token = lfTokenAnd - case '|': - if !peekIs('|') { - return &treeNode{}, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, i, "Invalid token '|'. Did you mean '||'?") - } - i += 2 - node.token = lfTokenOr - case '!': - i += 1 - node.token = lfTokenNot - case ',': - i += 1 - node.token = lfTokenOr - case '(': - i += 1 - node.token = lfTokenOpenGroup - case ')': - i += 1 - node.token = lfTokenCloseGroup - case '/': - i += 1 - value, n := consumeUntil("/") - i += n + 1 - node.token, node.value = lfTokenRegexp, value - default: - value, n := consumeUntil("&|!,()/") - i += n - node.token, node.value = lfTokenLabel, strings.TrimSpace(value) - } - return node, nil - } -} - -func MustParseLabelFilter(input string) LabelFilter { - filter, err := ParseLabelFilter(input) - if err != nil { - panic(err) - } - return filter -} - -func ParseLabelFilter(input string) (LabelFilter, error) { - if DEBUG_LABEL_FILTER_PARSING { - fmt.Println("\n==============") - fmt.Println("Input: ", input) - fmt.Print("Tokens: ") - } - if input == "" { - return func(_ []string) bool { return true }, nil - } - nextToken := tokenize(input) - - root := &treeNode{token: lfTokenRoot} - current := root -LOOP: - for { - node, err := nextToken() - if err != nil { - return nil, err - } - - if DEBUG_LABEL_FILTER_PARSING { - fmt.Print(node.tokenString() + " ") - } - - switch node.token { - case lfTokenEOF: - break LOOP - case lfTokenLabel, lfTokenRegexp: - if current.rightNode != nil { - return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, "Found two adjacent labels. You need an operator between them.") - } - current.setRightNode(node) - case lfTokenNot, lfTokenOpenGroup: - if current.rightNode != nil { - return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, fmt.Sprintf("Invalid token '%s'.", node.token)) - } - current.setRightNode(node) - current = node - case lfTokenAnd, lfTokenOr: - if current.rightNode == nil { - return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, fmt.Sprintf("Operator '%s' missing left hand operand.", node.token)) - } - nodeToStealFrom := current.firstAncestorWithPrecedenceLEQ(node.token.Precedence()) - node.setLeftNode(nodeToStealFrom.rightNode) - nodeToStealFrom.setRightNode(node) - current = node - case lfTokenCloseGroup: - firstUnmatchedOpenNode := current.firstUnmatchedOpenNode() - if firstUnmatchedOpenNode == nil { - return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, "Mismatched ')' - could not find matching '('.") - } - if firstUnmatchedOpenNode == current && current.rightNode == nil { - return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, "Found empty '()' group.") - } - firstUnmatchedOpenNode.token = lfTokenCloseGroup //signify the group is now closed - current = firstUnmatchedOpenNode.parent - default: - return nil, GinkgoErrors.SyntaxErrorParsingLabelFilter(input, node.location, fmt.Sprintf("Unknown token '%s'.", node.token)) - } - } - if DEBUG_LABEL_FILTER_PARSING { - fmt.Printf("\n Tree:\n%s", root.toString(0)) - } - return root.constructLabelFilter(input) -} - -func ValidateAndCleanupLabel(label string, cl CodeLocation) (string, error) { - out := strings.TrimSpace(label) - if out == "" { - return "", GinkgoErrors.InvalidEmptyLabel(cl) - } - if strings.ContainsAny(out, "&|!,()/") { - return "", GinkgoErrors.InvalidLabel(label, cl) - } - return out, nil -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/report_entry.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/report_entry.go deleted file mode 100644 index 7b1524b52..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/report_entry.go +++ /dev/null @@ -1,190 +0,0 @@ -package types - -import ( - "encoding/json" - "fmt" - "time" -) - -// ReportEntryValue wraps a report entry's value ensuring it can be encoded and decoded safely into reports -// and across the network connection when running in parallel -type ReportEntryValue struct { - raw interface{} //unexported to prevent gob from freaking out about unregistered structs - AsJSON string - Representation string -} - -func WrapEntryValue(value interface{}) ReportEntryValue { - return ReportEntryValue{ - raw: value, - } -} - -func (rev ReportEntryValue) GetRawValue() interface{} { - return rev.raw -} - -func (rev ReportEntryValue) String() string { - if rev.raw == nil { - return "" - } - if colorableStringer, ok := rev.raw.(ColorableStringer); ok { - return colorableStringer.ColorableString() - } - - if stringer, ok := rev.raw.(fmt.Stringer); ok { - return stringer.String() - } - if rev.Representation != "" { - return rev.Representation - } - return fmt.Sprintf("%+v", rev.raw) -} - -func (rev ReportEntryValue) MarshalJSON() ([]byte, error) { - //All this to capture the representation at encoding-time, not creating time - //This way users can Report on pointers and get their final values at reporting-time - out := struct { - AsJSON string - Representation string - }{ - Representation: rev.String(), - } - asJSON, err := json.Marshal(rev.raw) - if err != nil { - return nil, err - } - out.AsJSON = string(asJSON) - - return json.Marshal(out) -} - -func (rev *ReportEntryValue) UnmarshalJSON(data []byte) error { - in := struct { - AsJSON string - Representation string - }{} - err := json.Unmarshal(data, &in) - if err != nil { - return err - } - rev.AsJSON = in.AsJSON - rev.Representation = in.Representation - return json.Unmarshal([]byte(in.AsJSON), &(rev.raw)) -} - -func (rev ReportEntryValue) GobEncode() ([]byte, error) { - return rev.MarshalJSON() -} - -func (rev *ReportEntryValue) GobDecode(data []byte) error { - return rev.UnmarshalJSON(data) -} - -// ReportEntry captures information attached to `SpecReport` via `AddReportEntry` -type ReportEntry struct { - // Visibility captures the visibility policy for this ReportEntry - Visibility ReportEntryVisibility - // Location captures the location of the AddReportEntry call - Location CodeLocation - - Time time.Time //need this for backwards compatibility - TimelineLocation TimelineLocation - - // Name captures the name of this report - Name string - // Value captures the (optional) object passed into AddReportEntry - this can be - // anything the user wants. The value passed to AddReportEntry is wrapped in a ReportEntryValue to make - // encoding/decoding the value easier. To access the raw value call entry.GetRawValue() - Value ReportEntryValue -} - -// ColorableStringer is an interface that ReportEntry values can satisfy. If they do then ColorableString() is used to generate their representation. -type ColorableStringer interface { - ColorableString() string -} - -// StringRepresentation() returns the string representation of the value associated with the ReportEntry -- -// if value is nil, empty string is returned -// if value is a `ColorableStringer` then `Value.ColorableString()` is returned -// if value is a `fmt.Stringer` then `Value.String()` is returned -// otherwise the value is formatted with "%+v" -func (entry ReportEntry) StringRepresentation() string { - return entry.Value.String() -} - -// GetRawValue returns the Value object that was passed to AddReportEntry -// If called in-process this will be the same object that was passed into AddReportEntry. -// If used from a rehydrated JSON file _or_ in a ReportAfterSuite when running in parallel this will be -// a JSON-decoded {}interface. If you want to reconstitute your original object you can decode the entry.Value.AsJSON -// field yourself. -func (entry ReportEntry) GetRawValue() interface{} { - return entry.Value.GetRawValue() -} - -func (entry ReportEntry) GetTimelineLocation() TimelineLocation { - return entry.TimelineLocation -} - -type ReportEntries []ReportEntry - -func (re ReportEntries) HasVisibility(visibilities ...ReportEntryVisibility) bool { - for _, entry := range re { - if entry.Visibility.Is(visibilities...) { - return true - } - } - return false -} - -func (re ReportEntries) WithVisibility(visibilities ...ReportEntryVisibility) ReportEntries { - out := ReportEntries{} - - for _, entry := range re { - if entry.Visibility.Is(visibilities...) { - out = append(out, entry) - } - } - - return out -} - -// ReportEntryVisibility governs the visibility of ReportEntries in Ginkgo's console reporter -type ReportEntryVisibility uint - -const ( - // Always print out this ReportEntry - ReportEntryVisibilityAlways ReportEntryVisibility = iota - // Only print out this ReportEntry if the spec fails or if the test is run with -v - ReportEntryVisibilityFailureOrVerbose - // Never print out this ReportEntry (note that ReportEntrys are always encoded in machine readable reports (e.g. JSON, JUnit, etc.)) - ReportEntryVisibilityNever -) - -var revEnumSupport = NewEnumSupport(map[uint]string{ - uint(ReportEntryVisibilityAlways): "always", - uint(ReportEntryVisibilityFailureOrVerbose): "failure-or-verbose", - uint(ReportEntryVisibilityNever): "never", -}) - -func (rev ReportEntryVisibility) String() string { - return revEnumSupport.String(uint(rev)) -} -func (rev *ReportEntryVisibility) UnmarshalJSON(b []byte) error { - out, err := revEnumSupport.UnmarshJSON(b) - *rev = ReportEntryVisibility(out) - return err -} -func (rev ReportEntryVisibility) MarshalJSON() ([]byte, error) { - return revEnumSupport.MarshJSON(uint(rev)) -} - -func (v ReportEntryVisibility) Is(visibilities ...ReportEntryVisibility) bool { - for _, visibility := range visibilities { - if v == visibility { - return true - } - } - - return false -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/types.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/types.go deleted file mode 100644 index aae69b04c..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/types.go +++ /dev/null @@ -1,914 +0,0 @@ -package types - -import ( - "encoding/json" - "fmt" - "sort" - "strings" - "time" -) - -const GINKGO_FOCUS_EXIT_CODE = 197 -const GINKGO_TIME_FORMAT = "01/02/06 15:04:05.999" - -// Report captures information about a Ginkgo test run -type Report struct { - //SuitePath captures the absolute path to the test suite - SuitePath string - - //SuiteDescription captures the description string passed to the DSL's RunSpecs() function - SuiteDescription string - - //SuiteLabels captures any labels attached to the suite by the DSL's RunSpecs() function - SuiteLabels []string - - //SuiteSucceeded captures the success or failure status of the test run - //If true, the test run is considered successful. - //If false, the test run is considered unsuccessful - SuiteSucceeded bool - - //SuiteHasProgrammaticFocus captures whether the test suite has a test or set of tests that are programmatically focused - //(i.e an `FIt` or an `FDescribe` - SuiteHasProgrammaticFocus bool - - //SpecialSuiteFailureReasons may contain special failure reasons - //For example, a test suite might be considered "failed" even if none of the individual specs - //have a failure state. For example, if the user has configured --fail-on-pending the test suite - //will have failed if there are pending tests even though all non-pending tests may have passed. In such - //cases, Ginkgo populates SpecialSuiteFailureReasons with a clear message indicating the reason for the failure. - //SpecialSuiteFailureReasons is also populated if the test suite is interrupted by the user. - //Since multiple special failure reasons can occur, this field is a slice. - SpecialSuiteFailureReasons []string - - //PreRunStats contains a set of stats captured before the test run begins. This is primarily used - //by Ginkgo's reporter to tell the user how many specs are in the current suite (PreRunStats.TotalSpecs) - //and how many it intends to run (PreRunStats.SpecsThatWillRun) after applying any relevant focus or skip filters. - PreRunStats PreRunStats - - //StartTime and EndTime capture the start and end time of the test run - StartTime time.Time - EndTime time.Time - - //RunTime captures the duration of the test run - RunTime time.Duration - - //SuiteConfig captures the Ginkgo configuration governing this test run - //SuiteConfig includes information necessary for reproducing an identical test run, - //such as the random seed and any filters applied during the test run - SuiteConfig SuiteConfig - - //SpecReports is a list of all SpecReports generated by this test run - //It is empty when the SuiteReport is provided to ReportBeforeSuite - SpecReports SpecReports -} - -// PreRunStats contains a set of stats captured before the test run begins. This is primarily used -// by Ginkgo's reporter to tell the user how many specs are in the current suite (PreRunStats.TotalSpecs) -// and how many it intends to run (PreRunStats.SpecsThatWillRun) after applying any relevant focus or skip filters. -type PreRunStats struct { - TotalSpecs int - SpecsThatWillRun int -} - -// Add is used by Ginkgo's parallel aggregation mechanisms to combine test run reports form individual parallel processes -// to form a complete final report. -func (report Report) Add(other Report) Report { - report.SuiteSucceeded = report.SuiteSucceeded && other.SuiteSucceeded - - if other.StartTime.Before(report.StartTime) { - report.StartTime = other.StartTime - } - - if other.EndTime.After(report.EndTime) { - report.EndTime = other.EndTime - } - - specialSuiteFailureReasons := []string{} - reasonsLookup := map[string]bool{} - for _, reasons := range [][]string{report.SpecialSuiteFailureReasons, other.SpecialSuiteFailureReasons} { - for _, reason := range reasons { - if !reasonsLookup[reason] { - reasonsLookup[reason] = true - specialSuiteFailureReasons = append(specialSuiteFailureReasons, reason) - } - } - } - report.SpecialSuiteFailureReasons = specialSuiteFailureReasons - report.RunTime = report.EndTime.Sub(report.StartTime) - - reports := make(SpecReports, len(report.SpecReports)+len(other.SpecReports)) - copy(reports, report.SpecReports) - offset := len(report.SpecReports) - for i := range other.SpecReports { - reports[i+offset] = other.SpecReports[i] - } - - report.SpecReports = reports - return report -} - -// SpecReport captures information about a Ginkgo spec. -type SpecReport struct { - // ContainerHierarchyTexts is a slice containing the text strings of - // all Describe/Context/When containers in this spec's hierarchy. - ContainerHierarchyTexts []string - - // ContainerHierarchyLocations is a slice containing the CodeLocations of - // all Describe/Context/When containers in this spec's hierarchy. - ContainerHierarchyLocations []CodeLocation - - // ContainerHierarchyLabels is a slice containing the labels of - // all Describe/Context/When containers in this spec's hierarchy - ContainerHierarchyLabels [][]string - - // LeafNodeType, LeadNodeLocation, LeafNodeLabels and LeafNodeText capture the NodeType, CodeLocation, and text - // of the Ginkgo node being tested (typically an NodeTypeIt node, though this can also be - // one of the NodeTypesForSuiteLevelNodes node types) - LeafNodeType NodeType - LeafNodeLocation CodeLocation - LeafNodeLabels []string - LeafNodeText string - - // State captures whether the spec has passed, failed, etc. - State SpecState - - // IsSerial captures whether the spec has the Serial decorator - IsSerial bool - - // IsInOrderedContainer captures whether the spec appears in an Ordered container - IsInOrderedContainer bool - - // StartTime and EndTime capture the start and end time of the spec - StartTime time.Time - EndTime time.Time - - // RunTime captures the duration of the spec - RunTime time.Duration - - // ParallelProcess captures the parallel process that this spec ran on - ParallelProcess int - - // RunningInParallel captures whether this spec is part of a suite that ran in parallel - RunningInParallel bool - - //Failure is populated if a spec has failed, panicked, been interrupted, or skipped by the user (e.g. calling Skip()) - //It includes detailed information about the Failure - Failure Failure - - // NumAttempts captures the number of times this Spec was run. - // Flakey specs can be retried with ginkgo --flake-attempts=N or the use of the FlakeAttempts decorator. - // Repeated specs can be retried with the use of the MustPassRepeatedly decorator - NumAttempts int - - // MaxFlakeAttempts captures whether the spec has been retried with ginkgo --flake-attempts=N or the use of the FlakeAttempts decorator. - MaxFlakeAttempts int - - // MaxMustPassRepeatedly captures whether the spec has the MustPassRepeatedly decorator - MaxMustPassRepeatedly int - - // CapturedGinkgoWriterOutput contains text printed to the GinkgoWriter - CapturedGinkgoWriterOutput string - - // CapturedStdOutErr contains text printed to stdout/stderr (when running in parallel) - // This is always empty when running in series or calling CurrentSpecReport() - // It is used internally by Ginkgo's reporter - CapturedStdOutErr string - - // ReportEntries contains any reports added via `AddReportEntry` - ReportEntries ReportEntries - - // ProgressReports contains any progress reports generated during this spec. These can either be manually triggered, or automatically generated by Ginkgo via the PollProgressAfter() decorator - ProgressReports []ProgressReport - - // AdditionalFailures contains any failures that occurred after the initial spec failure. These typically occur in cleanup nodes after the initial failure and are only emitted when running in verbose mode. - AdditionalFailures []AdditionalFailure - - // SpecEvents capture additional events that occur during the spec run - SpecEvents SpecEvents -} - -func (report SpecReport) MarshalJSON() ([]byte, error) { - //All this to avoid emitting an empty Failure struct in the JSON - out := struct { - ContainerHierarchyTexts []string - ContainerHierarchyLocations []CodeLocation - ContainerHierarchyLabels [][]string - LeafNodeType NodeType - LeafNodeLocation CodeLocation - LeafNodeLabels []string - LeafNodeText string - State SpecState - StartTime time.Time - EndTime time.Time - RunTime time.Duration - ParallelProcess int - Failure *Failure `json:",omitempty"` - NumAttempts int - MaxFlakeAttempts int - MaxMustPassRepeatedly int - CapturedGinkgoWriterOutput string `json:",omitempty"` - CapturedStdOutErr string `json:",omitempty"` - ReportEntries ReportEntries `json:",omitempty"` - ProgressReports []ProgressReport `json:",omitempty"` - AdditionalFailures []AdditionalFailure `json:",omitempty"` - SpecEvents SpecEvents `json:",omitempty"` - }{ - ContainerHierarchyTexts: report.ContainerHierarchyTexts, - ContainerHierarchyLocations: report.ContainerHierarchyLocations, - ContainerHierarchyLabels: report.ContainerHierarchyLabels, - LeafNodeType: report.LeafNodeType, - LeafNodeLocation: report.LeafNodeLocation, - LeafNodeLabels: report.LeafNodeLabels, - LeafNodeText: report.LeafNodeText, - State: report.State, - StartTime: report.StartTime, - EndTime: report.EndTime, - RunTime: report.RunTime, - ParallelProcess: report.ParallelProcess, - Failure: nil, - ReportEntries: nil, - NumAttempts: report.NumAttempts, - MaxFlakeAttempts: report.MaxFlakeAttempts, - MaxMustPassRepeatedly: report.MaxMustPassRepeatedly, - CapturedGinkgoWriterOutput: report.CapturedGinkgoWriterOutput, - CapturedStdOutErr: report.CapturedStdOutErr, - } - - if !report.Failure.IsZero() { - out.Failure = &(report.Failure) - } - if len(report.ReportEntries) > 0 { - out.ReportEntries = report.ReportEntries - } - if len(report.ProgressReports) > 0 { - out.ProgressReports = report.ProgressReports - } - if len(report.AdditionalFailures) > 0 { - out.AdditionalFailures = report.AdditionalFailures - } - if len(report.SpecEvents) > 0 { - out.SpecEvents = report.SpecEvents - } - - return json.Marshal(out) -} - -// CombinedOutput returns a single string representation of both CapturedStdOutErr and CapturedGinkgoWriterOutput -// Note that both are empty when using CurrentSpecReport() so CurrentSpecReport().CombinedOutput() will always be empty. -// CombinedOutput() is used internally by Ginkgo's reporter. -func (report SpecReport) CombinedOutput() string { - if report.CapturedStdOutErr == "" { - return report.CapturedGinkgoWriterOutput - } - if report.CapturedGinkgoWriterOutput == "" { - return report.CapturedStdOutErr - } - return report.CapturedStdOutErr + "\n" + report.CapturedGinkgoWriterOutput -} - -// Failed returns true if report.State is one of the SpecStateFailureStates -// (SpecStateFailed, SpecStatePanicked, SpecStateinterrupted, SpecStateAborted) -func (report SpecReport) Failed() bool { - return report.State.Is(SpecStateFailureStates) -} - -// FullText returns a concatenation of all the report.ContainerHierarchyTexts and report.LeafNodeText -func (report SpecReport) FullText() string { - texts := []string{} - texts = append(texts, report.ContainerHierarchyTexts...) - if report.LeafNodeText != "" { - texts = append(texts, report.LeafNodeText) - } - return strings.Join(texts, " ") -} - -// Labels returns a deduped set of all the spec's Labels. -func (report SpecReport) Labels() []string { - out := []string{} - seen := map[string]bool{} - for _, labels := range report.ContainerHierarchyLabels { - for _, label := range labels { - if !seen[label] { - seen[label] = true - out = append(out, label) - } - } - } - for _, label := range report.LeafNodeLabels { - if !seen[label] { - seen[label] = true - out = append(out, label) - } - } - - return out -} - -// MatchesLabelFilter returns true if the spec satisfies the passed in label filter query -func (report SpecReport) MatchesLabelFilter(query string) (bool, error) { - filter, err := ParseLabelFilter(query) - if err != nil { - return false, err - } - return filter(report.Labels()), nil -} - -// FileName() returns the name of the file containing the spec -func (report SpecReport) FileName() string { - return report.LeafNodeLocation.FileName -} - -// LineNumber() returns the line number of the leaf node -func (report SpecReport) LineNumber() int { - return report.LeafNodeLocation.LineNumber -} - -// FailureMessage() returns the failure message (or empty string if the test hasn't failed) -func (report SpecReport) FailureMessage() string { - return report.Failure.Message -} - -// FailureLocation() returns the location of the failure (or an empty CodeLocation if the test hasn't failed) -func (report SpecReport) FailureLocation() CodeLocation { - return report.Failure.Location -} - -// Timeline() returns a timeline view of the report -func (report SpecReport) Timeline() Timeline { - timeline := Timeline{} - if !report.Failure.IsZero() { - timeline = append(timeline, report.Failure) - if report.Failure.AdditionalFailure != nil { - timeline = append(timeline, *(report.Failure.AdditionalFailure)) - } - } - for _, additionalFailure := range report.AdditionalFailures { - timeline = append(timeline, additionalFailure) - } - for _, reportEntry := range report.ReportEntries { - timeline = append(timeline, reportEntry) - } - for _, progressReport := range report.ProgressReports { - timeline = append(timeline, progressReport) - } - for _, specEvent := range report.SpecEvents { - timeline = append(timeline, specEvent) - } - sort.Sort(timeline) - return timeline -} - -type SpecReports []SpecReport - -// WithLeafNodeType returns the subset of SpecReports with LeafNodeType matching one of the requested NodeTypes -func (reports SpecReports) WithLeafNodeType(nodeTypes NodeType) SpecReports { - count := 0 - for i := range reports { - if reports[i].LeafNodeType.Is(nodeTypes) { - count++ - } - } - - out := make(SpecReports, count) - j := 0 - for i := range reports { - if reports[i].LeafNodeType.Is(nodeTypes) { - out[j] = reports[i] - j++ - } - } - return out -} - -// WithState returns the subset of SpecReports with State matching one of the requested SpecStates -func (reports SpecReports) WithState(states SpecState) SpecReports { - count := 0 - for i := range reports { - if reports[i].State.Is(states) { - count++ - } - } - - out, j := make(SpecReports, count), 0 - for i := range reports { - if reports[i].State.Is(states) { - out[j] = reports[i] - j++ - } - } - return out -} - -// CountWithState returns the number of SpecReports with State matching one of the requested SpecStates -func (reports SpecReports) CountWithState(states SpecState) int { - n := 0 - for i := range reports { - if reports[i].State.Is(states) { - n += 1 - } - } - return n -} - -// If the Spec passes, CountOfFlakedSpecs returns the number of SpecReports that failed after multiple attempts. -func (reports SpecReports) CountOfFlakedSpecs() int { - n := 0 - for i := range reports { - if reports[i].MaxFlakeAttempts > 1 && reports[i].State.Is(SpecStatePassed) && reports[i].NumAttempts > 1 { - n += 1 - } - } - return n -} - -// If the Spec fails, CountOfRepeatedSpecs returns the number of SpecReports that passed after multiple attempts -func (reports SpecReports) CountOfRepeatedSpecs() int { - n := 0 - for i := range reports { - if reports[i].MaxMustPassRepeatedly > 1 && reports[i].State.Is(SpecStateFailureStates) && reports[i].NumAttempts > 1 { - n += 1 - } - } - return n -} - -// TimelineLocation captures the location of an event in the spec's timeline -type TimelineLocation struct { - //Offset is the offset (in bytes) of the event relative to the GinkgoWriter stream - Offset int `json:",omitempty"` - - //Order is the order of the event with respect to other events. The absolute value of Order - //is irrelevant. All that matters is that an event with a lower Order occurs before ane vent with a higher Order - Order int `json:",omitempty"` - - Time time.Time -} - -// TimelineEvent represent an event on the timeline -// consumers of Timeline will need to check the concrete type of each entry to determine how to handle it -type TimelineEvent interface { - GetTimelineLocation() TimelineLocation -} - -type Timeline []TimelineEvent - -func (t Timeline) Len() int { return len(t) } -func (t Timeline) Less(i, j int) bool { - return t[i].GetTimelineLocation().Order < t[j].GetTimelineLocation().Order -} -func (t Timeline) Swap(i, j int) { t[i], t[j] = t[j], t[i] } -func (t Timeline) WithoutHiddenReportEntries() Timeline { - out := Timeline{} - for _, event := range t { - if reportEntry, isReportEntry := event.(ReportEntry); isReportEntry && reportEntry.Visibility == ReportEntryVisibilityNever { - continue - } - out = append(out, event) - } - return out -} - -func (t Timeline) WithoutVeryVerboseSpecEvents() Timeline { - out := Timeline{} - for _, event := range t { - if specEvent, isSpecEvent := event.(SpecEvent); isSpecEvent && specEvent.IsOnlyVisibleAtVeryVerbose() { - continue - } - out = append(out, event) - } - return out -} - -// Failure captures failure information for an individual test -type Failure struct { - // Message - the failure message passed into Fail(...). When using a matcher library - // like Gomega, this will contain the failure message generated by Gomega. - // - // Message is also populated if the user has called Skip(...). - Message string - - // Location - the CodeLocation where the failure occurred - // This CodeLocation will include a fully-populated StackTrace - Location CodeLocation - - TimelineLocation TimelineLocation - - // ForwardedPanic - if the failure represents a captured panic (i.e. Summary.State == SpecStatePanicked) - // then ForwardedPanic will be populated with a string representation of the captured panic. - ForwardedPanic string `json:",omitempty"` - - // FailureNodeContext - one of three contexts describing the node in which the failure occurred: - // FailureNodeIsLeafNode means the failure occurred in the leaf node of the associated SpecReport. None of the other FailureNode fields will be populated - // FailureNodeAtTopLevel means the failure occurred in a non-leaf node that is defined at the top-level of the spec (i.e. not in a container). FailureNodeType and FailureNodeLocation will be populated. - // FailureNodeInContainer means the failure occurred in a non-leaf node that is defined within a container. FailureNodeType, FailureNodeLocation, and FailureNodeContainerIndex will be populated. - // - // FailureNodeType will contain the NodeType of the node in which the failure occurred. - // FailureNodeLocation will contain the CodeLocation of the node in which the failure occurred. - // If populated, FailureNodeContainerIndex will be the index into SpecReport.ContainerHierarchyTexts and SpecReport.ContainerHierarchyLocations that represents the parent container of the node in which the failure occurred. - FailureNodeContext FailureNodeContext `json:",omitempty"` - - FailureNodeType NodeType `json:",omitempty"` - - FailureNodeLocation CodeLocation `json:",omitempty"` - - FailureNodeContainerIndex int `json:",omitempty"` - - //ProgressReport is populated if the spec was interrupted or timed out - ProgressReport ProgressReport `json:",omitempty"` - - //AdditionalFailure is non-nil if a follow-on failure occurred within the same node after the primary failure. This only happens when a node has timed out or been interrupted. In such cases the AdditionalFailure can include information about where/why the spec was stuck. - AdditionalFailure *AdditionalFailure `json:",omitempty"` -} - -func (f Failure) IsZero() bool { - return f.Message == "" && (f.Location == CodeLocation{}) -} - -func (f Failure) GetTimelineLocation() TimelineLocation { - return f.TimelineLocation -} - -// FailureNodeContext captures the location context for the node containing the failing line of code -type FailureNodeContext uint - -const ( - FailureNodeContextInvalid FailureNodeContext = iota - - FailureNodeIsLeafNode - FailureNodeAtTopLevel - FailureNodeInContainer -) - -var fncEnumSupport = NewEnumSupport(map[uint]string{ - uint(FailureNodeContextInvalid): "INVALID FAILURE NODE CONTEXT", - uint(FailureNodeIsLeafNode): "leaf-node", - uint(FailureNodeAtTopLevel): "top-level", - uint(FailureNodeInContainer): "in-container", -}) - -func (fnc FailureNodeContext) String() string { - return fncEnumSupport.String(uint(fnc)) -} -func (fnc *FailureNodeContext) UnmarshalJSON(b []byte) error { - out, err := fncEnumSupport.UnmarshJSON(b) - *fnc = FailureNodeContext(out) - return err -} -func (fnc FailureNodeContext) MarshalJSON() ([]byte, error) { - return fncEnumSupport.MarshJSON(uint(fnc)) -} - -// AdditionalFailure capturs any additional failures that occur after the initial failure of a psec -// these typically occur in clean up nodes after the spec has failed. -// We can't simply use Failure as we want to track the SpecState to know what kind of failure this is -type AdditionalFailure struct { - State SpecState - Failure Failure -} - -func (f AdditionalFailure) GetTimelineLocation() TimelineLocation { - return f.Failure.TimelineLocation -} - -// SpecState captures the state of a spec -// To determine if a given `state` represents a failure state, use `state.Is(SpecStateFailureStates)` -type SpecState uint - -const ( - SpecStateInvalid SpecState = 0 - - SpecStatePending SpecState = 1 << iota - SpecStateSkipped - SpecStatePassed - SpecStateFailed - SpecStateAborted - SpecStatePanicked - SpecStateInterrupted - SpecStateTimedout -) - -var ssEnumSupport = NewEnumSupport(map[uint]string{ - uint(SpecStateInvalid): "INVALID SPEC STATE", - uint(SpecStatePending): "pending", - uint(SpecStateSkipped): "skipped", - uint(SpecStatePassed): "passed", - uint(SpecStateFailed): "failed", - uint(SpecStateAborted): "aborted", - uint(SpecStatePanicked): "panicked", - uint(SpecStateInterrupted): "interrupted", - uint(SpecStateTimedout): "timedout", -}) - -func (ss SpecState) String() string { - return ssEnumSupport.String(uint(ss)) -} -func (ss SpecState) GomegaString() string { - return ssEnumSupport.String(uint(ss)) -} -func (ss *SpecState) UnmarshalJSON(b []byte) error { - out, err := ssEnumSupport.UnmarshJSON(b) - *ss = SpecState(out) - return err -} -func (ss SpecState) MarshalJSON() ([]byte, error) { - return ssEnumSupport.MarshJSON(uint(ss)) -} - -var SpecStateFailureStates = SpecStateFailed | SpecStateTimedout | SpecStateAborted | SpecStatePanicked | SpecStateInterrupted - -func (ss SpecState) Is(states SpecState) bool { - return ss&states != 0 -} - -// ProgressReport captures the progress of the current spec. It is, effectively, a structured Ginkgo-aware stack trace -type ProgressReport struct { - Message string `json:",omitempty"` - ParallelProcess int `json:",omitempty"` - RunningInParallel bool `json:",omitempty"` - - ContainerHierarchyTexts []string `json:",omitempty"` - LeafNodeText string `json:",omitempty"` - LeafNodeLocation CodeLocation `json:",omitempty"` - SpecStartTime time.Time `json:",omitempty"` - - CurrentNodeType NodeType `json:",omitempty"` - CurrentNodeText string `json:",omitempty"` - CurrentNodeLocation CodeLocation `json:",omitempty"` - CurrentNodeStartTime time.Time `json:",omitempty"` - - CurrentStepText string `json:",omitempty"` - CurrentStepLocation CodeLocation `json:",omitempty"` - CurrentStepStartTime time.Time `json:",omitempty"` - - AdditionalReports []string `json:",omitempty"` - - CapturedGinkgoWriterOutput string `json:",omitempty"` - TimelineLocation TimelineLocation `json:",omitempty"` - - Goroutines []Goroutine `json:",omitempty"` -} - -func (pr ProgressReport) IsZero() bool { - return pr.CurrentNodeType == NodeTypeInvalid -} - -func (pr ProgressReport) Time() time.Time { - return pr.TimelineLocation.Time -} - -func (pr ProgressReport) SpecGoroutine() Goroutine { - for _, goroutine := range pr.Goroutines { - if goroutine.IsSpecGoroutine { - return goroutine - } - } - return Goroutine{} -} - -func (pr ProgressReport) HighlightedGoroutines() []Goroutine { - out := []Goroutine{} - for _, goroutine := range pr.Goroutines { - if goroutine.IsSpecGoroutine || !goroutine.HasHighlights() { - continue - } - out = append(out, goroutine) - } - return out -} - -func (pr ProgressReport) OtherGoroutines() []Goroutine { - out := []Goroutine{} - for _, goroutine := range pr.Goroutines { - if goroutine.IsSpecGoroutine || goroutine.HasHighlights() { - continue - } - out = append(out, goroutine) - } - return out -} - -func (pr ProgressReport) WithoutCapturedGinkgoWriterOutput() ProgressReport { - out := pr - out.CapturedGinkgoWriterOutput = "" - return out -} - -func (pr ProgressReport) WithoutOtherGoroutines() ProgressReport { - out := pr - filteredGoroutines := []Goroutine{} - for _, goroutine := range pr.Goroutines { - if goroutine.IsSpecGoroutine || goroutine.HasHighlights() { - filteredGoroutines = append(filteredGoroutines, goroutine) - } - } - out.Goroutines = filteredGoroutines - return out -} - -func (pr ProgressReport) GetTimelineLocation() TimelineLocation { - return pr.TimelineLocation -} - -type Goroutine struct { - ID uint64 - State string - Stack []FunctionCall - IsSpecGoroutine bool -} - -func (g Goroutine) IsZero() bool { - return g.ID == 0 -} - -func (g Goroutine) HasHighlights() bool { - for _, fc := range g.Stack { - if fc.Highlight { - return true - } - } - - return false -} - -type FunctionCall struct { - Function string - Filename string - Line int - Highlight bool `json:",omitempty"` - Source []string `json:",omitempty"` - SourceHighlight int `json:",omitempty"` -} - -// NodeType captures the type of a given Ginkgo Node -type NodeType uint - -const ( - NodeTypeInvalid NodeType = 0 - - NodeTypeContainer NodeType = 1 << iota - NodeTypeIt - - NodeTypeBeforeEach - NodeTypeJustBeforeEach - NodeTypeAfterEach - NodeTypeJustAfterEach - - NodeTypeBeforeAll - NodeTypeAfterAll - - NodeTypeBeforeSuite - NodeTypeSynchronizedBeforeSuite - NodeTypeAfterSuite - NodeTypeSynchronizedAfterSuite - - NodeTypeReportBeforeEach - NodeTypeReportAfterEach - NodeTypeReportBeforeSuite - NodeTypeReportAfterSuite - - NodeTypeCleanupInvalid - NodeTypeCleanupAfterEach - NodeTypeCleanupAfterAll - NodeTypeCleanupAfterSuite -) - -var NodeTypesForContainerAndIt = NodeTypeContainer | NodeTypeIt -var NodeTypesForSuiteLevelNodes = NodeTypeBeforeSuite | NodeTypeSynchronizedBeforeSuite | NodeTypeAfterSuite | NodeTypeSynchronizedAfterSuite | NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite | NodeTypeCleanupAfterSuite -var NodeTypesAllowedDuringCleanupInterrupt = NodeTypeAfterEach | NodeTypeJustAfterEach | NodeTypeAfterAll | NodeTypeAfterSuite | NodeTypeSynchronizedAfterSuite | NodeTypeCleanupAfterEach | NodeTypeCleanupAfterAll | NodeTypeCleanupAfterSuite -var NodeTypesAllowedDuringReportInterrupt = NodeTypeReportBeforeEach | NodeTypeReportAfterEach | NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite - -var ntEnumSupport = NewEnumSupport(map[uint]string{ - uint(NodeTypeInvalid): "INVALID NODE TYPE", - uint(NodeTypeContainer): "Container", - uint(NodeTypeIt): "It", - uint(NodeTypeBeforeEach): "BeforeEach", - uint(NodeTypeJustBeforeEach): "JustBeforeEach", - uint(NodeTypeAfterEach): "AfterEach", - uint(NodeTypeJustAfterEach): "JustAfterEach", - uint(NodeTypeBeforeAll): "BeforeAll", - uint(NodeTypeAfterAll): "AfterAll", - uint(NodeTypeBeforeSuite): "BeforeSuite", - uint(NodeTypeSynchronizedBeforeSuite): "SynchronizedBeforeSuite", - uint(NodeTypeAfterSuite): "AfterSuite", - uint(NodeTypeSynchronizedAfterSuite): "SynchronizedAfterSuite", - uint(NodeTypeReportBeforeEach): "ReportBeforeEach", - uint(NodeTypeReportAfterEach): "ReportAfterEach", - uint(NodeTypeReportBeforeSuite): "ReportBeforeSuite", - uint(NodeTypeReportAfterSuite): "ReportAfterSuite", - uint(NodeTypeCleanupInvalid): "DeferCleanup", - uint(NodeTypeCleanupAfterEach): "DeferCleanup (Each)", - uint(NodeTypeCleanupAfterAll): "DeferCleanup (All)", - uint(NodeTypeCleanupAfterSuite): "DeferCleanup (Suite)", -}) - -func (nt NodeType) String() string { - return ntEnumSupport.String(uint(nt)) -} -func (nt *NodeType) UnmarshalJSON(b []byte) error { - out, err := ntEnumSupport.UnmarshJSON(b) - *nt = NodeType(out) - return err -} -func (nt NodeType) MarshalJSON() ([]byte, error) { - return ntEnumSupport.MarshJSON(uint(nt)) -} - -func (nt NodeType) Is(nodeTypes NodeType) bool { - return nt&nodeTypes != 0 -} - -/* -SpecEvent captures a vareity of events that can occur when specs run. See SpecEventType for the list of available events. -*/ -type SpecEvent struct { - SpecEventType SpecEventType - - CodeLocation CodeLocation - TimelineLocation TimelineLocation - - Message string `json:",omitempty"` - Duration time.Duration `json:",omitempty"` - NodeType NodeType `json:",omitempty"` - Attempt int `json:",omitempty"` -} - -func (se SpecEvent) GetTimelineLocation() TimelineLocation { - return se.TimelineLocation -} - -func (se SpecEvent) IsOnlyVisibleAtVeryVerbose() bool { - return se.SpecEventType.Is(SpecEventByEnd | SpecEventNodeStart | SpecEventNodeEnd) -} - -func (se SpecEvent) GomegaString() string { - out := &strings.Builder{} - out.WriteString("[" + se.SpecEventType.String() + " SpecEvent] ") - if se.Message != "" { - out.WriteString("Message=") - out.WriteString(`"` + se.Message + `",`) - } - if se.Duration != 0 { - out.WriteString("Duration=" + se.Duration.String() + ",") - } - if se.NodeType != NodeTypeInvalid { - out.WriteString("NodeType=" + se.NodeType.String() + ",") - } - if se.Attempt != 0 { - out.WriteString(fmt.Sprintf("Attempt=%d", se.Attempt) + ",") - } - out.WriteString("CL=" + se.CodeLocation.String() + ",") - out.WriteString(fmt.Sprintf("TL.Offset=%d", se.TimelineLocation.Offset)) - - return out.String() -} - -type SpecEvents []SpecEvent - -func (se SpecEvents) WithType(seType SpecEventType) SpecEvents { - out := SpecEvents{} - for _, event := range se { - if event.SpecEventType.Is(seType) { - out = append(out, event) - } - } - return out -} - -type SpecEventType uint - -const ( - SpecEventInvalid SpecEventType = 0 - - SpecEventByStart SpecEventType = 1 << iota - SpecEventByEnd - SpecEventNodeStart - SpecEventNodeEnd - SpecEventSpecRepeat - SpecEventSpecRetry -) - -var seEnumSupport = NewEnumSupport(map[uint]string{ - uint(SpecEventInvalid): "INVALID SPEC EVENT", - uint(SpecEventByStart): "By", - uint(SpecEventByEnd): "By (End)", - uint(SpecEventNodeStart): "Node", - uint(SpecEventNodeEnd): "Node (End)", - uint(SpecEventSpecRepeat): "Repeat", - uint(SpecEventSpecRetry): "Retry", -}) - -func (se SpecEventType) String() string { - return seEnumSupport.String(uint(se)) -} -func (se *SpecEventType) UnmarshalJSON(b []byte) error { - out, err := seEnumSupport.UnmarshJSON(b) - *se = SpecEventType(out) - return err -} -func (se SpecEventType) MarshalJSON() ([]byte, error) { - return seEnumSupport.MarshJSON(uint(se)) -} - -func (se SpecEventType) Is(specEventTypes SpecEventType) bool { - return se&specEventTypes != 0 -} diff --git a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/version.go b/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/version.go deleted file mode 100644 index ed9346474..000000000 --- a/plugins/traefik/vendor/github.com/onsi/ginkgo/v2/types/version.go +++ /dev/null @@ -1,3 +0,0 @@ -package types - -const VERSION = "2.15.0" diff --git a/plugins/traefik/vendor/github.com/quic-go/qpack/.gitmodules b/plugins/traefik/vendor/github.com/quic-go/qpack/.gitmodules index 5ac16f084..47b628623 100644 --- a/plugins/traefik/vendor/github.com/quic-go/qpack/.gitmodules +++ b/plugins/traefik/vendor/github.com/quic-go/qpack/.gitmodules @@ -1,3 +1,3 @@ -[submodule "integrationtests/interop/qifs"] - path = integrationtests/interop/qifs +[submodule "interop/qifs"] + path = interop/qifs url = https://github.com/qpackers/qifs.git diff --git a/plugins/traefik/vendor/github.com/quic-go/qpack/.golangci.yml b/plugins/traefik/vendor/github.com/quic-go/qpack/.golangci.yml index e6b574e8c..2b48866b0 100644 --- a/plugins/traefik/vendor/github.com/quic-go/qpack/.golangci.yml +++ b/plugins/traefik/vendor/github.com/quic-go/qpack/.golangci.yml @@ -1,23 +1,22 @@ -run: -linters-settings: +version: "2" linters: - disable-all: true + default: none enable: - asciicheck - copyloopvar - exhaustive - - goconst - - gofmt # redundant, since gofmt *should* be a no-op after gofumpt - - gofumpt - - goimports - - gosimple - govet - ineffassign - misspell + - nolintlint - prealloc - staticcheck - - stylecheck - unconvert - unparam - unused - + - usetesting +formatters: + enable: + - gofmt + - gofumpt + - goimports diff --git a/plugins/traefik/vendor/github.com/quic-go/qpack/README.md b/plugins/traefik/vendor/github.com/quic-go/qpack/README.md index 5bf1f77bd..df1164906 100644 --- a/plugins/traefik/vendor/github.com/quic-go/qpack/README.md +++ b/plugins/traefik/vendor/github.com/quic-go/qpack/README.md @@ -4,9 +4,9 @@ [![Code Coverage](https://img.shields.io/codecov/c/github/quic-go/qpack/master.svg?style=flat-square)](https://codecov.io/gh/quic-go/qpack) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/quic-go.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:quic-go) -This is a minimal QPACK ([RFC 9204](https://datatracker.ietf.org/doc/html/rfc9204)) implementation in Go. It is minimal in the sense that it doesn't use the dynamic table at all, but just the static table and (Huffman encoded) string literals. Wherever possible, it reuses code from the [HPACK implementation in the Go standard library](https://github.com/golang/net/tree/master/http2/hpack). +This is a minimal QPACK ([RFC 9204](https://datatracker.ietf.org/doc/html/rfc9204)) implementation in Go. It reuses the Huffman encoder / decoder code from the [HPACK implementation in the Go standard library](https://github.com/golang/net/tree/master/http2/hpack). -It is interoperable with other QPACK implementations (both encoders and decoders), however it won't achieve a high compression efficiency. If you're interested in dynamic table support, please comment on [the issue](https://github.com/quic-go/qpack/issues/33). +It is fully interoperable with other QPACK implementations (both encoders and decoders). However, it does not support the dynamic table and relies solely on the static table and string literals (including Huffman encoding), which limits compression efficiency. If you're interested in dynamic table support, please comment on [issue #33](https://github.com/quic-go/qpack/issues/33). ## Running the Interop Tests @@ -17,5 +17,5 @@ git submodule update --init --recursive Then run the tests: ```bash -go test -v ./integrationtests/interop/ +go test -v ./interop ``` diff --git a/plugins/traefik/vendor/github.com/quic-go/qpack/decoder.go b/plugins/traefik/vendor/github.com/quic-go/qpack/decoder.go index 88ea8ebbf..af8bbb59d 100644 --- a/plugins/traefik/vendor/github.com/quic-go/qpack/decoder.go +++ b/plugins/traefik/vendor/github.com/quic-go/qpack/decoder.go @@ -1,248 +1,157 @@ package qpack import ( - "bytes" "errors" "fmt" - "sync" + "io" "golang.org/x/net/http2/hpack" ) -// A decodingError is something the spec defines as a decoding error. -type decodingError struct { - err error -} - -func (de decodingError) Error() string { - return fmt.Sprintf("decoding error: %v", de.err) -} - -// An invalidIndexError is returned when an encoder references a table -// entry before the static table or after the end of the dynamic table. +// An invalidIndexError is returned when decoding encounters an invalid index +// (e.g., an index that is out of bounds for the static table). type invalidIndexError int func (e invalidIndexError) Error() string { return fmt.Sprintf("invalid indexed representation index %d", int(e)) } -var errNoDynamicTable = decodingError{errors.New("no dynamic table")} - -// errNeedMore is an internal sentinel error value that means the -// buffer is truncated and we need to read more data before we can -// continue parsing. -var errNeedMore = errors.New("need more data") +var errNoDynamicTable = errors.New("no dynamic table") -// A Decoder is the decoding context for incremental processing of -// header blocks. -type Decoder struct { - mutex sync.Mutex +// A Decoder decodes QPACK header blocks. +// A Decoder can be reused to decode multiple header blocks on different streams +// on the same connection (e.g., headers then trailers). +// This will be useful when dynamic table support is added. +type Decoder struct{} - emitFunc func(f HeaderField) +// DecodeFunc is a function that decodes the next header field from a header block. +// It should be called repeatedly until it returns io.EOF. +// It returns io.EOF when all header fields have been decoded. +// Any error other than io.EOF indicates a decoding error. +type DecodeFunc func() (HeaderField, error) - readRequiredInsertCount bool - readDeltaBase bool - - // buf is the unparsed buffer. It's only written to - // saveBuf if it was truncated in the middle of a header - // block. Because it's usually not owned, we can only - // process it under Write. - buf []byte // not owned; only valid during Write - - // saveBuf is previous data passed to Write which we weren't able - // to fully parse before. Unlike buf, we own this data. - saveBuf bytes.Buffer +// NewDecoder returns a new Decoder. +func NewDecoder() *Decoder { + return &Decoder{} } -// NewDecoder returns a new decoder -// The emitFunc will be called for each valid field parsed, -// in the same goroutine as calls to Write, before Write returns. -func NewDecoder(emitFunc func(f HeaderField)) *Decoder { - return &Decoder{emitFunc: emitFunc} -} - -func (d *Decoder) Write(p []byte) (int, error) { - if len(p) == 0 { - return 0, nil - } - - d.mutex.Lock() - n, err := d.writeLocked(p) - d.mutex.Unlock() - return n, err -} - -func (d *Decoder) writeLocked(p []byte) (int, error) { - // Only copy the data if we have to. Optimistically assume - // that p will contain a complete header block. - if d.saveBuf.Len() == 0 { - d.buf = p - } else { - d.saveBuf.Write(p) - d.buf = d.saveBuf.Bytes() - d.saveBuf.Reset() - } - - if err := d.decode(); err != nil { - if err != errNeedMore { - return 0, err +// Decode returns a function that decodes header fields from the given header block. +// It does not copy the slice; the caller must ensure it remains valid during decoding. +func (d *Decoder) Decode(p []byte) DecodeFunc { + var readRequiredInsertCount bool + var readDeltaBase bool + + return func() (HeaderField, error) { + if !readRequiredInsertCount { + requiredInsertCount, rest, err := readVarInt(8, p) + if err != nil { + return HeaderField{}, err + } + p = rest + readRequiredInsertCount = true + if requiredInsertCount != 0 { + return HeaderField{}, errors.New("expected Required Insert Count to be zero") + } } - // TODO: limit the size of the buffer - d.saveBuf.Write(d.buf) - } - return len(p), nil -} - -// DecodeFull decodes an entire block. -func (d *Decoder) DecodeFull(p []byte) ([]HeaderField, error) { - if len(p) == 0 { - return []HeaderField{}, nil - } - - d.mutex.Lock() - defer d.mutex.Unlock() - - saveFunc := d.emitFunc - defer func() { d.emitFunc = saveFunc }() - - var hf []HeaderField - d.emitFunc = func(f HeaderField) { hf = append(hf, f) } - if _, err := d.writeLocked(p); err != nil { - return nil, err - } - if err := d.Close(); err != nil { - return nil, err - } - return hf, nil -} - -// Close declares that the decoding is complete and resets the Decoder -// to be reused again for a new header block. If there is any remaining -// data in the decoder's buffer, Close returns an error. -func (d *Decoder) Close() error { - if d.saveBuf.Len() > 0 { - d.saveBuf.Reset() - return decodingError{errors.New("truncated headers")} - } - d.readRequiredInsertCount = false - d.readDeltaBase = false - return nil -} -func (d *Decoder) decode() error { - if !d.readRequiredInsertCount { - requiredInsertCount, rest, err := readVarInt(8, d.buf) - if err != nil { - return err - } - d.readRequiredInsertCount = true - if requiredInsertCount != 0 { - return decodingError{errors.New("expected Required Insert Count to be zero")} - } - d.buf = rest - } - if !d.readDeltaBase { - base, rest, err := readVarInt(7, d.buf) - if err != nil { - return err + if !readDeltaBase { + base, rest, err := readVarInt(7, p) + if err != nil { + return HeaderField{}, err + } + p = rest + readDeltaBase = true + if base != 0 { + return HeaderField{}, errors.New("expected Base to be zero") + } } - d.readDeltaBase = true - if base != 0 { - return decodingError{errors.New("expected Base to be zero")} + + if len(p) == 0 { + return HeaderField{}, io.EOF } - d.buf = rest - } - if len(d.buf) == 0 { - return errNeedMore - } - for len(d.buf) > 0 { - b := d.buf[0] + b := p[0] + var hf HeaderField + var rest []byte var err error switch { - case b&0x80 > 0: // 1xxxxxxx - err = d.parseIndexedHeaderField() - case b&0xc0 == 0x40: // 01xxxxxx - err = d.parseLiteralHeaderField() - case b&0xe0 == 0x20: // 001xxxxx - err = d.parseLiteralHeaderFieldWithoutNameReference() + case (b & 0x80) > 0: // 1xxxxxxx + hf, rest, err = d.parseIndexedHeaderField(p) + case (b & 0xc0) == 0x40: // 01xxxxxx + hf, rest, err = d.parseLiteralHeaderField(p) + case (b & 0xe0) == 0x20: // 001xxxxx + hf, rest, err = d.parseLiteralHeaderFieldWithoutNameReference(p) default: err = fmt.Errorf("unexpected type byte: %#x", b) } + p = rest if err != nil { - return err + return HeaderField{}, err } + return hf, nil } - return nil } -func (d *Decoder) parseIndexedHeaderField() error { - buf := d.buf +func (d *Decoder) parseIndexedHeaderField(buf []byte) (_ HeaderField, rest []byte, _ error) { if buf[0]&0x40 == 0 { - return errNoDynamicTable + return HeaderField{}, buf, errNoDynamicTable } - index, buf, err := readVarInt(6, buf) + index, rest, err := readVarInt(6, buf) if err != nil { - return err + return HeaderField{}, buf, err } hf, ok := d.at(index) if !ok { - return decodingError{invalidIndexError(index)} + return HeaderField{}, buf, invalidIndexError(index) } - d.emitFunc(hf) - d.buf = buf - return nil + return hf, rest, nil } -func (d *Decoder) parseLiteralHeaderField() error { - buf := d.buf +func (d *Decoder) parseLiteralHeaderField(buf []byte) (_ HeaderField, rest []byte, _ error) { if buf[0]&0x10 == 0 { - return errNoDynamicTable + return HeaderField{}, buf, errNoDynamicTable } // We don't need to check the value of the N-bit here. // It's only relevant when re-encoding header fields, // and determines whether the header field can be added to the dynamic table. // Since we don't support the dynamic table, we can ignore it. - index, buf, err := readVarInt(4, buf) + index, rest, err := readVarInt(4, buf) if err != nil { - return err + return HeaderField{}, buf, err } hf, ok := d.at(index) if !ok { - return decodingError{invalidIndexError(index)} + return HeaderField{}, buf, invalidIndexError(index) } + buf = rest if len(buf) == 0 { - return errNeedMore + return HeaderField{}, buf, io.ErrUnexpectedEOF } usesHuffman := buf[0]&0x80 > 0 - val, buf, err := d.readString(buf, 7, usesHuffman) + val, rest, err := d.readString(rest, 7, usesHuffman) if err != nil { - return err + return HeaderField{}, rest, err } hf.Value = val - d.emitFunc(hf) - d.buf = buf - return nil + return hf, rest, nil } -func (d *Decoder) parseLiteralHeaderFieldWithoutNameReference() error { - buf := d.buf +func (d *Decoder) parseLiteralHeaderFieldWithoutNameReference(buf []byte) (_ HeaderField, rest []byte, _ error) { usesHuffmanForName := buf[0]&0x8 > 0 - name, buf, err := d.readString(buf, 3, usesHuffmanForName) + name, rest, err := d.readString(buf, 3, usesHuffmanForName) if err != nil { - return err + return HeaderField{}, rest, err } + buf = rest if len(buf) == 0 { - return errNeedMore + return HeaderField{}, rest, io.ErrUnexpectedEOF } usesHuffmanForVal := buf[0]&0x80 > 0 - val, buf, err := d.readString(buf, 7, usesHuffmanForVal) + val, rest, err := d.readString(buf, 7, usesHuffmanForVal) if err != nil { - return err + return HeaderField{}, rest, err } - d.emitFunc(HeaderField{Name: name, Value: val}) - d.buf = buf - return nil + return HeaderField{Name: name, Value: val}, rest, nil } func (d *Decoder) readString(buf []byte, n uint8, usesHuffman bool) (string, []byte, error) { @@ -251,11 +160,10 @@ func (d *Decoder) readString(buf []byte, n uint8, usesHuffman bool) (string, []b return "", nil, err } if uint64(len(buf)) < l { - return "", nil, errNeedMore + return "", nil, io.ErrUnexpectedEOF } var val string if usesHuffman { - var err error val, err = hpack.HuffmanDecodeToString(buf[:l]) if err != nil { return "", nil, err diff --git a/plugins/traefik/vendor/github.com/quic-go/qpack/static_table.go b/plugins/traefik/vendor/github.com/quic-go/qpack/static_table.go index 73c365e1f..93eca275b 100644 --- a/plugins/traefik/vendor/github.com/quic-go/qpack/static_table.go +++ b/plugins/traefik/vendor/github.com/quic-go/qpack/static_table.go @@ -105,7 +105,7 @@ var staticTableEntries = [...]HeaderField{ // Only needed for tests. // use go:linkname to retrieve the static table. // -//nolint:deadcode,unused +//nolint:unused func getStaticTable() []HeaderField { return staticTableEntries[:] } diff --git a/plugins/traefik/vendor/github.com/quic-go/qpack/varint.go b/plugins/traefik/vendor/github.com/quic-go/qpack/varint.go index 28d71122e..30ff0932c 100644 --- a/plugins/traefik/vendor/github.com/quic-go/qpack/varint.go +++ b/plugins/traefik/vendor/github.com/quic-go/qpack/varint.go @@ -2,7 +2,10 @@ package qpack // copied from the Go standard library HPACK implementation -import "errors" +import ( + "errors" + "io" +) var errVarintOverflow = errors.New("varint integer overflow") @@ -31,13 +34,13 @@ func appendVarInt(dst []byte, n byte, i uint64) []byte { // n must always be between 1 and 8. // // The returned remain buffer is either a smaller suffix of p, or err != nil. -// The error is errNeedMore if p doesn't contain a complete integer. +// The error is io.ErrUnexpectedEOF if p doesn't contain a complete integer. func readVarInt(n byte, p []byte) (i uint64, remain []byte, err error) { if n < 1 || n > 8 { panic("bad n") } if len(p) == 0 { - return 0, p, errNeedMore + return 0, p, io.ErrUnexpectedEOF } i = uint64(p[0]) if n < 8 { @@ -62,5 +65,5 @@ func readVarInt(n byte, p []byte) (i uint64, remain []byte, err error) { return 0, origP, errVarintOverflow } } - return 0, origP, errNeedMore + return 0, origP, io.ErrUnexpectedEOF } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/.golangci.yml b/plugins/traefik/vendor/github.com/quic-go/quic-go/.golangci.yml index 1174b125e..cd2a3f87f 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/.golangci.yml +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/.golangci.yml @@ -1,51 +1,109 @@ -linters-settings: - misspell: - ignore-words: - - ect - depguard: - rules: - quicvarint: - list-mode: strict - files: - - "**/github.com/quic-go/quic-go/quicvarint/*" - - "!$test" - allow: - - $gostd - +version: "2" linters: - disable-all: true + default: none enable: - asciicheck - copyloopvar - depguard - exhaustive - - goimports - - gofmt # redundant, since gofmt *should* be a no-op after gofumpt - - gofumpt - - gosimple - govet - ineffassign - misspell + - nolintlint - prealloc - staticcheck - - stylecheck - unconvert - unparam - unused - -issues: - exclude-files: - - internal/handshake/cipher_suite.go - exclude-rules: - - path: internal/qtls - linters: - - depguard - - path: _test\.go - linters: - - exhaustive - - prealloc - - unparam - - path: _test\.go - text: "SA1029:" - linters: - - staticcheck + - usetesting + settings: + depguard: + rules: + random: + deny: + - pkg: "math/rand$" + desc: use math/rand/v2 + - pkg: "golang.org/x/exp/rand" + desc: use math/rand/v2 + quicvarint: + list-mode: strict + files: + - '**/github.com/quic-go/quic-go/quicvarint/*' + - '!$test' + allow: + - $gostd + rsa: + list-mode: original + deny: + - pkg: crypto/rsa + desc: "use crypto/ed25519 instead" + ginkgo: + list-mode: original + deny: + - pkg: github.com/onsi/ginkgo + desc: "use standard Go tests" + - pkg: github.com/onsi/ginkgo/v2 + desc: "use standard Go tests" + - pkg: github.com/onsi/gomega + desc: "use standard Go tests" + http3-internal: + list-mode: lax + files: + - '**/http3/**' + deny: + - pkg: 'github.com/quic-go/quic-go/internal' + desc: 'no dependency on quic-go/internal' + allow: + - 'github.com/quic-go/quic-go/internal/synctest' + misspell: + ignore-rules: + - ect + # see https://github.com/ldez/usetesting/issues/10 + usetesting: + context-background: false + context-todo: false + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - depguard + path: internal/qtls + - linters: + - exhaustive + - prealloc + - unparam + path: _test\.go + - linters: + - staticcheck + path: _test\.go + text: 'SA1029:' # inappropriate key in call to context.WithValue + # WebTransport still relies on the ConnectionTracingID and ConnectionTracingKey. + # See https://github.com/quic-go/quic-go/issues/4405 for more details. + - linters: + - staticcheck + paths: + - http3/ + - integrationtests/self/http_test.go + text: 'SA1019:.+quic\.ConnectionTracing(ID|Key)' + paths: + - internal/handshake/cipher_suite.go + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gofmt + - gofumpt + - goimports + exclusions: + generated: lax + paths: + - internal/handshake/cipher_suite.go + - third_party$ + - builtin$ + - examples$ diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/Changelog.md b/plugins/traefik/vendor/github.com/quic-go/quic-go/Changelog.md deleted file mode 100644 index 82df5fb24..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/Changelog.md +++ /dev/null @@ -1,109 +0,0 @@ -# Changelog - -## v0.22.0 (2021-07-25) - -- Use `ReadBatch` to read multiple UDP packets from the socket with a single syscall -- Add a config option (`Config.DisableVersionNegotiationPackets`) to disable sending of Version Negotiation packets -- Drop support for QUIC draft versions 32 and 34 -- Remove the `RetireBugBackwardsCompatibilityMode`, which was intended to mitigate a bug when retiring connection IDs in quic-go in v0.17.2 and ealier - -## v0.21.2 (2021-07-15) - -- Update qtls (for Go 1.15, 1.16 and 1.17rc1) to include the fix for the crypto/tls panic (see https://groups.google.com/g/golang-dev/c/5LJ2V7rd-Ag/m/YGLHVBZ6AAAJ for details) - -## v0.21.0 (2021-06-01) - -- quic-go now supports RFC 9000! - -## v0.20.0 (2021-03-19) - -- Remove the `quic.Config.HandshakeTimeout`. Introduce a `quic.Config.HandshakeIdleTimeout`. - -## v0.17.1 (2020-06-20) - -- Supports QUIC WG draft-29. -- Improve bundling of ACK frames (#2543). - -## v0.16.0 (2020-05-31) - -- Supports QUIC WG draft-28. - -## v0.15.0 (2020-03-01) - -- Supports QUIC WG draft-27. -- Add support for 0-RTT. -- Remove `Session.Close()`. Applications need to pass an application error code to the transport using `Session.CloseWithError()`. -- Make the TLS Cipher Suites configurable (via `tls.Config.CipherSuites`). - -## v0.14.0 (2019-12-04) - -- Supports QUIC WG draft-24. - -## v0.13.0 (2019-11-05) - -- Supports QUIC WG draft-23. -- Add an `EarlyListener` that allows sending of 0.5-RTT data. -- Add a `TokenStore` to store address validation tokens. -- Issue and use new connection IDs during a connection. - -## v0.12.0 (2019-08-05) - -- Implement HTTP/3. -- Rename `quic.Cookie` to `quic.Token` and `quic.Config.AcceptCookie` to `quic.Config.AcceptToken`. -- Distinguish between Retry tokens and tokens sent in NEW_TOKEN frames. -- Enforce application protocol negotiation (via `tls.Config.NextProtos`). -- Use a varint for error codes. -- Add support for [quic-trace](https://github.com/google/quic-trace). -- Add a context to `Listener.Accept`, `Session.Accept{Uni}Stream` and `Session.Open{Uni}StreamSync`. -- Implement TLS key updates. - -## v0.11.0 (2019-04-05) - -- Drop support for gQUIC. For qQUIC support, please switch to the *gquic* branch. -- Implement QUIC WG draft-19. -- Use [qtls](https://github.com/marten-seemann/qtls) for TLS 1.3. -- Return a `tls.ConnectionState` from `quic.Session.ConnectionState()`. -- Remove the error return values from `quic.Stream.CancelRead()` and `quic.Stream.CancelWrite()` - -## v0.10.0 (2018-08-28) - -- Add support for QUIC 44, drop support for QUIC 42. - -## v0.9.0 (2018-08-15) - -- Add a `quic.Config` option for the length of the connection ID (for IETF QUIC). -- Split Session.Close into one method for regular closing and one for closing with an error. - -## v0.8.0 (2018-06-26) - -- Add support for unidirectional streams (for IETF QUIC). -- Add a `quic.Config` option for the maximum number of incoming streams. -- Add support for QUIC 42 and 43. -- Add dial functions that use a context. -- Multiplex clients on a net.PacketConn, when using Dial(conn). - -## v0.7.0 (2018-02-03) - -- The lower boundary for packets included in ACKs is now derived, and the value sent in STOP_WAITING frames is ignored. -- Remove `DialNonFWSecure` and `DialAddrNonFWSecure`. -- Expose the `ConnectionState` in the `Session` (experimental API). -- Implement packet pacing. - -## v0.6.0 (2017-12-12) - -- Add support for QUIC 39, drop support for QUIC 35 - 37 -- Added `quic.Config` options for maximal flow control windows -- Add a `quic.Config` option for QUIC versions -- Add a `quic.Config` option to request omission of the connection ID from a server -- Add a `quic.Config` option to configure the source address validation -- Add a `quic.Config` option to configure the handshake timeout -- Add a `quic.Config` option to configure the idle timeout -- Add a `quic.Config` option to configure keep-alive -- Rename the STK to Cookie -- Implement `net.Conn`-style deadlines for streams -- Remove the `tls.Config` from the `quic.Config`. The `tls.Config` must now be passed to the `Dial` and `Listen` functions as a separate parameter. See the [Godoc](https://godoc.org/github.com/quic-go/quic-go) for details. -- Changed the log level environment variable to only accept strings ("DEBUG", "INFO", "ERROR"), see [the wiki](https://github.com/quic-go/quic-go/wiki/Logging) for more details. -- Rename the `h2quic.QuicRoundTripper` to `h2quic.RoundTripper` -- Changed `h2quic.Server.Serve()` to accept a `net.PacketConn` -- Drop support for Go 1.7 and 1.8. -- Various bugfixes diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/README.md b/plugins/traefik/vendor/github.com/quic-go/quic-go/README.md index ccc9e2133..53bbf4a0a 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/README.md +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/README.md @@ -5,7 +5,7 @@ [![Documentation](https://img.shields.io/badge/docs-quic--go.net-red?style=flat)](https://quic-go.net/docs/) [![PkgGoDev](https://pkg.go.dev/badge/github.com/quic-go/quic-go)](https://pkg.go.dev/github.com/quic-go/quic-go) [![Code Coverage](https://img.shields.io/codecov/c/github/quic-go/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/quic-go/quic-go/) -[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/quic-go.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:quic-go) +[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/quic-go.svg)](https://issues.oss-fuzz.com/issues?q=quic-go) quic-go is an implementation of the QUIC protocol ([RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000), [RFC 9001](https://datatracker.ietf.org/doc/html/rfc9001), [RFC 9002](https://datatracker.ietf.org/doc/html/rfc9002)) in Go. It has support for HTTP/3 ([RFC 9114](https://datatracker.ietf.org/doc/html/rfc9114)), including QPACK ([RFC 9204](https://datatracker.ietf.org/doc/html/rfc9204)) and HTTP Datagrams ([RFC 9297](https://datatracker.ietf.org/doc/html/rfc9297)). @@ -15,6 +15,7 @@ In addition to these base RFCs, it also implements the following RFCs: * Datagram Packetization Layer Path MTU Discovery (DPLPMTUD, [RFC 8899](https://datatracker.ietf.org/doc/html/rfc8899)) * QUIC Version 2 ([RFC 9369](https://datatracker.ietf.org/doc/html/rfc9369)) * QUIC Event Logging using qlog ([draft-ietf-quic-qlog-main-schema](https://datatracker.ietf.org/doc/draft-ietf-quic-qlog-main-schema/) and [draft-ietf-quic-qlog-quic-events](https://datatracker.ietf.org/doc/draft-ietf-quic-qlog-quic-events/)) +* QUIC Stream Resets with Partial Delivery ([draft-ietf-quic-reliable-stream-reset](https://datatracker.ietf.org/doc/html/draft-ietf-quic-reliable-stream-reset-07)) Support for WebTransport over HTTP/3 ([draft-ietf-webtrans-http3](https://datatracker.ietf.org/doc/draft-ietf-webtrans-http3/)) is implemented in [webtransport-go](https://github.com/quic-go/webtransport-go). @@ -33,6 +34,7 @@ Detailed documentation can be found on [quic-go.net](https://quic-go.net/docs/). | [gost](https://github.com/go-gost/gost) | A simple security tunnel written in Go | ![GitHub Repo stars](https://img.shields.io/github/stars/go-gost/gost?style=flat-square) | | [Hysteria](https://github.com/apernet/hysteria) | A powerful, lightning fast and censorship resistant proxy | ![GitHub Repo stars](https://img.shields.io/github/stars/apernet/hysteria?style=flat-square) | | [Mercure](https://github.com/dunglas/mercure) | An open, easy, fast, reliable and battery-efficient solution for real-time communications | ![GitHub Repo stars](https://img.shields.io/github/stars/dunglas/mercure?style=flat-square) | +| [nodepass](https://github.com/yosebyte/nodepass) | A secure, efficient TCP/UDP tunneling solution that delivers fast, reliable access across network restrictions using pre-established TCP/QUIC connections | ![GitHub Repo stars](https://img.shields.io/github/stars/yosebyte/nodepass?style=flat-square) | | [OONI Probe](https://github.com/ooni/probe-cli) | Next generation OONI Probe. Library and CLI tool. | ![GitHub Repo stars](https://img.shields.io/github/stars/ooni/probe-cli?style=flat-square) | | [reverst](https://github.com/flipt-io/reverst) | Reverse Tunnels in Go over HTTP/3 and QUIC | ![GitHub Repo stars](https://img.shields.io/github/stars/flipt-io/reverst?style=flat-square) | | [RoadRunner](https://github.com/roadrunner-server/roadrunner) | High-performance PHP application server, process manager written in Go and powered with plugins | ![GitHub Repo stars](https://img.shields.io/github/stars/roadrunner-server/roadrunner?style=flat-square) | diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/client.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/client.go index 29a715cc6..63132f2de 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/client.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/client.go @@ -15,8 +15,8 @@ var generateConnectionIDForInitial = protocol.GenerateConnectionIDForInitial // DialAddr establishes a new QUIC connection to a server. // It resolves the address, and then creates a new UDP connection to dial the QUIC server. // When the QUIC connection is closed, this UDP connection is closed. -// See Dial for more details. -func DialAddr(ctx context.Context, addr string, tlsConf *tls.Config, conf *Config) (Connection, error) { +// See [Dial] for more details. +func DialAddr(ctx context.Context, addr string, tlsConf *tls.Config, conf *Config) (*Conn, error) { udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) if err != nil { return nil, err @@ -29,12 +29,17 @@ func DialAddr(ctx context.Context, addr string, tlsConf *tls.Config, conf *Confi if err != nil { return nil, err } - return tr.dial(ctx, udpAddr, addr, tlsConf, conf, false) + conn, err := tr.dial(ctx, udpAddr, addr, tlsConf, conf, false) + if err != nil { + tr.Close() + return nil, err + } + return conn, nil } // DialAddrEarly establishes a new 0-RTT QUIC connection to a server. -// See DialAddr for more details. -func DialAddrEarly(ctx context.Context, addr string, tlsConf *tls.Config, conf *Config) (EarlyConnection, error) { +// See [DialAddr] for more details. +func DialAddrEarly(ctx context.Context, addr string, tlsConf *tls.Config, conf *Config) (*Conn, error) { udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) if err != nil { return nil, err @@ -56,8 +61,8 @@ func DialAddrEarly(ctx context.Context, addr string, tlsConf *tls.Config, conf * } // DialEarly establishes a new 0-RTT QUIC connection to a server using a net.PacketConn. -// See Dial for more details. -func DialEarly(ctx context.Context, c net.PacketConn, addr net.Addr, tlsConf *tls.Config, conf *Config) (EarlyConnection, error) { +// See [Dial] for more details. +func DialEarly(ctx context.Context, c net.PacketConn, addr net.Addr, tlsConf *tls.Config, conf *Config) (*Conn, error) { dl, err := setupTransport(c, tlsConf, false) if err != nil { return nil, err @@ -71,15 +76,15 @@ func DialEarly(ctx context.Context, c net.PacketConn, addr net.Addr, tlsConf *tl } // Dial establishes a new QUIC connection to a server using a net.PacketConn. -// If the PacketConn satisfies the OOBCapablePacketConn interface (as a net.UDPConn does), +// If the PacketConn satisfies the [OOBCapablePacketConn] interface (as a [net.UDPConn] does), // ECN and packet info support will be enabled. In this case, ReadMsgUDP and WriteMsgUDP // will be used instead of ReadFrom and WriteTo to read/write packets. -// The tls.Config must define an application protocol (using NextProtos). +// The [tls.Config] must define an application protocol (using tls.Config.NextProtos). // -// This is a convenience function. More advanced use cases should instantiate a Transport, +// This is a convenience function. More advanced use cases should instantiate a [Transport], // which offers configuration options for a more fine-grained control of the connection establishment, // including reusing the underlying UDP socket for multiple QUIC connections. -func Dial(ctx context.Context, c net.PacketConn, addr net.Addr, tlsConf *tls.Config, conf *Config) (Connection, error) { +func Dial(ctx context.Context, c net.PacketConn, addr net.Addr, tlsConf *tls.Config, conf *Config) (*Conn, error) { dl, err := setupTransport(c, tlsConf, false) if err != nil { return nil, err diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/codecov.yml b/plugins/traefik/vendor/github.com/quic-go/quic-go/codecov.yml index 77e47fbe5..58c94e9bd 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/codecov.yml +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/codecov.yml @@ -2,12 +2,13 @@ coverage: round: nearest ignore: - http3/gzip_reader.go + - example/ - interop/ - internal/handshake/cipher_suite.go + - internal/mocks/ - internal/utils/linkedlist/linkedlist.go - internal/testdata - - logging/connection_tracer_multiplexer.go - - logging/tracer_multiplexer.go + - internal/synctest - testutils/ - fuzzing/ - metrics/ diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/config.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/config.go index d42bdc1c5..74c2054e4 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/config.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/config.go @@ -8,7 +8,7 @@ import ( "github.com/quic-go/quic-go/quicvarint" ) -// Clone clones a Config +// Clone clones a Config. func (c *Config) Clone() *Config { copy := *c return © @@ -106,23 +106,24 @@ func populateConfig(config *Config) *Config { } return &Config{ - GetConfigForClient: config.GetConfigForClient, - Versions: versions, - HandshakeIdleTimeout: handshakeIdleTimeout, - MaxIdleTimeout: idleTimeout, - KeepAlivePeriod: config.KeepAlivePeriod, - InitialStreamReceiveWindow: initialStreamReceiveWindow, - MaxStreamReceiveWindow: maxStreamReceiveWindow, - InitialConnectionReceiveWindow: initialConnectionReceiveWindow, - MaxConnectionReceiveWindow: maxConnectionReceiveWindow, - AllowConnectionWindowIncrease: config.AllowConnectionWindowIncrease, - MaxIncomingStreams: maxIncomingStreams, - MaxIncomingUniStreams: maxIncomingUniStreams, - TokenStore: config.TokenStore, - EnableDatagrams: config.EnableDatagrams, - InitialPacketSize: initialPacketSize, - DisablePathMTUDiscovery: config.DisablePathMTUDiscovery, - Allow0RTT: config.Allow0RTT, - Tracer: config.Tracer, + GetConfigForClient: config.GetConfigForClient, + Versions: versions, + HandshakeIdleTimeout: handshakeIdleTimeout, + MaxIdleTimeout: idleTimeout, + KeepAlivePeriod: config.KeepAlivePeriod, + InitialStreamReceiveWindow: initialStreamReceiveWindow, + MaxStreamReceiveWindow: maxStreamReceiveWindow, + InitialConnectionReceiveWindow: initialConnectionReceiveWindow, + MaxConnectionReceiveWindow: maxConnectionReceiveWindow, + AllowConnectionWindowIncrease: config.AllowConnectionWindowIncrease, + MaxIncomingStreams: maxIncomingStreams, + MaxIncomingUniStreams: maxIncomingUniStreams, + TokenStore: config.TokenStore, + EnableDatagrams: config.EnableDatagrams, + InitialPacketSize: initialPacketSize, + DisablePathMTUDiscovery: config.DisablePathMTUDiscovery, + EnableStreamResetPartialDelivery: config.EnableStreamResetPartialDelivery, + Allow0RTT: config.Allow0RTT, + Tracer: config.Tracer, } } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/conn_id_generator.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/conn_id_generator.go index c309c2cd7..133932a6a 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/conn_id_generator.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/conn_id_generator.go @@ -2,47 +2,76 @@ package quic import ( "fmt" + "slices" + "time" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/wire" ) +type connRunnerCallbacks struct { + AddConnectionID func(protocol.ConnectionID) + RemoveConnectionID func(protocol.ConnectionID) + ReplaceWithClosed func([]protocol.ConnectionID, []byte, time.Duration) +} + +// The memory address of the Transport is used as the key. +type connRunners map[connRunner]connRunnerCallbacks + +func (cr connRunners) AddConnectionID(id protocol.ConnectionID) { + for _, c := range cr { + c.AddConnectionID(id) + } +} + +func (cr connRunners) RemoveConnectionID(id protocol.ConnectionID) { + for _, c := range cr { + c.RemoveConnectionID(id) + } +} + +func (cr connRunners) ReplaceWithClosed(ids []protocol.ConnectionID, b []byte, expiry time.Duration) { + for _, c := range cr { + c.ReplaceWithClosed(ids, b, expiry) + } +} + +type connIDToRetire struct { + t monotime.Time + connID protocol.ConnectionID +} + type connIDGenerator struct { - generator ConnectionIDGenerator - highestSeq uint64 + generator ConnectionIDGenerator + highestSeq uint64 + connRunners connRunners activeSrcConnIDs map[uint64]protocol.ConnectionID + connIDsToRetire []connIDToRetire // sorted by t initialClientDestConnID *protocol.ConnectionID // nil for the client - addConnectionID func(protocol.ConnectionID) - statelessResetter *statelessResetter - removeConnectionID func(protocol.ConnectionID) - retireConnectionID func(protocol.ConnectionID) - replaceWithClosed func([]protocol.ConnectionID, []byte) - queueControlFrame func(wire.Frame) + statelessResetter *statelessResetter + + queueControlFrame func(wire.Frame) } func newConnIDGenerator( + runner connRunner, initialConnectionID protocol.ConnectionID, initialClientDestConnID *protocol.ConnectionID, // nil for the client - addConnectionID func(protocol.ConnectionID), statelessResetter *statelessResetter, - removeConnectionID func(protocol.ConnectionID), - retireConnectionID func(protocol.ConnectionID), - replaceWithClosed func([]protocol.ConnectionID, []byte), + callbacks connRunnerCallbacks, queueControlFrame func(wire.Frame), generator ConnectionIDGenerator, ) *connIDGenerator { m := &connIDGenerator{ - generator: generator, - activeSrcConnIDs: make(map[uint64]protocol.ConnectionID), - addConnectionID: addConnectionID, - statelessResetter: statelessResetter, - removeConnectionID: removeConnectionID, - retireConnectionID: retireConnectionID, - replaceWithClosed: replaceWithClosed, - queueControlFrame: queueControlFrame, + generator: generator, + activeSrcConnIDs: make(map[uint64]protocol.ConnectionID), + statelessResetter: statelessResetter, + connRunners: map[connRunner]connRunnerCallbacks{runner: callbacks}, + queueControlFrame: queueControlFrame, } m.activeSrcConnIDs[0] = initialConnectionID m.initialClientDestConnID = initialClientDestConnID @@ -67,7 +96,7 @@ func (m *connIDGenerator) SetMaxActiveConnIDs(limit uint64) error { return nil } -func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.ConnectionID) error { +func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.ConnectionID, expiry monotime.Time) error { if seq > m.highestSeq { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, @@ -85,7 +114,8 @@ func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.Connect ErrorMessage: fmt.Sprintf("retired connection ID %d (%s), which was used as the Destination Connection ID on this packet", seq, connID), } } - m.retireConnectionID(connID) + m.queueConnIDForRetiring(connID, expiry) + delete(m.activeSrcConnIDs, seq) // Don't issue a replacement for the initial connection ID. if seq == 0 { @@ -94,13 +124,23 @@ func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.Connect return m.issueNewConnID() } +func (m *connIDGenerator) queueConnIDForRetiring(connID protocol.ConnectionID, expiry monotime.Time) { + idx := slices.IndexFunc(m.connIDsToRetire, func(c connIDToRetire) bool { + return c.t.After(expiry) + }) + if idx == -1 { + idx = len(m.connIDsToRetire) + } + m.connIDsToRetire = slices.Insert(m.connIDsToRetire, idx, connIDToRetire{t: expiry, connID: connID}) +} + func (m *connIDGenerator) issueNewConnID() error { connID, err := m.generator.GenerateConnectionID() if err != nil { return err } m.activeSrcConnIDs[m.highestSeq+1] = connID - m.addConnectionID(connID) + m.connRunners.AddConnectionID(connID) m.queueControlFrame(&wire.NewConnectionIDFrame{ SequenceNumber: m.highestSeq + 1, ConnectionID: connID, @@ -110,29 +150,63 @@ func (m *connIDGenerator) issueNewConnID() error { return nil } -func (m *connIDGenerator) SetHandshakeComplete() { +func (m *connIDGenerator) SetHandshakeComplete(connIDExpiry monotime.Time) { if m.initialClientDestConnID != nil { - m.retireConnectionID(*m.initialClientDestConnID) + m.queueConnIDForRetiring(*m.initialClientDestConnID, connIDExpiry) m.initialClientDestConnID = nil } } +func (m *connIDGenerator) RemoveRetiredConnIDs(now monotime.Time) { + if len(m.connIDsToRetire) == 0 { + return + } + for _, c := range m.connIDsToRetire { + if c.t.After(now) { + break + } + m.connRunners.RemoveConnectionID(c.connID) + m.connIDsToRetire = m.connIDsToRetire[1:] + } +} + func (m *connIDGenerator) RemoveAll() { if m.initialClientDestConnID != nil { - m.removeConnectionID(*m.initialClientDestConnID) + m.connRunners.RemoveConnectionID(*m.initialClientDestConnID) } for _, connID := range m.activeSrcConnIDs { - m.removeConnectionID(connID) + m.connRunners.RemoveConnectionID(connID) + } + for _, c := range m.connIDsToRetire { + m.connRunners.RemoveConnectionID(c.connID) } } -func (m *connIDGenerator) ReplaceWithClosed(connClose []byte) { - connIDs := make([]protocol.ConnectionID, 0, len(m.activeSrcConnIDs)+1) +func (m *connIDGenerator) ReplaceWithClosed(connClose []byte, expiry time.Duration) { + connIDs := make([]protocol.ConnectionID, 0, len(m.activeSrcConnIDs)+len(m.connIDsToRetire)+1) if m.initialClientDestConnID != nil { connIDs = append(connIDs, *m.initialClientDestConnID) } for _, connID := range m.activeSrcConnIDs { connIDs = append(connIDs, connID) } - m.replaceWithClosed(connIDs, connClose) + for _, c := range m.connIDsToRetire { + connIDs = append(connIDs, c.connID) + } + m.connRunners.ReplaceWithClosed(connIDs, connClose, expiry) +} + +func (m *connIDGenerator) AddConnRunner(runner connRunner, r connRunnerCallbacks) { + // The transport might have already been added earlier. + // This happens if the application migrates back to and old path. + if _, ok := m.connRunners[runner]; ok { + return + } + m.connRunners[runner] = r + if m.initialClientDestConnID != nil { + r.AddConnectionID(*m.initialClientDestConnID) + } + for _, connID := range m.activeSrcConnIDs { + r.AddConnectionID(connID) + } } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/conn_id_manager.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/conn_id_manager.go index a4fbd93cd..5513b645d 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/conn_id_manager.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/conn_id_manager.go @@ -2,11 +2,11 @@ package quic import ( "fmt" + "slices" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" - list "github.com/quic-go/quic-go/internal/utils/linkedlist" "github.com/quic-go/quic-go/internal/wire" ) @@ -17,7 +17,7 @@ type newConnID struct { } type connIDManager struct { - queue list.List[newConnID] + queue []newConnID highestProbingID uint64 pathProbing map[pathID]newConnID // initialized lazily @@ -53,6 +53,7 @@ func newConnIDManager( addStatelessResetToken: addStatelessResetToken, removeStatelessResetToken: removeStatelessResetToken, queueControlFrame: queueControlFrame, + queue: make([]newConnID, 0, protocol.MaxActiveConnectionIDs), } } @@ -64,7 +65,7 @@ func (h *connIDManager) Add(f *wire.NewConnectionIDFrame) error { if err := h.add(f); err != nil { return err } - if h.queue.Len() >= protocol.MaxActiveConnectionIDs { + if len(h.queue) >= protocol.MaxActiveConnectionIDs { return &qerr.TransportError{ErrorCode: qerr.ConnectionIDLimitError} } return nil @@ -92,6 +93,7 @@ func (h *connIDManager) add(f *wire.NewConnectionIDFrame) error { h.queueControlFrame(&wire.RetireConnectionIDFrame{ SequenceNumber: entry.SequenceNumber, }) + h.removeStatelessResetToken(entry.StatelessResetToken) delete(h.pathProbing, id) } } @@ -99,17 +101,15 @@ func (h *connIDManager) add(f *wire.NewConnectionIDFrame) error { // Retire elements in the queue. // Doesn't retire the active connection ID. if f.RetirePriorTo > h.highestRetired { - var next *list.Element[newConnID] - for el := h.queue.Front(); el != nil; el = next { - if el.Value.SequenceNumber >= f.RetirePriorTo { - break + var newQueue []newConnID + for _, entry := range h.queue { + if entry.SequenceNumber >= f.RetirePriorTo { + newQueue = append(newQueue, entry) + } else { + h.queueControlFrame(&wire.RetireConnectionIDFrame{SequenceNumber: entry.SequenceNumber}) } - next = el.Next() - h.queueControlFrame(&wire.RetireConnectionIDFrame{ - SequenceNumber: el.Value.SequenceNumber, - }) - h.queue.Remove(el) } + h.queue = newQueue h.highestRetired = f.RetirePriorTo } @@ -130,36 +130,39 @@ func (h *connIDManager) add(f *wire.NewConnectionIDFrame) error { } func (h *connIDManager) addConnectionID(seq uint64, connID protocol.ConnectionID, resetToken protocol.StatelessResetToken) error { - // insert a new element at the end - if h.queue.Len() == 0 || h.queue.Back().Value.SequenceNumber < seq { - h.queue.PushBack(newConnID{ + // fast path: add to the end of the queue + if len(h.queue) == 0 || h.queue[len(h.queue)-1].SequenceNumber < seq { + h.queue = append(h.queue, newConnID{ SequenceNumber: seq, ConnectionID: connID, StatelessResetToken: resetToken, }) return nil } - // insert a new element somewhere in the middle - for el := h.queue.Front(); el != nil; el = el.Next() { - if el.Value.SequenceNumber == seq { - if el.Value.ConnectionID != connID { + + // slow path: insert in the middle + for i, entry := range h.queue { + if entry.SequenceNumber == seq { + if entry.ConnectionID != connID { return fmt.Errorf("received conflicting connection IDs for sequence number %d", seq) } - if el.Value.StatelessResetToken != resetToken { + if entry.StatelessResetToken != resetToken { return fmt.Errorf("received conflicting stateless reset tokens for sequence number %d", seq) } - break + return nil } - if el.Value.SequenceNumber > seq { - h.queue.InsertBefore(newConnID{ + + // insert at the correct position to maintain sorted order + if entry.SequenceNumber > seq { + h.queue = slices.Insert(h.queue, i, newConnID{ SequenceNumber: seq, ConnectionID: connID, StatelessResetToken: resetToken, - }, el) - break + }) + return nil } } - return nil + return nil // unreachable } func (h *connIDManager) updateConnectionID() { @@ -172,7 +175,8 @@ func (h *connIDManager) updateConnectionID() { h.removeStatelessResetToken(*h.activeStatelessResetToken) } - front := h.queue.Remove(h.queue.Front()) + front := h.queue[0] + h.queue = h.queue[1:] h.activeSequenceNumber = front.SequenceNumber h.activeConnectionID = front.ConnectionID h.activeStatelessResetToken = &front.StatelessResetToken @@ -186,6 +190,11 @@ func (h *connIDManager) Close() { if h.activeStatelessResetToken != nil { h.removeStatelessResetToken(*h.activeStatelessResetToken) } + if h.pathProbing != nil { + for _, entry := range h.pathProbing { + h.removeStatelessResetToken(entry.StatelessResetToken) + } + } } // is called when the server performs a Retry @@ -216,13 +225,13 @@ func (h *connIDManager) shouldUpdateConnID() bool { return false } // initiate the first change as early as possible (after handshake completion) - if h.queue.Len() > 0 && h.activeSequenceNumber == 0 { + if len(h.queue) > 0 && h.activeSequenceNumber == 0 { return true } // For later changes, only change if // 1. The queue of connection IDs is filled more than 50%. // 2. We sent at least PacketsPerConnectionID packets - return 2*h.queue.Len() >= protocol.MaxActiveConnectionIDs && + return 2*len(h.queue) >= protocol.MaxActiveConnectionIDs && h.packetsSinceLastChange >= h.packetsPerConnectionID } @@ -256,12 +265,14 @@ func (h *connIDManager) GetConnIDForPath(id pathID) (protocol.ConnectionID, bool if ok { return entry.ConnectionID, true } - if h.queue.Len() == 0 { + if len(h.queue) == 0 { return protocol.ConnectionID{}, false } - front := h.queue.Remove(h.queue.Front()) + front := h.queue[0] + h.queue = h.queue[1:] h.pathProbing[id] = front h.highestProbingID = front.SequenceNumber + h.addStatelessResetToken(front.StatelessResetToken) return front.ConnectionID, true } @@ -279,9 +290,26 @@ func (h *connIDManager) RetireConnIDForPath(pathID pathID) { h.queueControlFrame(&wire.RetireConnectionIDFrame{ SequenceNumber: entry.SequenceNumber, }) + h.removeStatelessResetToken(entry.StatelessResetToken) delete(h.pathProbing, pathID) } +func (h *connIDManager) IsActiveStatelessResetToken(token protocol.StatelessResetToken) bool { + if h.activeStatelessResetToken != nil { + if *h.activeStatelessResetToken == token { + return true + } + } + if h.pathProbing != nil { + for _, entry := range h.pathProbing { + if entry.StatelessResetToken == token { + return true + } + } + } + return false +} + // Using the connIDManager after it has been closed can have disastrous effects: // If the connection ID is rotated, a new entry would be inserted into the packet handler map, // leading to a memory leak of the connection struct. diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/connection.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/connection.go index 9415584de..faf9f1101 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/connection.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/connection.go @@ -9,6 +9,7 @@ import ( "io" "net" "reflect" + "slices" "sync" "sync/atomic" "time" @@ -16,34 +17,19 @@ import ( "github.com/quic-go/quic-go/internal/ackhandler" "github.com/quic-go/quic-go/internal/flowcontrol" "github.com/quic-go/quic-go/internal/handshake" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/utils/ringbuffer" "github.com/quic-go/quic-go/internal/wire" - "github.com/quic-go/quic-go/logging" + "github.com/quic-go/quic-go/qlog" + "github.com/quic-go/quic-go/qlogwriter" ) type unpacker interface { UnpackLongHeader(hdr *wire.Header, data []byte) (*unpackedPacket, error) - UnpackShortHeader(rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) -} - -type streamManager interface { - GetOrOpenSendStream(protocol.StreamID) (sendStreamI, error) - GetOrOpenReceiveStream(protocol.StreamID) (receiveStreamI, error) - OpenStream() (Stream, error) - OpenUniStream() (SendStream, error) - OpenStreamSync(context.Context) (Stream, error) - OpenUniStreamSync(context.Context) (SendStream, error) - AcceptStream(context.Context) (Stream, error) - AcceptUniStream(context.Context) (ReceiveStream, error) - DeleteStream(protocol.StreamID) error - UpdateLimits(*wire.TransportParameters) - HandleMaxStreamsFrame(*wire.MaxStreamsFrame) - CloseWithError(error) - ResetFor0RTT() - UseResetMaps() + UnpackShortHeader(rcvTime monotime.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) } type cryptoStreamHandler interface { @@ -63,7 +49,7 @@ type receivedPacket struct { buffer *packetBuffer remoteAddr net.Addr - rcvTime time.Time + rcvTime monotime.Time data []byte ecn protocol.ECN @@ -86,9 +72,8 @@ func (p *receivedPacket) Clone() *receivedPacket { type connRunner interface { Add(protocol.ConnectionID, packetHandler) bool - Retire(protocol.ConnectionID) Remove(protocol.ConnectionID) - ReplaceWithClosed([]protocol.ConnectionID, []byte) + ReplaceWithClosed([]protocol.ConnectionID, []byte, time.Duration) AddResetToken(protocol.StatelessResetToken, packetHandler) RemoveResetToken(protocol.StatelessResetToken) } @@ -107,11 +92,35 @@ func (e *errCloseForRecreating) Error() string { return "closing connection in order to recreate it" } +var deadlineSendImmediately = monotime.Time(42 * time.Millisecond) // any value > time.Time{} and before time.Now() is fine + var connTracingID atomic.Uint64 // to be accessed atomically func nextConnTracingID() ConnectionTracingID { return ConnectionTracingID(connTracingID.Add(1)) } -// A Connection is a QUIC connection -type connection struct { +type blockMode uint8 + +const ( + // blockModeNone means that the connection is not blocked. + blockModeNone blockMode = iota + // blockModeCongestionLimited means that the connection is congestion limited. + // In that case, we can still send acknowledgments and PTO probe packets. + blockModeCongestionLimited + // blockModeHardBlocked means that no packet can be sent, under no circumstances. This can happen when: + // * the send queue is full + // * the SentPacketHandler returns SendNone, e.g. when we are tracking the maximum number of packets + // In that case, the timer will be set to the idle timeout. + blockModeHardBlocked +) + +// A Conn is a QUIC connection between two peers. +// Calls to the connection (and to streams) can return the following types of errors: +// - [ApplicationError]: for errors triggered by the application running on top of QUIC +// - [TransportError]: for errors triggered by the QUIC transport (in many cases a misbehaving peer) +// - [IdleTimeoutError]: when the peer goes away unexpectedly (this is a [net.Error] timeout error) +// - [HandshakeTimeoutError]: when the cryptographic handshake takes too long (this is a [net.Error] timeout error) +// - [StatelessResetError]: when we receive a stateless reset +// - [VersionNegotiationError]: returned by the client, when there's no version overlap between the peers +type Conn struct { // Destination connection ID used during the handshake. // Used to check source connection ID on incoming packets. handshakeDestConnID protocol.ConnectionID @@ -129,14 +138,16 @@ type connection struct { sendQueue sender // lazily initialzed: most connections never migrate - pathManager *pathManager - largestRcvdAppData protocol.PacketNumber + pathManager *pathManager + largestRcvdAppData protocol.PacketNumber + pathManagerOutgoing atomic.Pointer[pathManagerOutgoing] - streamsMap streamManager + streamsMap *streamsMap connIDManager *connIDManager connIDGenerator *connIDGenerator - rttStats *utils.RTTStats + rttStats *utils.RTTStats + connStats utils.ConnectionStats cryptoStreamManager *cryptoStreamManager sentPacketHandler ackhandler.SentPacketHandler @@ -154,7 +165,7 @@ type connection struct { currentMTUEstimate atomic.Uint32 - initialStream *cryptoStream + initialStream *initialCryptoStream handshakeStream *cryptoStream oneRTTStream *cryptoStream // only set for the server cryptoStreamHandler cryptoStreamHandler @@ -185,19 +196,21 @@ type connection struct { versionNegotiated bool receivedFirstPacket bool + blocked blockMode + // the minimum of the max_idle_timeout values advertised by both endpoints idleTimeout time.Duration - creationTime time.Time + creationTime monotime.Time // The idle timeout is set based on the max of the time we received the last packet... - lastPacketReceivedTime time.Time + lastPacketReceivedTime monotime.Time // ... and the time we sent a new ack-eliciting packet after receiving a packet. - firstAckElicitingPacketAfterIdleSentTime time.Time + firstAckElicitingPacketAfterIdleSentTime monotime.Time // pacingDeadline is the time when the next packet should be sent - pacingDeadline time.Time + pacingDeadline monotime.Time peerParams *wire.TransportParameters - timer connectionTimer + timer *time.Timer // keepAlivePingSent stores whether a keep alive PING is in flight. // It is reset as soon as we receive a packet from the peer. keepAlivePingSent bool @@ -208,16 +221,28 @@ type connection struct { connStateMutex sync.Mutex connState ConnectionState - logID string - tracer *logging.ConnectionTracer - logger utils.Logger + logID string + qlogTrace qlogwriter.Trace + qlogger qlogwriter.Recorder + logger utils.Logger } -var ( - _ Connection = &connection{} - _ EarlyConnection = &connection{} - _ streamSender = &connection{} -) +var _ streamSender = &Conn{} + +type connTestHooks struct { + run func() error + earlyConnReady func() <-chan struct{} + context func() context.Context + handshakeComplete func() <-chan struct{} + closeWithTransportError func(TransportErrorCode) + destroy func(error) + handlePacket func(receivedPacket) +} + +type wrappedConn struct { + testHooks *connTestHooks + *Conn +} var newConnection = func( ctx context.Context, @@ -235,11 +260,12 @@ var newConnection = func( tlsConf *tls.Config, tokenGenerator *handshake.TokenGenerator, clientAddressValidated bool, - tracer *logging.ConnectionTracer, + rtt time.Duration, + qlogTrace qlogwriter.Trace, logger utils.Logger, v protocol.Version, -) quicConn { - s := &connection{ +) *wrappedConn { + s := &Conn{ ctx: ctx, ctxCancel: ctxCancel, conn: conn, @@ -249,10 +275,13 @@ var newConnection = func( tokenGenerator: tokenGenerator, oneRTTStream: newCryptoStream(), perspective: protocol.PerspectiveServer, - tracer: tracer, + qlogTrace: qlogTrace, logger: logger, version: v, } + if qlogTrace != nil { + s.qlogger = qlogTrace.AddProducer() + } if origDestConnID.Len() > 0 { s.logID = origDestConnID.String() } else { @@ -265,25 +294,29 @@ var newConnection = func( s.queueControlFrame, ) s.connIDGenerator = newConnIDGenerator( + runner, srcConnID, &clientDestConnID, - func(connID protocol.ConnectionID) { runner.Add(connID, s) }, statelessResetter, - runner.Remove, - runner.Retire, - runner.ReplaceWithClosed, + connRunnerCallbacks{ + AddConnectionID: func(connID protocol.ConnectionID) { runner.Add(connID, s) }, + RemoveConnectionID: runner.Remove, + ReplaceWithClosed: runner.ReplaceWithClosed, + }, s.queueControlFrame, connIDGenerator, ) s.preSetup() + s.rttStats.SetInitialRTT(rtt) s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler( 0, protocol.ByteCount(s.config.InitialPacketSize), s.rttStats, + &s.connStats, clientAddressValidated, s.conn.capabilities().ECN, s.perspective, - s.tracer, + s.qlogger, s.logger, ) s.currentMTUEstimate.Store(uint32(estimateMaxPayloadSize(protocol.ByteCount(s.config.InitialPacketSize)))) @@ -299,7 +332,6 @@ var newConnection = func( MaxAckDelay: protocol.MaxAckDelayInclGranularity, AckDelayExponent: protocol.AckDelayExponent, MaxUDPPayloadSize: protocol.MaxPacketBufferSize, - DisableActiveMigration: true, StatelessResetToken: &statelessResetToken, OriginalDestinationConnectionID: origDestConnID, // For interoperability with quic-go versions before May 2023, this value must be set to a value @@ -310,14 +342,15 @@ var newConnection = func( ActiveConnectionIDLimit: protocol.MaxActiveConnectionIDs, InitialSourceConnectionID: srcConnID, RetrySourceConnectionID: retrySrcConnID, + EnableResetStreamAt: conf.EnableStreamResetPartialDelivery, } if s.config.EnableDatagrams { params.MaxDatagramFrameSize = wire.MaxDatagramSize } else { params.MaxDatagramFrameSize = protocol.InvalidByteCount } - if s.tracer != nil && s.tracer.SentTransportParameters != nil { - s.tracer.SentTransportParameters(params) + if s.qlogger != nil { + s.qlogTransportParameters(params, protocol.PerspectiveServer, false) } cs := handshake.NewCryptoSetupServer( clientDestConnID, @@ -327,7 +360,7 @@ var newConnection = func( tlsConf, conf.Allow0RTT, s.rttStats, - tracer, + s.qlogger, logger, s.version, ) @@ -335,7 +368,7 @@ var newConnection = func( s.packer = newPacketPacker(srcConnID, s.connIDManager.Get, s.initialStream, s.handshakeStream, s.sentPacketHandler, s.retransmissionQueue, cs, s.framer, s.receivedPacketHandler, s.datagramQueue, s.perspective) s.unpacker = newPacketUnpacker(cs, s.srcConnIDLen) s.cryptoStreamManager = newCryptoStreamManager(s.initialStream, s.handshakeStream, s.oneRTTStream) - return s + return &wrappedConn{Conn: s} } // declare this as a variable, such that we can it mock it in the tests @@ -352,11 +385,11 @@ var newClientConnection = func( initialPacketNumber protocol.PacketNumber, enable0RTT bool, hasNegotiatedVersion bool, - tracer *logging.ConnectionTracer, + qlogTrace qlogwriter.Trace, logger utils.Logger, v protocol.Version, -) quicConn { - s := &connection{ +) *wrappedConn { + s := &Conn{ conn: conn, config: conf, origDestConnID: destConnID, @@ -365,10 +398,23 @@ var newClientConnection = func( perspective: protocol.PerspectiveClient, logID: destConnID.String(), logger: logger, - tracer: tracer, + qlogTrace: qlogTrace, versionNegotiated: hasNegotiatedVersion, version: v, } + if qlogTrace != nil { + s.qlogger = qlogTrace.AddProducer() + } + if s.qlogger != nil { + var srcAddr, destAddr *net.UDPAddr + if addr, ok := conn.LocalAddr().(*net.UDPAddr); ok { + srcAddr = addr + } + if addr, ok := conn.RemoteAddr().(*net.UDPAddr); ok { + destAddr = addr + } + s.qlogger.RecordEvent(startedConnectionEvent(srcAddr, destAddr)) + } s.connIDManager = newConnIDManager( destConnID, func(token protocol.StatelessResetToken) { runner.AddResetToken(token, s) }, @@ -376,13 +422,15 @@ var newClientConnection = func( s.queueControlFrame, ) s.connIDGenerator = newConnIDGenerator( + runner, srcConnID, nil, - func(connID protocol.ConnectionID) { runner.Add(connID, s) }, statelessResetter, - runner.Remove, - runner.Retire, - runner.ReplaceWithClosed, + connRunnerCallbacks{ + AddConnectionID: func(connID protocol.ConnectionID) { runner.Add(connID, s) }, + RemoveConnectionID: runner.Remove, + ReplaceWithClosed: runner.ReplaceWithClosed, + }, s.queueControlFrame, connIDGenerator, ) @@ -392,10 +440,11 @@ var newClientConnection = func( initialPacketNumber, protocol.ByteCount(s.config.InitialPacketSize), s.rttStats, + &s.connStats, false, // has no effect s.conn.capabilities().ECN, s.perspective, - s.tracer, + s.qlogger, s.logger, ) s.currentMTUEstimate.Store(uint32(estimateMaxPayloadSize(protocol.ByteCount(s.config.InitialPacketSize)))) @@ -411,7 +460,6 @@ var newClientConnection = func( MaxAckDelay: protocol.MaxAckDelayInclGranularity, MaxUDPPayloadSize: protocol.MaxPacketBufferSize, AckDelayExponent: protocol.AckDelayExponent, - DisableActiveMigration: true, // For interoperability with quic-go versions before May 2023, this value must be set to a value // different from protocol.DefaultActiveConnectionIDLimit. // If set to the default value, it will be omitted from the transport parameters, which will make @@ -419,14 +467,15 @@ var newClientConnection = func( // See https://github.com/quic-go/quic-go/pull/3806. ActiveConnectionIDLimit: protocol.MaxActiveConnectionIDs, InitialSourceConnectionID: srcConnID, + EnableResetStreamAt: conf.EnableStreamResetPartialDelivery, } if s.config.EnableDatagrams { params.MaxDatagramFrameSize = wire.MaxDatagramSize } else { params.MaxDatagramFrameSize = protocol.InvalidByteCount } - if s.tracer != nil && s.tracer.SentTransportParameters != nil { - s.tracer.SentTransportParameters(params) + if s.qlogger != nil { + s.qlogTransportParameters(params, protocol.PerspectiveClient, false) } cs := handshake.NewCryptoSetupClient( destConnID, @@ -434,7 +483,7 @@ var newClientConnection = func( tlsConf, enable0RTT, s.rttStats, - tracer, + s.qlogger, logger, s.version, ) @@ -450,120 +499,125 @@ var newClientConnection = func( if s.config.TokenStore != nil { if token := s.config.TokenStore.Pop(s.tokenStoreKey); token != nil { s.packer.SetToken(token.data) + s.rttStats.SetInitialRTT(token.rtt) } } - return s + return &wrappedConn{Conn: s} } -func (s *connection) preSetup() { - s.largestRcvdAppData = protocol.InvalidPacketNumber - s.initialStream = newCryptoStream() - s.handshakeStream = newCryptoStream() - s.sendQueue = newSendQueue(s.conn) - s.retransmissionQueue = newRetransmissionQueue() - s.frameParser = *wire.NewFrameParser(s.config.EnableDatagrams) - s.rttStats = &utils.RTTStats{} - s.connFlowController = flowcontrol.NewConnectionFlowController( - protocol.ByteCount(s.config.InitialConnectionReceiveWindow), - protocol.ByteCount(s.config.MaxConnectionReceiveWindow), +func (c *Conn) preSetup() { + c.largestRcvdAppData = protocol.InvalidPacketNumber + c.initialStream = newInitialCryptoStream(c.perspective == protocol.PerspectiveClient) + c.handshakeStream = newCryptoStream() + c.sendQueue = newSendQueue(c.conn) + c.retransmissionQueue = newRetransmissionQueue() + c.frameParser = *wire.NewFrameParser( + c.config.EnableDatagrams, + c.config.EnableStreamResetPartialDelivery, + false, // ACK_FREQUENCY is not supported yet + ) + c.rttStats = utils.NewRTTStats() + c.connFlowController = flowcontrol.NewConnectionFlowController( + protocol.ByteCount(c.config.InitialConnectionReceiveWindow), + protocol.ByteCount(c.config.MaxConnectionReceiveWindow), func(size protocol.ByteCount) bool { - if s.config.AllowConnectionWindowIncrease == nil { + if c.config.AllowConnectionWindowIncrease == nil { return true } - return s.config.AllowConnectionWindowIncrease(s, uint64(size)) + return c.config.AllowConnectionWindowIncrease(c, uint64(size)) }, - s.rttStats, - s.logger, + c.rttStats, + c.logger, ) - s.earlyConnReadyChan = make(chan struct{}) - s.streamsMap = newStreamsMap( - s.ctx, - s, - s.queueControlFrame, - s.newFlowController, - uint64(s.config.MaxIncomingStreams), - uint64(s.config.MaxIncomingUniStreams), - s.perspective, + c.earlyConnReadyChan = make(chan struct{}) + c.streamsMap = newStreamsMap( + c.ctx, + c, + c.queueControlFrame, + c.newFlowController, + uint64(c.config.MaxIncomingStreams), + uint64(c.config.MaxIncomingUniStreams), + c.perspective, ) - s.framer = newFramer(s.connFlowController) - s.receivedPackets.Init(8) - s.notifyReceivedPacket = make(chan struct{}, 1) - s.closeChan = make(chan struct{}, 1) - s.sendingScheduled = make(chan struct{}, 1) - s.handshakeCompleteChan = make(chan struct{}) + c.framer = newFramer(c.connFlowController) + c.receivedPackets.Init(8) + c.notifyReceivedPacket = make(chan struct{}, 1) + c.closeChan = make(chan struct{}, 1) + c.sendingScheduled = make(chan struct{}, 1) + c.handshakeCompleteChan = make(chan struct{}) - now := time.Now() - s.lastPacketReceivedTime = now - s.creationTime = now + now := monotime.Now() + c.lastPacketReceivedTime = now + c.creationTime = now - s.datagramQueue = newDatagramQueue(s.scheduleSending, s.logger) - s.connState.Version = s.version + c.datagramQueue = newDatagramQueue(c.scheduleSending, c.logger) + c.connState.Version = c.version } // run the connection main loop -func (s *connection) run() (err error) { - defer func() { s.ctxCancel(err) }() +func (c *Conn) run() (err error) { + defer func() { c.ctxCancel(err) }() defer func() { // drain queued packets that will never be processed - s.receivedPacketMx.Lock() - defer s.receivedPacketMx.Unlock() + c.receivedPacketMx.Lock() + defer c.receivedPacketMx.Unlock() - for !s.receivedPackets.Empty() { - p := s.receivedPackets.PopFront() + for !c.receivedPackets.Empty() { + p := c.receivedPackets.PopFront() p.buffer.Decrement() p.buffer.MaybeRelease() } }() - s.timer = *newTimer() + c.timer = time.NewTimer(monotime.Until(c.idleTimeoutStartTime().Add(c.config.HandshakeIdleTimeout))) - if err := s.cryptoStreamHandler.StartHandshake(s.ctx); err != nil { + if err := c.cryptoStreamHandler.StartHandshake(c.ctx); err != nil { return err } - if err := s.handleHandshakeEvents(time.Now()); err != nil { + if err := c.handleHandshakeEvents(monotime.Now()); err != nil { return err } go func() { - if err := s.sendQueue.Run(); err != nil { - s.destroyImpl(err) + if err := c.sendQueue.Run(); err != nil { + c.destroyImpl(err) } }() - if s.perspective == protocol.PerspectiveClient { - s.scheduleSending() // so the ClientHello actually gets sent + if c.perspective == protocol.PerspectiveClient { + c.scheduleSending() // so the ClientHello actually gets sent } var sendQueueAvailable <-chan struct{} runLoop: for { - if s.framer.QueuedTooManyControlFrames() { - s.setCloseError(&closeError{err: &qerr.TransportError{ErrorCode: InternalError}}) + if c.framer.QueuedTooManyControlFrames() { + c.setCloseError(&closeError{err: &qerr.TransportError{ErrorCode: InternalError}}) break runLoop } // Close immediately if requested select { - case <-s.closeChan: + case <-c.closeChan: break runLoop default: } // no need to set a timer if we can send packets immediately - if s.pacingDeadline != deadlineSendImmediately { - s.maybeResetTimer() + if c.pacingDeadline != deadlineSendImmediately { + c.maybeResetTimer() } // 1st: handle undecryptable packets, if any. // This can only occur before completion of the handshake. - if len(s.undecryptablePacketsToProcess) > 0 { + if len(c.undecryptablePacketsToProcess) > 0 { var processedUndecryptablePacket bool - queue := s.undecryptablePacketsToProcess - s.undecryptablePacketsToProcess = nil + queue := c.undecryptablePacketsToProcess + c.undecryptablePacketsToProcess = nil for _, p := range queue { - processed, err := s.handleOnePacket(p) + processed, err := c.handleOnePacket(p) if err != nil { - s.setCloseError(&closeError{err: err}) + c.setCloseError(&closeError{err: err}) break runLoop } if processed { @@ -577,16 +631,16 @@ runLoop: } // 2nd: receive packets. - processed, err := s.handlePackets() // don't check receivedPackets.Len() in the run loop to avoid locking the mutex + processed, err := c.handlePackets() // don't check receivedPackets.Len() in the run loop to avoid locking the mutex if err != nil { - s.setCloseError(&closeError{err: err}) + c.setCloseError(&closeError{err: err}) break runLoop } // We don't need to wait for new events if: // * we processed packets: we probably need to send an ACK, and potentially more data // * the pacer allows us to send more packets immediately - shouldProceedImmediately := sendQueueAvailable == nil && (processed || s.pacingDeadline == deadlineSendImmediately) + shouldProceedImmediately := sendQueueAvailable == nil && (processed || c.pacingDeadline.Equal(deadlineSendImmediately)) if !shouldProceedImmediately { // 3rd: wait for something to happen: // * closing of the connection @@ -595,16 +649,15 @@ runLoop: // * send queue available // * received packets select { - case <-s.closeChan: + case <-c.closeChan: break runLoop - case <-s.timer.Chan(): - s.timer.SetRead() - case <-s.sendingScheduled: + case <-c.timer.C: + case <-c.sendingScheduled: case <-sendQueueAvailable: - case <-s.notifyReceivedPacket: - wasProcessed, err := s.handlePackets() + case <-c.notifyReceivedPacket: + wasProcessed, err := c.handlePackets() if err != nil { - s.setCloseError(&closeError{err: err}) + c.setCloseError(&closeError{err: err}) break runLoop } // if we processed any undecryptable packets, jump to the resetting of the timers directly @@ -616,224 +669,344 @@ runLoop: // Check for loss detection timeout. // This could cause packets to be declared lost, and retransmissions to be enqueued. - now := time.Now() - if timeout := s.sentPacketHandler.GetLossDetectionTimeout(); !timeout.IsZero() && timeout.Before(now) { - if err := s.sentPacketHandler.OnLossDetectionTimeout(now); err != nil { - s.setCloseError(&closeError{err: err}) + now := monotime.Now() + if timeout := c.sentPacketHandler.GetLossDetectionTimeout(); !timeout.IsZero() && !timeout.After(now) { + if err := c.sentPacketHandler.OnLossDetectionTimeout(now); err != nil { + c.setCloseError(&closeError{err: err}) break runLoop } } - if keepAliveTime := s.nextKeepAliveTime(); !keepAliveTime.IsZero() && !now.Before(keepAliveTime) { + if keepAliveTime := c.nextKeepAliveTime(); !keepAliveTime.IsZero() && !now.Before(keepAliveTime) { // send a PING frame since there is no activity in the connection - s.logger.Debugf("Sending a keep-alive PING to keep the connection alive.") - s.framer.QueueControlFrame(&wire.PingFrame{}) - s.keepAlivePingSent = true - } else if !s.handshakeComplete && now.Sub(s.creationTime) >= s.config.handshakeTimeout() { - s.destroyImpl(qerr.ErrHandshakeTimeout) + c.logger.Debugf("Sending a keep-alive PING to keep the connection alive.") + c.framer.QueueControlFrame(&wire.PingFrame{}) + c.keepAlivePingSent = true + } else if !c.handshakeComplete && now.Sub(c.creationTime) >= c.config.handshakeTimeout() { + c.destroyImpl(qerr.ErrHandshakeTimeout) break runLoop } else { - idleTimeoutStartTime := s.idleTimeoutStartTime() - if (!s.handshakeComplete && now.Sub(idleTimeoutStartTime) >= s.config.HandshakeIdleTimeout) || - (s.handshakeComplete && now.After(s.nextIdleTimeoutTime())) { - s.destroyImpl(qerr.ErrIdleTimeout) + idleTimeoutStartTime := c.idleTimeoutStartTime() + if (!c.handshakeComplete && now.Sub(idleTimeoutStartTime) >= c.config.HandshakeIdleTimeout) || + (c.handshakeComplete && !now.Before(c.nextIdleTimeoutTime())) { + c.destroyImpl(qerr.ErrIdleTimeout) break runLoop } } - if s.sendQueue.WouldBlock() { + c.connIDGenerator.RemoveRetiredConnIDs(now) + + if c.perspective == protocol.PerspectiveClient { + pm := c.pathManagerOutgoing.Load() + if pm != nil { + tr, ok := pm.ShouldSwitchPath() + if ok { + c.switchToNewPath(tr, now) + } + } + } + + if c.sendQueue.WouldBlock() { // The send queue is still busy sending out packets. Wait until there's space to enqueue new packets. - sendQueueAvailable = s.sendQueue.Available() + sendQueueAvailable = c.sendQueue.Available() // Cancel the pacing timer, as we can't send any more packets until the send queue is available again. - s.pacingDeadline = time.Time{} + c.pacingDeadline = 0 + c.blocked = blockModeHardBlocked continue } - if s.closeErr.Load() != nil { + if c.closeErr.Load() != nil { break runLoop } - if err := s.triggerSending(now); err != nil { - s.setCloseError(&closeError{err: err}) + c.blocked = blockModeNone // sending might set it back to true if we're congestion limited + if err := c.triggerSending(now); err != nil { + c.setCloseError(&closeError{err: err}) break runLoop } - if s.sendQueue.WouldBlock() { + if c.sendQueue.WouldBlock() { // The send queue is still busy sending out packets. Wait until there's space to enqueue new packets. - sendQueueAvailable = s.sendQueue.Available() + sendQueueAvailable = c.sendQueue.Available() // Cancel the pacing timer, as we can't send any more packets until the send queue is available again. - s.pacingDeadline = time.Time{} + c.pacingDeadline = 0 + c.blocked = blockModeHardBlocked } else { sendQueueAvailable = nil } } - closeErr := s.closeErr.Load() - s.cryptoStreamHandler.Close() - s.sendQueue.Close() // close the send queue before sending the CONNECTION_CLOSE - s.handleCloseError(closeErr) - if s.tracer != nil && s.tracer.Close != nil { + closeErr := c.closeErr.Load() + c.cryptoStreamHandler.Close() + c.sendQueue.Close() // close the send queue before sending the CONNECTION_CLOSE + c.handleCloseError(closeErr) + if c.qlogger != nil { if e := (&errCloseForRecreating{}); !errors.As(closeErr.err, &e) { - s.tracer.Close() + c.qlogger.Close() } } - s.logger.Infof("Connection %s closed.", s.logID) - s.timer.Stop() + c.logger.Infof("Connection %s closed.", c.logID) + c.timer.Stop() return closeErr.err } // blocks until the early connection can be used -func (s *connection) earlyConnReady() <-chan struct{} { - return s.earlyConnReadyChan -} - -func (s *connection) HandshakeComplete() <-chan struct{} { - return s.handshakeCompleteChan -} - -func (s *connection) Context() context.Context { - return s.ctx -} - -func (s *connection) supportsDatagrams() bool { - return s.peerParams.MaxDatagramFrameSize > 0 -} - -func (s *connection) ConnectionState() ConnectionState { - s.connStateMutex.Lock() - defer s.connStateMutex.Unlock() - cs := s.cryptoStreamHandler.ConnectionState() - s.connState.TLS = cs.ConnectionState - s.connState.Used0RTT = cs.Used0RTT - s.connState.GSO = s.conn.capabilities().GSO - return s.connState +func (c *Conn) earlyConnReady() <-chan struct{} { + return c.earlyConnReadyChan +} + +// Context returns a context that is cancelled when the connection is closed. +// The cancellation cause is set to the error that caused the connection to close. +func (c *Conn) Context() context.Context { + return c.ctx +} + +func (c *Conn) supportsDatagrams() bool { + return c.peerParams.MaxDatagramFrameSize > 0 +} + +// ConnectionState returns basic details about the QUIC connection. +func (c *Conn) ConnectionState() ConnectionState { + c.connStateMutex.Lock() + defer c.connStateMutex.Unlock() + cs := c.cryptoStreamHandler.ConnectionState() + c.connState.TLS = cs.ConnectionState + c.connState.Used0RTT = cs.Used0RTT + c.connState.SupportsStreamResetPartialDelivery = c.peerParams.EnableResetStreamAt + c.connState.GSO = c.conn.capabilities().GSO + return c.connState +} + +// ConnectionStats contains statistics about the QUIC connection +type ConnectionStats struct { + // MinRTT is the estimate of the minimum RTT observed on the active network + // path. + MinRTT time.Duration + // LatestRTT is the last RTT sample observed on the active network path. + LatestRTT time.Duration + // SmoothedRTT is an exponentially weighted moving average of an endpoint's + // RTT samples. See https://www.rfc-editor.org/rfc/rfc9002#section-5.3 + SmoothedRTT time.Duration + // MeanDeviation estimates the variation in the RTT samples using a mean + // variation. See https://www.rfc-editor.org/rfc/rfc9002#section-5.3 + MeanDeviation time.Duration + + // BytesSent is the number of bytes sent on the underlying connection, + // including retransmissions. Does not include UDP or any other outer + // framing. + BytesSent uint64 + // PacketsSent is the number of packets sent on the underlying connection, + // including those that are determined to have been lost. + PacketsSent uint64 + // BytesReceived is the number of total bytes received on the underlying + // connection, including duplicate data for streams. Does not include UDP or + // any other outer framing. + BytesReceived uint64 + // PacketsReceived is the number of total packets received on the underlying + // connection, including packets that were not processable. + PacketsReceived uint64 + // BytesLost is the number of bytes lost on the underlying connection (does + // not monotonically increase, because packets that are declared lost can + // subsequently be received). Does not include UDP or any other outer + // framing. + BytesLost uint64 + // PacketsLost is the number of packets lost on the underlying connection + // (does not monotonically increase, because packets that are declared lost + // can subsequently be received). + PacketsLost uint64 +} + +func (c *Conn) ConnectionStats() ConnectionStats { + return ConnectionStats{ + MinRTT: c.rttStats.MinRTT(), + LatestRTT: c.rttStats.LatestRTT(), + SmoothedRTT: c.rttStats.SmoothedRTT(), + MeanDeviation: c.rttStats.MeanDeviation(), + + BytesSent: c.connStats.BytesSent.Load(), + PacketsSent: c.connStats.PacketsSent.Load(), + BytesReceived: c.connStats.BytesReceived.Load(), + PacketsReceived: c.connStats.PacketsReceived.Load(), + BytesLost: c.connStats.BytesLost.Load(), + PacketsLost: c.connStats.PacketsLost.Load(), + } } // Time when the connection should time out -func (s *connection) nextIdleTimeoutTime() time.Time { - idleTimeout := max(s.idleTimeout, s.rttStats.PTO(true)*3) - return s.idleTimeoutStartTime().Add(idleTimeout) +func (c *Conn) nextIdleTimeoutTime() monotime.Time { + idleTimeout := max(c.idleTimeout, c.rttStats.PTO(true)*3) + return c.idleTimeoutStartTime().Add(idleTimeout) } // Time when the next keep-alive packet should be sent. // It returns a zero time if no keep-alive should be sent. -func (s *connection) nextKeepAliveTime() time.Time { - if s.config.KeepAlivePeriod == 0 || s.keepAlivePingSent { - return time.Time{} +func (c *Conn) nextKeepAliveTime() monotime.Time { + if c.config.KeepAlivePeriod == 0 || c.keepAlivePingSent { + return 0 } - keepAliveInterval := max(s.keepAliveInterval, s.rttStats.PTO(true)*3/2) - return s.lastPacketReceivedTime.Add(keepAliveInterval) + keepAliveInterval := max(c.keepAliveInterval, c.rttStats.PTO(true)*3/2) + return c.lastPacketReceivedTime.Add(keepAliveInterval) } -func (s *connection) maybeResetTimer() { - var deadline time.Time - if !s.handshakeComplete { - deadline = s.creationTime.Add(s.config.handshakeTimeout()) - if t := s.idleTimeoutStartTime().Add(s.config.HandshakeIdleTimeout); t.Before(deadline) { +func (c *Conn) maybeResetTimer() { + var deadline monotime.Time + if !c.handshakeComplete { + deadline = c.creationTime.Add(c.config.handshakeTimeout()) + if t := c.idleTimeoutStartTime().Add(c.config.HandshakeIdleTimeout); t.Before(deadline) { deadline = t } } else { - if keepAliveTime := s.nextKeepAliveTime(); !keepAliveTime.IsZero() { - deadline = keepAliveTime + // A keep-alive packet is ack-eliciting, so it can only be sent if the connection is + // neither congestion limited nor hard-blocked. + if c.blocked != blockModeNone { + deadline = c.nextIdleTimeoutTime() } else { - deadline = s.nextIdleTimeoutTime() + if keepAliveTime := c.nextKeepAliveTime(); !keepAliveTime.IsZero() { + deadline = keepAliveTime + } else { + deadline = c.nextIdleTimeoutTime() + } } } + // If the connection is hard-blocked, we can't even send acknowledgments, + // nor can we send PTO probe packets. + if c.blocked == blockModeHardBlocked { + c.timer.Reset(monotime.Until(deadline)) + return + } - s.timer.SetTimer( - deadline, - s.receivedPacketHandler.GetAlarmTimeout(), - s.sentPacketHandler.GetLossDetectionTimeout(), - s.pacingDeadline, - ) + if t := c.receivedPacketHandler.GetAlarmTimeout(); !t.IsZero() && t.Before(deadline) { + deadline = t + } + if t := c.sentPacketHandler.GetLossDetectionTimeout(); !t.IsZero() && t.Before(deadline) { + deadline = t + } + if c.blocked == blockModeCongestionLimited { + c.timer.Reset(monotime.Until(deadline)) + return + } + + if !c.pacingDeadline.IsZero() && c.pacingDeadline.Before(deadline) { + deadline = c.pacingDeadline + } + c.timer.Reset(monotime.Until(deadline)) } -func (s *connection) idleTimeoutStartTime() time.Time { - startTime := s.lastPacketReceivedTime - if t := s.firstAckElicitingPacketAfterIdleSentTime; t.After(startTime) { +func (c *Conn) idleTimeoutStartTime() monotime.Time { + startTime := c.lastPacketReceivedTime + if t := c.firstAckElicitingPacketAfterIdleSentTime; !t.IsZero() && t.After(startTime) { startTime = t } return startTime } -func (s *connection) handleHandshakeComplete(now time.Time) error { - defer close(s.handshakeCompleteChan) +func (c *Conn) switchToNewPath(tr *Transport, now monotime.Time) { + initialPacketSize := protocol.ByteCount(c.config.InitialPacketSize) + c.sentPacketHandler.MigratedPath(now, initialPacketSize) + maxPacketSize := protocol.ByteCount(protocol.MaxPacketBufferSize) + if c.peerParams.MaxUDPPayloadSize > 0 && c.peerParams.MaxUDPPayloadSize < maxPacketSize { + maxPacketSize = c.peerParams.MaxUDPPayloadSize + } + c.mtuDiscoverer.Reset(now, initialPacketSize, maxPacketSize) + c.conn = newSendConn(tr.conn, c.conn.RemoteAddr(), packetInfo{}, utils.DefaultLogger) // TODO: find a better way + c.sendQueue.Close() + c.sendQueue = newSendQueue(c.conn) + go func() { + if err := c.sendQueue.Run(); err != nil { + c.destroyImpl(err) + } + }() +} + +func (c *Conn) handleHandshakeComplete(now monotime.Time) error { + defer close(c.handshakeCompleteChan) // Once the handshake completes, we have derived 1-RTT keys. // There's no point in queueing undecryptable packets for later decryption anymore. - s.undecryptablePackets = nil + c.undecryptablePackets = nil - s.connIDManager.SetHandshakeComplete() - s.connIDGenerator.SetHandshakeComplete() + c.connIDManager.SetHandshakeComplete() + c.connIDGenerator.SetHandshakeComplete(now.Add(3 * c.rttStats.PTO(false))) - if s.tracer != nil && s.tracer.ChoseALPN != nil { - s.tracer.ChoseALPN(s.cryptoStreamHandler.ConnectionState().NegotiatedProtocol) + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.ALPNInformation{ + ChosenALPN: c.cryptoStreamHandler.ConnectionState().NegotiatedProtocol, + }) } // The server applies transport parameters right away, but the client side has to wait for handshake completion. // During a 0-RTT connection, the client is only allowed to use the new transport parameters for 1-RTT packets. - if s.perspective == protocol.PerspectiveClient { - s.applyTransportParameters() + if c.perspective == protocol.PerspectiveClient { + c.applyTransportParameters() return nil } // All these only apply to the server side. - if err := s.handleHandshakeConfirmed(now); err != nil { + if err := c.handleHandshakeConfirmed(now); err != nil { return err } - ticket, err := s.cryptoStreamHandler.GetSessionTicket() + ticket, err := c.cryptoStreamHandler.GetSessionTicket() if err != nil { return err } if ticket != nil { // may be nil if session tickets are disabled via tls.Config.SessionTicketsDisabled - s.oneRTTStream.Write(ticket) - for s.oneRTTStream.HasData() { - s.queueControlFrame(s.oneRTTStream.PopCryptoFrame(protocol.MaxPostHandshakeCryptoFrameSize)) + c.oneRTTStream.Write(ticket) + for c.oneRTTStream.HasData() { + if cf := c.oneRTTStream.PopCryptoFrame(protocol.MaxPostHandshakeCryptoFrameSize); cf != nil { + c.queueControlFrame(cf) + } } } - token, err := s.tokenGenerator.NewToken(s.conn.RemoteAddr()) + token, err := c.tokenGenerator.NewToken(c.conn.RemoteAddr(), c.rttStats.SmoothedRTT()) if err != nil { return err } - s.queueControlFrame(&wire.NewTokenFrame{Token: token}) - s.queueControlFrame(&wire.HandshakeDoneFrame{}) + c.queueControlFrame(&wire.NewTokenFrame{Token: token}) + c.queueControlFrame(&wire.HandshakeDoneFrame{}) return nil } -func (s *connection) handleHandshakeConfirmed(now time.Time) error { - if err := s.dropEncryptionLevel(protocol.EncryptionHandshake, now); err != nil { +func (c *Conn) handleHandshakeConfirmed(now monotime.Time) error { + // Drop initial keys. + // On the client side, this should have happened when sending the first Handshake packet, + // but this is not guaranteed if the server misbehaves. + // See CVE-2025-59530 for more details. + if err := c.dropEncryptionLevel(protocol.EncryptionInitial, now); err != nil { + return err + } + if err := c.dropEncryptionLevel(protocol.EncryptionHandshake, now); err != nil { return err } - s.handshakeConfirmed = true - s.cryptoStreamHandler.SetHandshakeConfirmed() + c.handshakeConfirmed = true + c.cryptoStreamHandler.SetHandshakeConfirmed() - if !s.config.DisablePathMTUDiscovery && s.conn.capabilities().DF { - s.mtuDiscoverer.Start(now) + if !c.config.DisablePathMTUDiscovery && c.conn.capabilities().DF { + c.mtuDiscoverer.Start(now) } return nil } -func (s *connection) handlePackets() (wasProcessed bool, _ error) { +func (c *Conn) handlePackets() (wasProcessed bool, _ error) { // Now process all packets in the receivedPackets channel. // Limit the number of packets to the length of the receivedPackets channel, // so we eventually get a chance to send out an ACK when receiving a lot of packets. - s.receivedPacketMx.Lock() - numPackets := s.receivedPackets.Len() + c.receivedPacketMx.Lock() + numPackets := c.receivedPackets.Len() if numPackets == 0 { - s.receivedPacketMx.Unlock() + c.receivedPacketMx.Unlock() return false, nil } var hasMorePackets bool for i := 0; i < numPackets; i++ { if i > 0 { - s.receivedPacketMx.Lock() + c.receivedPacketMx.Lock() } - p := s.receivedPackets.PopFront() - hasMorePackets = !s.receivedPackets.Empty() - s.receivedPacketMx.Unlock() + p := c.receivedPackets.PopFront() + hasMorePackets = !c.receivedPackets.Empty() + c.receivedPacketMx.Unlock() - processed, err := s.handleOnePacket(p) + processed, err := c.handleOnePacket(p) if err != nil { return false, err } @@ -844,24 +1017,24 @@ func (s *connection) handlePackets() (wasProcessed bool, _ error) { break } // only process a single packet at a time before handshake completion - if !s.handshakeComplete { + if !c.handshakeComplete { break } } if hasMorePackets { select { - case s.notifyReceivedPacket <- struct{}{}: + case c.notifyReceivedPacket <- struct{}{}: default: } } return wasProcessed, nil } -func (s *connection) handleOnePacket(rp receivedPacket) (wasProcessed bool, _ error) { - s.sentPacketHandler.ReceivedBytes(rp.Size(), rp.rcvTime) +func (c *Conn) handleOnePacket(rp receivedPacket) (wasProcessed bool, _ error) { + c.sentPacketHandler.ReceivedBytes(rp.Size(), rp.rcvTime) if wire.IsVersionNegotiationPacket(rp.data) { - s.handleVersionNegotiationPacket(rp) + c.handleVersionNegotiationPacket(rp) return false, nil } @@ -874,19 +1047,26 @@ func (s *connection) handleOnePacket(rp receivedPacket) (wasProcessed bool, _ er p = *(p.Clone()) p.data = data - destConnID, err := wire.ParseConnectionID(p.data, s.srcConnIDLen) + destConnID, err := wire.ParseConnectionID(p.data, c.srcConnIDLen) if err != nil { - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropHeaderParseError) + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Raw: qlog.RawInfo{Length: len(data)}, + Trigger: qlog.PacketDropHeaderParseError, + }) } - s.logger.Debugf("error parsing packet, couldn't parse connection ID: %s", err) + c.logger.Debugf("error parsing packet, couldn't parse connection ID: %s", err) break } if destConnID != lastConnID { - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropUnknownConnectionID) + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{DestConnectionID: destConnID}, + Raw: qlog.RawInfo{Length: len(data)}, + Trigger: qlog.PacketDropUnknownConnectionID, + }) } - s.logger.Debugf("coalesced packet has different destination connection ID: %s, expected %s", destConnID, lastConnID) + c.logger.Debugf("coalesced packet has different destination connection ID: %s, expected %s", destConnID, lastConnID) break } } @@ -894,23 +1074,33 @@ func (s *connection) handleOnePacket(rp receivedPacket) (wasProcessed bool, _ er if wire.IsLongHeaderPacket(p.data[0]) { hdr, packetData, rest, err := wire.ParsePacket(p.data) if err != nil { - if s.tracer != nil && s.tracer.DroppedPacket != nil { - dropReason := logging.PacketDropHeaderParseError + if c.qlogger != nil { if err == wire.ErrUnsupportedVersion { - dropReason = logging.PacketDropUnsupportedVersion + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{Version: hdr.Version}, + Raw: qlog.RawInfo{Length: len(data)}, + Trigger: qlog.PacketDropUnsupportedVersion, + }) + } else { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Raw: qlog.RawInfo{Length: len(data)}, + Trigger: qlog.PacketDropHeaderParseError, + }) } - s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), dropReason) } - s.logger.Debugf("error parsing packet: %s", err) + c.logger.Debugf("error parsing packet: %s", err) break } lastConnID = hdr.DestConnectionID - if hdr.Version != s.version { - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedVersion) + if hdr.Version != c.version { + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Raw: qlog.RawInfo{Length: len(data)}, + Trigger: qlog.PacketDropUnexpectedVersion, + }) } - s.logger.Debugf("Dropping packet with version %x. Expected %x.", hdr.Version, s.version) + c.logger.Debugf("Dropping packet with version %x. Expected %x.", hdr.Version, c.version) break } @@ -920,13 +1110,13 @@ func (s *connection) handleOnePacket(rp receivedPacket) (wasProcessed bool, _ er counter++ // only log if this actually a coalesced packet - if s.logger.Debug() && (counter > 1 || len(rest) > 0) { - s.logger.Debugf("Parsed a coalesced packet. Part %d: %d bytes. Remaining: %d bytes.", counter, len(packetData), len(rest)) + if c.logger.Debug() && (counter > 1 || len(rest) > 0) { + c.logger.Debugf("Parsed a coalesced packet. Part %d: %d bytes. Remaining: %d bytes.", counter, len(packetData), len(rest)) } p.data = packetData - processed, err := s.handleLongHeaderPacket(p, hdr) + processed, err := c.handleLongHeaderPacket(p, hdr) if err != nil { return false, err } @@ -938,7 +1128,7 @@ func (s *connection) handleOnePacket(rp receivedPacket) (wasProcessed bool, _ er if counter > 0 { p.buffer.Split() } - processed, err := s.handleShortHeaderPacket(p) + processed, err := c.handleShortHeaderPacket(p, counter > 0) if err != nil { return false, err } @@ -950,10 +1140,11 @@ func (s *connection) handleOnePacket(rp receivedPacket) (wasProcessed bool, _ er } p.buffer.MaybeRelease() + c.blocked = blockModeNone return wasProcessed, nil } -func (s *connection) handleShortHeaderPacket(p receivedPacket) (wasProcessed bool, _ error) { +func (c *Conn) handleShortHeaderPacket(p receivedPacket, isCoalesced bool) (wasProcessed bool, _ error) { var wasQueued bool defer func() { @@ -963,99 +1154,128 @@ func (s *connection) handleShortHeaderPacket(p receivedPacket) (wasProcessed boo } }() - destConnID, err := wire.ParseConnectionID(p.data, s.srcConnIDLen) + destConnID, err := wire.ParseConnectionID(p.data, c.srcConnIDLen) if err != nil { - s.tracer.DroppedPacket(logging.PacketType1RTT, protocol.InvalidPacketNumber, protocol.ByteCount(len(p.data)), logging.PacketDropHeaderParseError) + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{ + PacketType: qlog.PacketType1RTT, + PacketNumber: protocol.InvalidPacketNumber, + }, + Raw: qlog.RawInfo{Length: len(p.data)}, + Trigger: qlog.PacketDropHeaderParseError, + }) return false, nil } - pn, pnLen, keyPhase, data, err := s.unpacker.UnpackShortHeader(p.rcvTime, p.data) + pn, pnLen, keyPhase, data, err := c.unpacker.UnpackShortHeader(p.rcvTime, p.data) if err != nil { - wasQueued, err = s.handleUnpackError(err, p, logging.PacketType1RTT) + // Stateless reset packets (see RFC 9000, section 10.3): + // * fill the entire UDP datagram (i.e. they cannot be part of a coalesced packet) + // * are short header packets (first bit is 0) + // * have the QUIC bit set (second bit is 1) + // * are at least 21 bytes long + if !isCoalesced && len(p.data) >= protocol.MinReceivedStatelessResetSize && p.data[0]&0b11000000 == 0b01000000 { + token := protocol.StatelessResetToken(p.data[len(p.data)-16:]) + if c.connIDManager.IsActiveStatelessResetToken(token) { + return false, &StatelessResetError{} + } + } + wasQueued, err = c.handleUnpackError(err, p, qlog.PacketType1RTT) return false, err } - s.largestRcvdAppData = max(s.largestRcvdAppData, pn) + c.largestRcvdAppData = max(c.largestRcvdAppData, pn) - if s.logger.Debug() { - s.logger.Debugf("<- Reading packet %d (%d bytes) for connection %s, 1-RTT", pn, p.Size(), destConnID) - wire.LogShortHeader(s.logger, destConnID, pn, pnLen, keyPhase) + if c.logger.Debug() { + c.logger.Debugf("<- Reading packet %d (%d bytes) for connection %s, 1-RTT", pn, p.Size(), destConnID) + wire.LogShortHeader(c.logger, destConnID, pn, pnLen, keyPhase) } - if s.receivedPacketHandler.IsPotentiallyDuplicate(pn, protocol.Encryption1RTT) { - s.logger.Debugf("Dropping (potentially) duplicate packet.") - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(logging.PacketType1RTT, pn, p.Size(), logging.PacketDropDuplicate) + if c.receivedPacketHandler.IsPotentiallyDuplicate(pn, protocol.Encryption1RTT) { + c.logger.Debugf("Dropping (potentially) duplicate packet.") + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{ + PacketType: qlog.PacketType1RTT, + PacketNumber: pn, + }, + Raw: qlog.RawInfo{Length: int(p.Size())}, + Trigger: qlog.PacketDropDuplicate, + }) } return false, nil } - var log func([]logging.Frame) - if s.tracer != nil && s.tracer.ReceivedShortHeaderPacket != nil { - log = func(frames []logging.Frame) { - s.tracer.ReceivedShortHeaderPacket( - &logging.ShortHeader{ + var log func([]qlog.Frame) + if c.qlogger != nil { + log = func(frames []qlog.Frame) { + c.qlogger.RecordEvent(qlog.PacketReceived{ + Header: qlog.PacketHeader{ + PacketType: qlog.PacketType1RTT, DestConnectionID: destConnID, PacketNumber: pn, - PacketNumberLen: pnLen, - KeyPhase: keyPhase, + KeyPhaseBit: keyPhase, }, - p.Size(), - p.ecn, - frames, - ) + Raw: qlog.RawInfo{ + Length: int(p.Size()), + PayloadLength: int(p.Size() - wire.ShortHeaderLen(destConnID, pnLen)), + }, + Frames: frames, + ECN: toQlogECN(p.ecn), + }) } } - isNonProbing, err := s.handleUnpackedShortHeaderPacket(destConnID, pn, data, p.ecn, p.rcvTime, log) + isNonProbing, pathChallenge, err := c.handleUnpackedShortHeaderPacket(destConnID, pn, data, p.ecn, p.rcvTime, log) if err != nil { return false, err } // In RFC 9000, only the client can migrate between paths. - if s.perspective == protocol.PerspectiveClient { + if c.perspective == protocol.PerspectiveClient { + return true, nil + } + if addrsEqual(p.remoteAddr, c.RemoteAddr()) { return true, nil } var shouldSwitchPath bool - if pn == s.largestRcvdAppData && !addrsEqual(p.remoteAddr, s.RemoteAddr()) { - if s.pathManager == nil { - s.pathManager = newPathManager( - s.connIDManager.GetConnIDForPath, - s.connIDManager.RetireConnIDForPath, - s.logger, - ) - } - var destConnID protocol.ConnectionID - var pathChallenge ackhandler.Frame - destConnID, pathChallenge, shouldSwitchPath = s.pathManager.HandlePacket(p, isNonProbing) - if pathChallenge.Frame != nil { - probe, buf, err := s.packer.PackPathProbePacket(destConnID, pathChallenge, s.version) - if err != nil { - return false, err - } - s.logger.Debugf("sending path probe packet to %s", p.remoteAddr) - s.logShortHeaderPacket(probe.DestConnID, probe.Ack, probe.Frames, probe.StreamFrames, probe.PacketNumber, probe.PacketNumberLen, probe.KeyPhase, protocol.ECNNon, buf.Len(), false) - s.registerPackedShortHeaderPacket(probe, protocol.ECNNon, p.rcvTime) - s.sendQueue.SendProbe(buf, p.remoteAddr) - } + if c.pathManager == nil { + c.pathManager = newPathManager( + c.connIDManager.GetConnIDForPath, + c.connIDManager.RetireConnIDForPath, + c.logger, + ) } - - if shouldSwitchPath { - s.pathManager.SwitchToPath(p.remoteAddr) - s.sentPacketHandler.MigratedPath(p.rcvTime, protocol.ByteCount(s.config.InitialPacketSize)) - maxPacketSize := protocol.ByteCount(protocol.MaxPacketBufferSize) - if s.peerParams.MaxUDPPayloadSize > 0 && s.peerParams.MaxUDPPayloadSize < maxPacketSize { - maxPacketSize = s.peerParams.MaxUDPPayloadSize + destConnID, frames, shouldSwitchPath := c.pathManager.HandlePacket(p.remoteAddr, p.rcvTime, pathChallenge, isNonProbing) + if len(frames) > 0 { + probe, buf, err := c.packer.PackPathProbePacket(destConnID, frames, c.version) + if err != nil { + return true, err } - s.mtuDiscoverer.Reset( - p.rcvTime, - protocol.ByteCount(s.config.InitialPacketSize), - maxPacketSize, - ) - s.conn.ChangeRemoteAddr(p.remoteAddr, p.info) + c.logger.Debugf("sending path probe packet to %s", p.remoteAddr) + c.logShortHeaderPacket(probe.DestConnID, probe.Ack, probe.Frames, probe.StreamFrames, probe.PacketNumber, probe.PacketNumberLen, probe.KeyPhase, protocol.ECNNon, buf.Len(), false) + c.registerPackedShortHeaderPacket(probe, protocol.ECNNon, p.rcvTime) + c.sendQueue.SendProbe(buf, p.remoteAddr) + } + // We only switch paths in response to the highest-numbered non-probing packet, + // see section 9.3 of RFC 9000. + if !shouldSwitchPath || pn != c.largestRcvdAppData { + return true, nil + } + c.pathManager.SwitchToPath(p.remoteAddr) + c.sentPacketHandler.MigratedPath(p.rcvTime, protocol.ByteCount(c.config.InitialPacketSize)) + maxPacketSize := protocol.ByteCount(protocol.MaxPacketBufferSize) + if c.peerParams.MaxUDPPayloadSize > 0 && c.peerParams.MaxUDPPayloadSize < maxPacketSize { + maxPacketSize = c.peerParams.MaxUDPPayloadSize } + c.mtuDiscoverer.Reset( + p.rcvTime, + protocol.ByteCount(c.config.InitialPacketSize), + maxPacketSize, + ) + c.conn.ChangeRemoteAddr(p.remoteAddr, p.info) return true, nil } -func (s *connection) handleLongHeaderPacket(p receivedPacket, hdr *wire.Header) (wasProcessed bool, _ error) { +func (c *Conn) handleLongHeaderPacket(p receivedPacket, hdr *wire.Header) (wasProcessed bool, _ error) { var wasQueued bool defer func() { @@ -1066,63 +1286,96 @@ func (s *connection) handleLongHeaderPacket(p receivedPacket, hdr *wire.Header) }() if hdr.Type == protocol.PacketTypeRetry { - return s.handleRetryPacket(hdr, p.data, p.rcvTime), nil + return c.handleRetryPacket(hdr, p.data, p.rcvTime), nil } // The server can change the source connection ID with the first Handshake packet. // After this, all packets with a different source connection have to be ignored. - if s.receivedFirstPacket && hdr.Type == protocol.PacketTypeInitial && hdr.SrcConnectionID != s.handshakeDestConnID { - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(logging.PacketTypeInitial, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropUnknownConnectionID) + if c.receivedFirstPacket && hdr.Type == protocol.PacketTypeInitial && hdr.SrcConnectionID != c.handshakeDestConnID { + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{ + PacketType: qlog.PacketTypeInitial, + PacketNumber: protocol.InvalidPacketNumber, + }, + Raw: qlog.RawInfo{Length: int(p.Size())}, + Trigger: qlog.PacketDropUnknownConnectionID, + }) } - s.logger.Debugf("Dropping Initial packet (%d bytes) with unexpected source connection ID: %s (expected %s)", p.Size(), hdr.SrcConnectionID, s.handshakeDestConnID) + c.logger.Debugf("Dropping Initial packet (%d bytes) with unexpected source connection ID: %s (expected %s)", p.Size(), hdr.SrcConnectionID, c.handshakeDestConnID) return false, nil } // drop 0-RTT packets, if we are a client - if s.perspective == protocol.PerspectiveClient && hdr.Type == protocol.PacketType0RTT { - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(logging.PacketType0RTT, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropUnexpectedPacket) + if c.perspective == protocol.PerspectiveClient && hdr.Type == protocol.PacketType0RTT { + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{ + PacketType: qlog.PacketType0RTT, + PacketNumber: protocol.InvalidPacketNumber, + }, + Raw: qlog.RawInfo{Length: int(p.Size())}, + Trigger: qlog.PacketDropUnexpectedPacket, + }) } return false, nil } - packet, err := s.unpacker.UnpackLongHeader(hdr, p.data) + packet, err := c.unpacker.UnpackLongHeader(hdr, p.data) if err != nil { - wasQueued, err = s.handleUnpackError(err, p, logging.PacketTypeFromHeader(hdr)) + wasQueued, err = c.handleUnpackError(err, p, toQlogPacketType(hdr.Type)) return false, err } - if s.logger.Debug() { - s.logger.Debugf("<- Reading packet %d (%d bytes) for connection %s, %s", packet.hdr.PacketNumber, p.Size(), hdr.DestConnectionID, packet.encryptionLevel) - packet.hdr.Log(s.logger) + if c.logger.Debug() { + c.logger.Debugf("<- Reading packet %d (%d bytes) for connection %s, %s", packet.hdr.PacketNumber, p.Size(), hdr.DestConnectionID, packet.encryptionLevel) + packet.hdr.Log(c.logger) } - if pn := packet.hdr.PacketNumber; s.receivedPacketHandler.IsPotentiallyDuplicate(pn, packet.encryptionLevel) { - s.logger.Debugf("Dropping (potentially) duplicate packet.") - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), pn, p.Size(), logging.PacketDropDuplicate) + if pn := packet.hdr.PacketNumber; c.receivedPacketHandler.IsPotentiallyDuplicate(pn, packet.encryptionLevel) { + c.logger.Debugf("Dropping (potentially) duplicate packet.") + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{ + PacketType: toQlogPacketType(packet.hdr.Type), + DestConnectionID: hdr.DestConnectionID, + SrcConnectionID: hdr.SrcConnectionID, + PacketNumber: pn, + Version: packet.hdr.Version, + }, + Raw: qlog.RawInfo{Length: int(p.Size()), PayloadLength: int(packet.hdr.Length)}, + Trigger: qlog.PacketDropDuplicate, + }) } return false, nil } - if err := s.handleUnpackedLongHeaderPacket(packet, p.ecn, p.rcvTime, p.Size()); err != nil { + if err := c.handleUnpackedLongHeaderPacket(packet, p.ecn, p.rcvTime, p.Size()); err != nil { return false, err } return true, nil } -func (s *connection) handleUnpackError(err error, p receivedPacket, pt logging.PacketType) (wasQueued bool, _ error) { +func (c *Conn) handleUnpackError(err error, p receivedPacket, pt qlog.PacketType) (wasQueued bool, _ error) { switch err { case handshake.ErrKeysDropped: - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(pt, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropKeyUnavailable) + if c.qlogger != nil { + connID, _ := wire.ParseConnectionID(p.data, c.srcConnIDLen) + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{ + PacketType: pt, + DestConnectionID: connID, + PacketNumber: protocol.InvalidPacketNumber, + }, + Raw: qlog.RawInfo{Length: int(p.Size())}, + Trigger: qlog.PacketDropKeyUnavailable, + }) } - s.logger.Debugf("Dropping %s packet (%d bytes) because we already dropped the keys.", pt, p.Size()) + c.logger.Debugf("Dropping %s packet (%d bytes) because we already dropped the keys.", pt, p.Size()) return false, nil case handshake.ErrKeysNotYetAvailable: // Sealer for this encryption level not yet available. // Try again later. - s.tryQueueingUndecryptablePacket(p, pt) + c.tryQueueingUndecryptablePacket(p, pt) return true, nil case wire.ErrInvalidReservedBits: return false, &qerr.TransportError{ @@ -1131,19 +1384,37 @@ func (s *connection) handleUnpackError(err error, p receivedPacket, pt logging.P } case handshake.ErrDecryptionFailed: // This might be a packet injected by an attacker. Drop it. - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(pt, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropPayloadDecryptError) + if c.qlogger != nil { + connID, _ := wire.ParseConnectionID(p.data, c.srcConnIDLen) + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{ + PacketType: pt, + DestConnectionID: connID, + PacketNumber: protocol.InvalidPacketNumber, + }, + Raw: qlog.RawInfo{Length: int(p.Size())}, + Trigger: qlog.PacketDropPayloadDecryptError, + }) } - s.logger.Debugf("Dropping %s packet (%d bytes) that could not be unpacked. Error: %s", pt, p.Size(), err) + c.logger.Debugf("Dropping %s packet (%d bytes) that could not be unpacked. Error: %s", pt, p.Size(), err) return false, nil default: var headerErr *headerParseError if errors.As(err, &headerErr) { // This might be a packet injected by an attacker. Drop it. - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(pt, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropHeaderParseError) + if c.qlogger != nil { + connID, _ := wire.ParseConnectionID(p.data, c.srcConnIDLen) + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{ + PacketType: pt, + DestConnectionID: connID, + PacketNumber: protocol.InvalidPacketNumber, + }, + Raw: qlog.RawInfo{Length: int(p.Size())}, + Trigger: qlog.PacketDropHeaderParseError, + }) } - s.logger.Debugf("Dropping %s packet (%d bytes) for which we couldn't unpack the header. Error: %s", pt, p.Size(), err) + c.logger.Debugf("Dropping %s packet (%d bytes) for which we couldn't unpack the header. Error: %s", pt, p.Size(), err) return false, nil } // This is an error returned by the AEAD (other than ErrDecryptionFailed). @@ -1152,270 +1423,430 @@ func (s *connection) handleUnpackError(err error, p receivedPacket, pt logging.P } } -func (s *connection) handleRetryPacket(hdr *wire.Header, data []byte, rcvTime time.Time) bool /* was this a valid Retry */ { - if s.perspective == protocol.PerspectiveServer { - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket) +func (c *Conn) handleRetryPacket(hdr *wire.Header, data []byte, rcvTime monotime.Time) bool /* was this a valid Retry */ { + if c.perspective == protocol.PerspectiveServer { + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{ + PacketType: qlog.PacketTypeRetry, + SrcConnectionID: hdr.SrcConnectionID, + DestConnectionID: hdr.DestConnectionID, + Version: hdr.Version, + }, + Raw: qlog.RawInfo{Length: len(data)}, + Trigger: qlog.PacketDropUnexpectedPacket, + }) } - s.logger.Debugf("Ignoring Retry.") + c.logger.Debugf("Ignoring Retry.") return false } - if s.receivedFirstPacket { - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket) + if c.receivedFirstPacket { + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{ + PacketType: qlog.PacketTypeRetry, + SrcConnectionID: hdr.SrcConnectionID, + DestConnectionID: hdr.DestConnectionID, + Version: hdr.Version, + }, + Raw: qlog.RawInfo{Length: len(data)}, + Trigger: qlog.PacketDropUnexpectedPacket, + }) } - s.logger.Debugf("Ignoring Retry, since we already received a packet.") + c.logger.Debugf("Ignoring Retry, since we already received a packet.") return false } - destConnID := s.connIDManager.Get() + destConnID := c.connIDManager.Get() if hdr.SrcConnectionID == destConnID { - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket) + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{ + PacketType: qlog.PacketTypeRetry, + SrcConnectionID: hdr.SrcConnectionID, + DestConnectionID: hdr.DestConnectionID, + Version: hdr.Version, + }, + Raw: qlog.RawInfo{Length: len(data)}, + Trigger: qlog.PacketDropUnexpectedPacket, + }) } - s.logger.Debugf("Ignoring Retry, since the server didn't change the Source Connection ID.") + c.logger.Debugf("Ignoring Retry, since the server didn't change the Source Connection ID.") return false } // If a token is already set, this means that we already received a Retry from the server. // Ignore this Retry packet. - if s.receivedRetry { - s.logger.Debugf("Ignoring Retry, since a Retry was already received.") + if c.receivedRetry { + c.logger.Debugf("Ignoring Retry, since a Retry was already received.") return false } tag := handshake.GetRetryIntegrityTag(data[:len(data)-16], destConnID, hdr.Version) if !bytes.Equal(data[len(data)-16:], tag[:]) { - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropPayloadDecryptError) + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{ + PacketType: qlog.PacketTypeRetry, + SrcConnectionID: hdr.SrcConnectionID, + DestConnectionID: hdr.DestConnectionID, + Version: hdr.Version, + }, + Raw: qlog.RawInfo{Length: len(data)}, + Trigger: qlog.PacketDropPayloadDecryptError, + }) } - s.logger.Debugf("Ignoring spoofed Retry. Integrity Tag doesn't match.") + c.logger.Debugf("Ignoring spoofed Retry. Integrity Tag doesn't match.") return false } newDestConnID := hdr.SrcConnectionID - s.receivedRetry = true - s.sentPacketHandler.ResetForRetry(rcvTime) - s.handshakeDestConnID = newDestConnID - s.retrySrcConnID = &newDestConnID - s.cryptoStreamHandler.ChangeConnectionID(newDestConnID) - s.packer.SetToken(hdr.Token) - s.connIDManager.ChangeInitialConnID(newDestConnID) - - if s.logger.Debug() { - s.logger.Debugf("<- Received Retry:") - (&wire.ExtendedHeader{Header: *hdr}).Log(s.logger) - s.logger.Debugf("Switching destination connection ID to: %s", hdr.SrcConnectionID) - } - if s.tracer != nil && s.tracer.ReceivedRetry != nil { - s.tracer.ReceivedRetry(hdr) + c.receivedRetry = true + c.sentPacketHandler.ResetForRetry(rcvTime) + c.handshakeDestConnID = newDestConnID + c.retrySrcConnID = &newDestConnID + c.cryptoStreamHandler.ChangeConnectionID(newDestConnID) + c.packer.SetToken(hdr.Token) + c.connIDManager.ChangeInitialConnID(newDestConnID) + + if c.logger.Debug() { + c.logger.Debugf("<- Received Retry:") + (&wire.ExtendedHeader{Header: *hdr}).Log(c.logger) + c.logger.Debugf("Switching destination connection ID to: %s", hdr.SrcConnectionID) + } + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketReceived{ + Header: qlog.PacketHeader{ + PacketType: qlog.PacketTypeRetry, + DestConnectionID: destConnID, + SrcConnectionID: newDestConnID, + Version: hdr.Version, + Token: &qlog.Token{Raw: hdr.Token}, + }, + Raw: qlog.RawInfo{Length: len(data)}, + }) } - s.scheduleSending() + c.scheduleSending() return true } -func (s *connection) handleVersionNegotiationPacket(p receivedPacket) { - if s.perspective == protocol.PerspectiveServer || // servers never receive version negotiation packets - s.receivedFirstPacket || s.versionNegotiated { // ignore delayed / duplicated version negotiation packets - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropUnexpectedPacket) +func (c *Conn) handleVersionNegotiationPacket(p receivedPacket) { + if c.perspective == protocol.PerspectiveServer || // servers never receive version negotiation packets + c.receivedFirstPacket || c.versionNegotiated { // ignore delayed / duplicated version negotiation packets + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{PacketType: qlog.PacketTypeVersionNegotiation}, + Raw: qlog.RawInfo{Length: int(p.Size())}, + Trigger: qlog.PacketDropUnexpectedPacket, + }) } return } src, dest, supportedVersions, err := wire.ParseVersionNegotiationPacket(p.data) if err != nil { - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropHeaderParseError) - } - s.logger.Debugf("Error parsing Version Negotiation packet: %s", err) + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{PacketType: qlog.PacketTypeVersionNegotiation}, + Raw: qlog.RawInfo{Length: int(p.Size())}, + Trigger: qlog.PacketDropHeaderParseError, + }) + } + c.logger.Debugf("Error parsing Version Negotiation packet: %s", err) return } - for _, v := range supportedVersions { - if v == s.version { - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropUnexpectedVersion) - } - // The Version Negotiation packet contains the version that we offered. - // This might be a packet sent by an attacker, or it was corrupted. - return + if slices.Contains(supportedVersions, c.version) { + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{PacketType: qlog.PacketTypeVersionNegotiation}, + Raw: qlog.RawInfo{Length: int(p.Size())}, + Trigger: qlog.PacketDropUnexpectedVersion, + }) } + // The Version Negotiation packet contains the version that we offered. + // This might be a packet sent by an attacker, or it was corrupted. + return } - s.logger.Infof("Received a Version Negotiation packet. Supported Versions: %s", supportedVersions) - if s.tracer != nil && s.tracer.ReceivedVersionNegotiationPacket != nil { - s.tracer.ReceivedVersionNegotiationPacket(dest, src, supportedVersions) + c.logger.Infof("Received a Version Negotiation packet. Supported Versions: %s", supportedVersions) + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.VersionNegotiationReceived{ + Header: qlog.PacketHeaderVersionNegotiation{ + DestConnectionID: dest, + SrcConnectionID: src, + }, + SupportedVersions: supportedVersions, + }) } - newVersion, ok := protocol.ChooseSupportedVersion(s.config.Versions, supportedVersions) + newVersion, ok := protocol.ChooseSupportedVersion(c.config.Versions, supportedVersions) if !ok { - s.destroyImpl(&VersionNegotiationError{ - Ours: s.config.Versions, + c.destroyImpl(&VersionNegotiationError{ + Ours: c.config.Versions, Theirs: supportedVersions, }) - s.logger.Infof("No compatible QUIC version found.") + c.logger.Infof("No compatible QUIC version found.") return } - if s.tracer != nil && s.tracer.NegotiatedVersion != nil { - s.tracer.NegotiatedVersion(newVersion, s.config.Versions, supportedVersions) + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.VersionInformation{ + ChosenVersion: newVersion, + ClientVersions: c.config.Versions, + ServerVersions: supportedVersions, + }) } - s.logger.Infof("Switching to QUIC version %s.", newVersion) - nextPN, _ := s.sentPacketHandler.PeekPacketNumber(protocol.EncryptionInitial) - s.destroyImpl(&errCloseForRecreating{ + c.logger.Infof("Switching to QUIC version %s.", newVersion) + nextPN, _ := c.sentPacketHandler.PeekPacketNumber(protocol.EncryptionInitial) + c.destroyImpl(&errCloseForRecreating{ nextPacketNumber: nextPN, nextVersion: newVersion, }) } -func (s *connection) handleUnpackedLongHeaderPacket( +func (c *Conn) handleUnpackedLongHeaderPacket( packet *unpackedPacket, ecn protocol.ECN, - rcvTime time.Time, + rcvTime monotime.Time, packetSize protocol.ByteCount, // only for logging ) error { - if !s.receivedFirstPacket { - s.receivedFirstPacket = true - if !s.versionNegotiated && s.tracer != nil && s.tracer.NegotiatedVersion != nil { - var clientVersions, serverVersions []protocol.Version - switch s.perspective { + if !c.receivedFirstPacket { + c.receivedFirstPacket = true + if !c.versionNegotiated && c.qlogger != nil { + var clientVersions, serverVersions []Version + switch c.perspective { case protocol.PerspectiveClient: - clientVersions = s.config.Versions + clientVersions = c.config.Versions case protocol.PerspectiveServer: - serverVersions = s.config.Versions + serverVersions = c.config.Versions } - s.tracer.NegotiatedVersion(s.version, clientVersions, serverVersions) + c.qlogger.RecordEvent(qlog.VersionInformation{ + ChosenVersion: c.version, + ClientVersions: clientVersions, + ServerVersions: serverVersions, + }) } // The server can change the source connection ID with the first Handshake packet. - if s.perspective == protocol.PerspectiveClient && packet.hdr.SrcConnectionID != s.handshakeDestConnID { + if c.perspective == protocol.PerspectiveClient && packet.hdr.SrcConnectionID != c.handshakeDestConnID { cid := packet.hdr.SrcConnectionID - s.logger.Debugf("Received first packet. Switching destination connection ID to: %s", cid) - s.handshakeDestConnID = cid - s.connIDManager.ChangeInitialConnID(cid) + c.logger.Debugf("Received first packet. Switching destination connection ID to: %s", cid) + c.handshakeDestConnID = cid + c.connIDManager.ChangeInitialConnID(cid) } // We create the connection as soon as we receive the first packet from the client. // We do that before authenticating the packet. // That means that if the source connection ID was corrupted, // we might have created a connection with an incorrect source connection ID. // Once we authenticate the first packet, we need to update it. - if s.perspective == protocol.PerspectiveServer { - if packet.hdr.SrcConnectionID != s.handshakeDestConnID { - s.handshakeDestConnID = packet.hdr.SrcConnectionID - s.connIDManager.ChangeInitialConnID(packet.hdr.SrcConnectionID) + if c.perspective == protocol.PerspectiveServer { + if packet.hdr.SrcConnectionID != c.handshakeDestConnID { + c.handshakeDestConnID = packet.hdr.SrcConnectionID + c.connIDManager.ChangeInitialConnID(packet.hdr.SrcConnectionID) } - if s.tracer != nil && s.tracer.StartedConnection != nil { - s.tracer.StartedConnection( - s.conn.LocalAddr(), - s.conn.RemoteAddr(), - packet.hdr.SrcConnectionID, - packet.hdr.DestConnectionID, - ) + if c.qlogger != nil { + var srcAddr, destAddr *net.UDPAddr + if addr, ok := c.conn.LocalAddr().(*net.UDPAddr); ok { + srcAddr = addr + } + if addr, ok := c.conn.RemoteAddr().(*net.UDPAddr); ok { + destAddr = addr + } + c.qlogger.RecordEvent(startedConnectionEvent(srcAddr, destAddr)) } } } - if s.perspective == protocol.PerspectiveServer && packet.encryptionLevel == protocol.EncryptionHandshake && - !s.droppedInitialKeys { + if c.perspective == protocol.PerspectiveServer && packet.encryptionLevel == protocol.EncryptionHandshake && + !c.droppedInitialKeys { // On the server side, Initial keys are dropped as soon as the first Handshake packet is received. // See Section 4.9.1 of RFC 9001. - if err := s.dropEncryptionLevel(protocol.EncryptionInitial, rcvTime); err != nil { + if err := c.dropEncryptionLevel(protocol.EncryptionInitial, rcvTime); err != nil { return err } } - s.lastPacketReceivedTime = rcvTime - s.firstAckElicitingPacketAfterIdleSentTime = time.Time{} - s.keepAlivePingSent = false + c.lastPacketReceivedTime = rcvTime + c.firstAckElicitingPacketAfterIdleSentTime = 0 + c.keepAlivePingSent = false if packet.hdr.Type == protocol.PacketType0RTT { - s.largestRcvdAppData = max(s.largestRcvdAppData, packet.hdr.PacketNumber) + c.largestRcvdAppData = max(c.largestRcvdAppData, packet.hdr.PacketNumber) } - var log func([]logging.Frame) - if s.tracer != nil && s.tracer.ReceivedLongHeaderPacket != nil { - log = func(frames []logging.Frame) { - s.tracer.ReceivedLongHeaderPacket(packet.hdr, packetSize, ecn, frames) + var log func([]qlog.Frame) + if c.qlogger != nil { + log = func(frames []qlog.Frame) { + var token *qlog.Token + if len(packet.hdr.Token) > 0 { + token = &qlog.Token{Raw: packet.hdr.Token} + } + c.qlogger.RecordEvent(qlog.PacketReceived{ + Header: qlog.PacketHeader{ + PacketType: toQlogPacketType(packet.hdr.Type), + DestConnectionID: packet.hdr.DestConnectionID, + SrcConnectionID: packet.hdr.SrcConnectionID, + PacketNumber: packet.hdr.PacketNumber, + Version: packet.hdr.Version, + Token: token, + }, + Raw: qlog.RawInfo{ + Length: int(packetSize), + PayloadLength: int(packet.hdr.Length), + }, + Frames: frames, + ECN: toQlogECN(ecn), + }) } } - isAckEliciting, _, err := s.handleFrames(packet.data, packet.hdr.DestConnectionID, packet.encryptionLevel, log, rcvTime) + isAckEliciting, _, _, err := c.handleFrames(packet.data, packet.hdr.DestConnectionID, packet.encryptionLevel, log, rcvTime) if err != nil { return err } - return s.receivedPacketHandler.ReceivedPacket(packet.hdr.PacketNumber, ecn, packet.encryptionLevel, rcvTime, isAckEliciting) + return c.receivedPacketHandler.ReceivedPacket(packet.hdr.PacketNumber, ecn, packet.encryptionLevel, rcvTime, isAckEliciting) } -func (s *connection) handleUnpackedShortHeaderPacket( +func (c *Conn) handleUnpackedShortHeaderPacket( destConnID protocol.ConnectionID, pn protocol.PacketNumber, data []byte, ecn protocol.ECN, - rcvTime time.Time, - log func([]logging.Frame), -) (isNonProbing bool, _ error) { - s.lastPacketReceivedTime = rcvTime - s.firstAckElicitingPacketAfterIdleSentTime = time.Time{} - s.keepAlivePingSent = false - - isAckEliciting, isNonProbing, err := s.handleFrames(data, destConnID, protocol.Encryption1RTT, log, rcvTime) + rcvTime monotime.Time, + log func([]qlog.Frame), +) (isNonProbing bool, pathChallenge *wire.PathChallengeFrame, _ error) { + c.lastPacketReceivedTime = rcvTime + c.firstAckElicitingPacketAfterIdleSentTime = 0 + c.keepAlivePingSent = false + + isAckEliciting, isNonProbing, pathChallenge, err := c.handleFrames(data, destConnID, protocol.Encryption1RTT, log, rcvTime) if err != nil { - return false, err + return false, nil, err } - if err := s.receivedPacketHandler.ReceivedPacket(pn, ecn, protocol.Encryption1RTT, rcvTime, isAckEliciting); err != nil { - return false, err + if err := c.receivedPacketHandler.ReceivedPacket(pn, ecn, protocol.Encryption1RTT, rcvTime, isAckEliciting); err != nil { + return false, nil, err } - return isNonProbing, nil + return isNonProbing, pathChallenge, nil } -func (s *connection) handleFrames( +// handleFrames parses the frames, one after the other, and handles them. +// It returns the last PATH_CHALLENGE frame contained in the packet, if any. +func (c *Conn) handleFrames( data []byte, destConnID protocol.ConnectionID, encLevel protocol.EncryptionLevel, - log func([]logging.Frame), - rcvTime time.Time, -) (isAckEliciting, isNonProbing bool, _ error) { + log func([]qlog.Frame), + rcvTime monotime.Time, +) (isAckEliciting, isNonProbing bool, pathChallenge *wire.PathChallengeFrame, _ error) { // Only used for tracing. // If we're not tracing, this slice will always remain empty. - var frames []logging.Frame + var frames []qlog.Frame if log != nil { - frames = make([]logging.Frame, 0, 4) + frames = make([]qlog.Frame, 0, 4) } - handshakeWasComplete := s.handshakeComplete + handshakeWasComplete := c.handshakeComplete var handleErr error + var skipHandling bool + for len(data) > 0 { - l, frame, err := s.frameParser.ParseNext(data, encLevel, s.version) + frameType, l, err := c.frameParser.ParseType(data, encLevel) if err != nil { - return false, false, err + // The frame parser skips over PADDING frames, and returns an io.EOF if the PADDING + // frames were the last frames in this packet. + if err == io.EOF { + break + } + return false, false, nil, err } data = data[l:] - if frame == nil { - break - } - if ackhandler.IsFrameAckEliciting(frame) { + + if ackhandler.IsFrameTypeAckEliciting(frameType) { isAckEliciting = true } - if !wire.IsProbingFrame(frame) { + if !wire.IsProbingFrameType(frameType) { isNonProbing = true } - if log != nil { - frames = append(frames, toLoggingFrame(frame)) + + // We're inlining common cases, to avoid using interfaces + // Fast path: STREAM, DATAGRAM and ACK + if frameType.IsStreamFrameType() { + streamFrame, l, err := c.frameParser.ParseStreamFrame(frameType, data, c.version) + if err != nil { + return false, false, nil, err + } + data = data[l:] + + if log != nil { + frames = append(frames, toQlogFrame(streamFrame)) + } + // an error occurred handling a previous frame, don't handle the current frame + if skipHandling { + continue + } + wire.LogFrame(c.logger, streamFrame, false) + handleErr = c.streamsMap.HandleStreamFrame(streamFrame, rcvTime) + } else if frameType.IsAckFrameType() { + ackFrame, l, err := c.frameParser.ParseAckFrame(frameType, data, encLevel, c.version) + if err != nil { + return false, false, nil, err + } + data = data[l:] + if log != nil { + frames = append(frames, toQlogFrame(ackFrame)) + } + // an error occurred handling a previous frame, don't handle the current frame + if skipHandling { + continue + } + wire.LogFrame(c.logger, ackFrame, false) + handleErr = c.handleAckFrame(ackFrame, encLevel, rcvTime) + } else if frameType.IsDatagramFrameType() { + datagramFrame, l, err := c.frameParser.ParseDatagramFrame(frameType, data, c.version) + if err != nil { + return false, false, nil, err + } + data = data[l:] + + if log != nil { + frames = append(frames, toQlogFrame(datagramFrame)) + } + // an error occurred handling a previous frame, don't handle the current frame + if skipHandling { + continue + } + wire.LogFrame(c.logger, datagramFrame, false) + handleErr = c.handleDatagramFrame(datagramFrame) + } else { + frame, l, err := c.frameParser.ParseLessCommonFrame(frameType, data, c.version) + if err != nil { + return false, false, nil, err + } + data = data[l:] + + if log != nil { + frames = append(frames, toQlogFrame(frame)) + } + // an error occurred handling a previous frame, don't handle the current frame + if skipHandling { + continue + } + pc, err := c.handleFrame(frame, encLevel, destConnID, rcvTime) + if pc != nil { + pathChallenge = pc + } + handleErr = err } - // An error occurred handling a previous frame. - // Don't handle the current frame. + if handleErr != nil { - continue - } - if err := s.handleFrame(frame, encLevel, destConnID, rcvTime); err != nil { + // if we're logging, we need to keep parsing (but not handling) all frames + skipHandling = true if log == nil { - return false, false, err + return false, false, nil, handleErr } - // If we're logging, we need to keep parsing (but not handling) all frames. - handleErr = err } } if log != nil { log(frames) if handleErr != nil { - return false, false, handleErr + return false, false, nil, handleErr } } @@ -1423,88 +1854,86 @@ func (s *connection) handleFrames( // This ensures that we correctly handle the following case on the server side: // We receive a Handshake packet that contains the CRYPTO frame that allows us to complete the handshake, // and an ACK serialized after that CRYPTO frame. In this case, we still want to process the ACK frame. - if !handshakeWasComplete && s.handshakeComplete { - if err := s.handleHandshakeComplete(rcvTime); err != nil { - return false, false, err + if !handshakeWasComplete && c.handshakeComplete { + if err := c.handleHandshakeComplete(rcvTime); err != nil { + return false, false, nil, err } } return } -func (s *connection) handleFrame( +func (c *Conn) handleFrame( f wire.Frame, encLevel protocol.EncryptionLevel, destConnID protocol.ConnectionID, - rcvTime time.Time, -) error { + rcvTime monotime.Time, +) (pathChallenge *wire.PathChallengeFrame, _ error) { var err error - wire.LogFrame(s.logger, f, false) + wire.LogFrame(c.logger, f, false) switch frame := f.(type) { case *wire.CryptoFrame: - err = s.handleCryptoFrame(frame, encLevel, rcvTime) - case *wire.StreamFrame: - err = s.handleStreamFrame(frame, rcvTime) - case *wire.AckFrame: - err = s.handleAckFrame(frame, encLevel, rcvTime) + err = c.handleCryptoFrame(frame, encLevel, rcvTime) case *wire.ConnectionCloseFrame: - err = s.handleConnectionCloseFrame(frame) + err = c.handleConnectionCloseFrame(frame) case *wire.ResetStreamFrame: - err = s.handleResetStreamFrame(frame, rcvTime) + err = c.streamsMap.HandleResetStreamFrame(frame, rcvTime) case *wire.MaxDataFrame: - s.handleMaxDataFrame(frame) + c.connFlowController.UpdateSendWindow(frame.MaximumData) case *wire.MaxStreamDataFrame: - err = s.handleMaxStreamDataFrame(frame) + err = c.streamsMap.HandleMaxStreamDataFrame(frame) case *wire.MaxStreamsFrame: - s.handleMaxStreamsFrame(frame) + c.streamsMap.HandleMaxStreamsFrame(frame) case *wire.DataBlockedFrame: case *wire.StreamDataBlockedFrame: - err = s.handleStreamDataBlockedFrame(frame) + err = c.streamsMap.HandleStreamDataBlockedFrame(frame) case *wire.StreamsBlockedFrame: case *wire.StopSendingFrame: - err = s.handleStopSendingFrame(frame) + err = c.streamsMap.HandleStopSendingFrame(frame) case *wire.PingFrame: case *wire.PathChallengeFrame: - s.handlePathChallengeFrame(frame) + c.handlePathChallengeFrame(frame) + pathChallenge = frame case *wire.PathResponseFrame: - err = s.handlePathResponseFrame(frame) + err = c.handlePathResponseFrame(frame) case *wire.NewTokenFrame: - err = s.handleNewTokenFrame(frame) + err = c.handleNewTokenFrame(frame) case *wire.NewConnectionIDFrame: - err = s.handleNewConnectionIDFrame(frame) + err = c.connIDManager.Add(frame) case *wire.RetireConnectionIDFrame: - err = s.handleRetireConnectionIDFrame(frame, destConnID) + err = c.connIDGenerator.Retire(frame.SequenceNumber, destConnID, rcvTime.Add(3*c.rttStats.PTO(false))) case *wire.HandshakeDoneFrame: - err = s.handleHandshakeDoneFrame(rcvTime) - case *wire.DatagramFrame: - err = s.handleDatagramFrame(frame) + err = c.handleHandshakeDoneFrame(rcvTime) default: err = fmt.Errorf("unexpected frame type: %s", reflect.ValueOf(&frame).Elem().Type().Name()) } - return err + return pathChallenge, err } // handlePacket is called by the server with a new packet -func (s *connection) handlePacket(p receivedPacket) { - s.receivedPacketMx.Lock() +func (c *Conn) handlePacket(p receivedPacket) { + c.receivedPacketMx.Lock() // Discard packets once the amount of queued packets is larger than // the channel size, protocol.MaxConnUnprocessedPackets - if s.receivedPackets.Len() >= protocol.MaxConnUnprocessedPackets { - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropDOSPrevention) - } - s.receivedPacketMx.Unlock() + if c.receivedPackets.Len() >= protocol.MaxConnUnprocessedPackets { + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Raw: qlog.RawInfo{Length: int(p.Size())}, + Trigger: qlog.PacketDropDOSPrevention, + }) + } + c.receivedPacketMx.Unlock() return } - s.receivedPackets.PushBack(p) - s.receivedPacketMx.Unlock() + c.receivedPackets.PushBack(p) + c.receivedPacketMx.Unlock() select { - case s.notifyReceivedPacket <- struct{}{}: + case c.notifyReceivedPacket <- struct{}{}: default: } } -func (s *connection) handleConnectionCloseFrame(frame *wire.ConnectionCloseFrame) error { +func (c *Conn) handleConnectionCloseFrame(frame *wire.ConnectionCloseFrame) error { if frame.IsApplicationError { return &qerr.ApplicationError{ Remote: true, @@ -1520,25 +1949,25 @@ func (s *connection) handleConnectionCloseFrame(frame *wire.ConnectionCloseFrame } } -func (s *connection) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) error { - if err := s.cryptoStreamManager.HandleCryptoFrame(frame, encLevel); err != nil { +func (c *Conn) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel, rcvTime monotime.Time) error { + if err := c.cryptoStreamManager.HandleCryptoFrame(frame, encLevel); err != nil { return err } for { - data := s.cryptoStreamManager.GetCryptoData(encLevel) + data := c.cryptoStreamManager.GetCryptoData(encLevel) if data == nil { break } - if err := s.cryptoStreamHandler.HandleMessage(data, encLevel); err != nil { + if err := c.cryptoStreamHandler.HandleMessage(data, encLevel); err != nil { return err } } - return s.handleHandshakeEvents(rcvTime) + return c.handleHandshakeEvents(rcvTime) } -func (s *connection) handleHandshakeEvents(now time.Time) error { +func (c *Conn) handleHandshakeEvents(now monotime.Time) error { for { - ev := s.cryptoStreamHandler.NextEvent() + ev := c.cryptoStreamHandler.NextEvent() var err error switch ev.Kind { case handshake.EventNoEvent: @@ -1546,22 +1975,22 @@ func (s *connection) handleHandshakeEvents(now time.Time) error { case handshake.EventHandshakeComplete: // Don't call handleHandshakeComplete yet. // It's advantageous to process ACK frames that might be serialized after the CRYPTO frame first. - s.handshakeComplete = true + c.handshakeComplete = true case handshake.EventReceivedTransportParameters: - err = s.handleTransportParameters(ev.TransportParameters) + err = c.handleTransportParameters(ev.TransportParameters) case handshake.EventRestoredTransportParameters: - s.restoreTransportParameters(ev.TransportParameters) - close(s.earlyConnReadyChan) + c.restoreTransportParameters(ev.TransportParameters) + close(c.earlyConnReadyChan) case handshake.EventReceivedReadKeys: // queue all previously undecryptable packets - s.undecryptablePacketsToProcess = append(s.undecryptablePacketsToProcess, s.undecryptablePackets...) - s.undecryptablePackets = nil + c.undecryptablePacketsToProcess = append(c.undecryptablePacketsToProcess, c.undecryptablePackets...) + c.undecryptablePackets = nil case handshake.EventDiscard0RTTKeys: - err = s.dropEncryptionLevel(protocol.Encryption0RTT, now) + err = c.dropEncryptionLevel(protocol.Encryption0RTT, now) case handshake.EventWriteInitialData: - _, err = s.initialStream.Write(ev.Data) + _, err = c.initialStream.Write(ev.Data) case handshake.EventWriteHandshakeData: - _, err = s.handshakeStream.Write(ev.Data) + _, err = c.handshakeStream.Write(ev.Data) } if err != nil { return err @@ -1569,123 +1998,75 @@ func (s *connection) handleHandshakeEvents(now time.Time) error { } } -func (s *connection) handleStreamFrame(frame *wire.StreamFrame, rcvTime time.Time) error { - str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID) - if err != nil { - return err - } - if str == nil { // stream was already closed and garbage collected - return nil - } - return str.handleStreamFrame(frame, rcvTime) -} - -func (s *connection) handleMaxDataFrame(frame *wire.MaxDataFrame) { - s.connFlowController.UpdateSendWindow(frame.MaximumData) -} - -func (s *connection) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) error { - str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID) - if err != nil { - return err - } - if str == nil { - // stream is closed and already garbage collected - return nil +func (c *Conn) handlePathChallengeFrame(f *wire.PathChallengeFrame) { + if c.perspective == protocol.PerspectiveClient { + c.queueControlFrame(&wire.PathResponseFrame{Data: f.Data}) } - str.updateSendWindow(frame.MaximumStreamData) - return nil -} - -func (s *connection) handleStreamDataBlockedFrame(frame *wire.StreamDataBlockedFrame) error { - // We don't need to do anything in response to a STREAM_DATA_BLOCKED frame, - // but we need to make sure that the stream ID is valid. - _, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID) - return err -} - -func (s *connection) handleMaxStreamsFrame(frame *wire.MaxStreamsFrame) { - s.streamsMap.HandleMaxStreamsFrame(frame) } -func (s *connection) handleResetStreamFrame(frame *wire.ResetStreamFrame, rcvTime time.Time) error { - str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID) - if err != nil { - return err - } - if str == nil { - // stream is closed and already garbage collected - return nil +func (c *Conn) handlePathResponseFrame(f *wire.PathResponseFrame) error { + switch c.perspective { + case protocol.PerspectiveClient: + return c.handlePathResponseFrameClient(f) + case protocol.PerspectiveServer: + return c.handlePathResponseFrameServer(f) + default: + panic("unreachable") } - return str.handleResetStreamFrame(frame, rcvTime) } -func (s *connection) handleStopSendingFrame(frame *wire.StopSendingFrame) error { - str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID) - if err != nil { - return err - } - if str == nil { - // stream is closed and already garbage collected - return nil +func (c *Conn) handlePathResponseFrameClient(f *wire.PathResponseFrame) error { + pm := c.pathManagerOutgoing.Load() + if pm == nil { + return &qerr.TransportError{ + ErrorCode: qerr.ProtocolViolation, + ErrorMessage: "unexpected PATH_RESPONSE frame", + } } - str.handleStopSendingFrame(frame) + pm.HandlePathResponseFrame(f) return nil } -func (s *connection) handlePathChallengeFrame(f *wire.PathChallengeFrame) { - s.queueControlFrame(&wire.PathResponseFrame{Data: f.Data}) -} - -func (s *connection) handlePathResponseFrame(f *wire.PathResponseFrame) error { - s.logger.Debugf("received PATH_RESPONSE frame: %v", f.Data) - if s.pathManager == nil { +func (c *Conn) handlePathResponseFrameServer(f *wire.PathResponseFrame) error { + if c.pathManager == nil { // since we didn't send PATH_CHALLENGEs yet, we don't expect PATH_RESPONSEs return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "unexpected PATH_RESPONSE frame", } } - s.pathManager.HandlePathResponseFrame(f) + c.pathManager.HandlePathResponseFrame(f) return nil } -func (s *connection) handleNewTokenFrame(frame *wire.NewTokenFrame) error { - if s.perspective == protocol.PerspectiveServer { +func (c *Conn) handleNewTokenFrame(frame *wire.NewTokenFrame) error { + if c.perspective == protocol.PerspectiveServer { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "received NEW_TOKEN frame from the client", } } - if s.config.TokenStore != nil { - s.config.TokenStore.Put(s.tokenStoreKey, &ClientToken{data: frame.Token}) + if c.config.TokenStore != nil { + c.config.TokenStore.Put(c.tokenStoreKey, &ClientToken{data: frame.Token, rtt: c.rttStats.SmoothedRTT()}) } return nil } -func (s *connection) handleNewConnectionIDFrame(f *wire.NewConnectionIDFrame) error { - return s.connIDManager.Add(f) -} - -func (s *connection) handleRetireConnectionIDFrame(f *wire.RetireConnectionIDFrame, destConnID protocol.ConnectionID) error { - return s.connIDGenerator.Retire(f.SequenceNumber, destConnID) -} - -func (s *connection) handleHandshakeDoneFrame(rcvTime time.Time) error { - if s.perspective == protocol.PerspectiveServer { +func (c *Conn) handleHandshakeDoneFrame(rcvTime monotime.Time) error { + if c.perspective == protocol.PerspectiveServer { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "received a HANDSHAKE_DONE frame", } } - if !s.handshakeConfirmed { - return s.handleHandshakeConfirmed(rcvTime) + if !c.handshakeConfirmed { + return c.handleHandshakeConfirmed(rcvTime) } return nil } -func (s *connection) handleAckFrame(frame *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) error { - acked1RTTPacket, err := s.sentPacketHandler.ReceivedAck(frame, encLevel, s.lastPacketReceivedTime) +func (c *Conn) handleAckFrame(frame *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime monotime.Time) error { + acked1RTTPacket, err := c.sentPacketHandler.ReceivedAck(frame, encLevel, c.lastPacketReceivedTime) if err != nil { return err } @@ -1695,81 +2076,83 @@ func (s *connection) handleAckFrame(frame *wire.AckFrame, encLevel protocol.Encr // On the client side: If the packet acknowledged a 1-RTT packet, this confirms the handshake. // This is only possible if the ACK was sent in a 1-RTT packet. // This is an optimization over simply waiting for a HANDSHAKE_DONE frame, see section 4.1.2 of RFC 9001. - if s.perspective == protocol.PerspectiveClient && !s.handshakeConfirmed { - if err := s.handleHandshakeConfirmed(rcvTime); err != nil { + if c.perspective == protocol.PerspectiveClient && !c.handshakeConfirmed { + if err := c.handleHandshakeConfirmed(rcvTime); err != nil { return err } } // If one of the acknowledged packets was a Path MTU probe packet, this might have increased the Path MTU estimate. - if s.mtuDiscoverer != nil { - if mtu := s.mtuDiscoverer.CurrentSize(); mtu > protocol.ByteCount(s.currentMTUEstimate.Load()) { - s.currentMTUEstimate.Store(uint32(mtu)) - s.sentPacketHandler.SetMaxDatagramSize(mtu) + if c.mtuDiscoverer != nil { + if mtu := c.mtuDiscoverer.CurrentSize(); mtu > protocol.ByteCount(c.currentMTUEstimate.Load()) { + c.currentMTUEstimate.Store(uint32(mtu)) + c.sentPacketHandler.SetMaxDatagramSize(mtu) } } - return s.cryptoStreamHandler.SetLargest1RTTAcked(frame.LargestAcked()) + return c.cryptoStreamHandler.SetLargest1RTTAcked(frame.LargestAcked()) } -func (s *connection) handleDatagramFrame(f *wire.DatagramFrame) error { - if f.Length(s.version) > wire.MaxDatagramSize { +func (c *Conn) handleDatagramFrame(f *wire.DatagramFrame) error { + if f.Length(c.version) > wire.MaxDatagramSize { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "DATAGRAM frame too large", } } - s.datagramQueue.HandleDatagramFrame(f) + c.datagramQueue.HandleDatagramFrame(f) return nil } -func (s *connection) setCloseError(e *closeError) { - s.closeErr.CompareAndSwap(nil, e) +func (c *Conn) setCloseError(e *closeError) { + c.closeErr.CompareAndSwap(nil, e) select { - case s.closeChan <- struct{}{}: + case c.closeChan <- struct{}{}: default: } } // closeLocal closes the connection and send a CONNECTION_CLOSE containing the error -func (s *connection) closeLocal(e error) { - s.setCloseError(&closeError{err: e, immediate: false}) +func (c *Conn) closeLocal(e error) { + c.setCloseError(&closeError{err: e, immediate: false}) } // destroy closes the connection without sending the error on the wire -func (s *connection) destroy(e error) { - s.destroyImpl(e) - <-s.ctx.Done() +func (c *Conn) destroy(e error) { + c.destroyImpl(e) + <-c.ctx.Done() } -func (s *connection) destroyImpl(e error) { - s.setCloseError(&closeError{err: e, immediate: true}) +func (c *Conn) destroyImpl(e error) { + c.setCloseError(&closeError{err: e, immediate: true}) } -func (s *connection) CloseWithError(code ApplicationErrorCode, desc string) error { - s.closeLocal(&qerr.ApplicationError{ +// CloseWithError closes the connection with an error. +// The error string will be sent to the peer. +func (c *Conn) CloseWithError(code ApplicationErrorCode, desc string) error { + c.closeLocal(&qerr.ApplicationError{ ErrorCode: code, ErrorMessage: desc, }) - <-s.ctx.Done() + <-c.ctx.Done() return nil } -func (s *connection) closeWithTransportError(code TransportErrorCode) { - s.closeLocal(&qerr.TransportError{ErrorCode: code}) - <-s.ctx.Done() +func (c *Conn) closeWithTransportError(code TransportErrorCode) { + c.closeLocal(&qerr.TransportError{ErrorCode: code}) + <-c.ctx.Done() } -func (s *connection) handleCloseError(closeErr *closeError) { +func (c *Conn) handleCloseError(closeErr *closeError) { if closeErr.immediate { if nerr, ok := closeErr.err.(net.Error); ok && nerr.Timeout() { - s.logger.Errorf("Destroying connection: %s", closeErr.err) + c.logger.Errorf("Destroying connection: %s", closeErr.err) } else { - s.logger.Errorf("Destroying connection with error: %s", closeErr.err) + c.logger.Errorf("Destroying connection with error: %s", closeErr.err) } } else { if closeErr.err == nil { - s.logger.Infof("Closing connection.") + c.logger.Infof("Closing connection.") } else { - s.logger.Errorf("Closing connection with error: %s", closeErr.err) + c.logger.Errorf("Closing connection with error: %s", closeErr.err) } } @@ -1788,153 +2171,200 @@ func (s *connection) handleCloseError(closeErr *closeError) { transportErr *TransportError ) var isRemoteClose bool + var trigger qlog.ConnectionCloseTrigger + var reason string + var transportErrorCode *qlog.TransportErrorCode + var applicationErrorCode *qlog.ApplicationErrorCode switch { case errors.Is(e, qerr.ErrIdleTimeout), - errors.Is(e, qerr.ErrHandshakeTimeout), - errors.As(e, &statelessResetErr), - errors.As(e, &versionNegotiationErr), - errors.As(e, &recreateErr): + errors.Is(e, qerr.ErrHandshakeTimeout): + trigger = qlog.ConnectionCloseTriggerIdleTimeout + case errors.As(e, &statelessResetErr): + trigger = qlog.ConnectionCloseTriggerStatelessReset + case errors.As(e, &versionNegotiationErr): + trigger = qlog.ConnectionCloseTriggerVersionMismatch + case errors.As(e, &recreateErr): case errors.As(e, &applicationErr): isRemoteClose = applicationErr.Remote + reason = applicationErr.ErrorMessage + applicationErrorCode = &applicationErr.ErrorCode case errors.As(e, &transportErr): isRemoteClose = transportErr.Remote + reason = transportErr.ErrorMessage + transportErrorCode = &transportErr.ErrorCode case closeErr.immediate: e = closeErr.err default: - e = &qerr.TransportError{ + te := &qerr.TransportError{ ErrorCode: qerr.InternalError, ErrorMessage: e.Error(), } + e = te + reason = te.ErrorMessage + code := te.ErrorCode + transportErrorCode = &code } - s.streamsMap.CloseWithError(e) - if s.datagramQueue != nil { - s.datagramQueue.CloseWithError(e) + c.streamsMap.CloseWithError(e) + if c.datagramQueue != nil { + c.datagramQueue.CloseWithError(e) } // In rare instances, the connection ID manager might switch to a new connection ID // when sending the CONNECTION_CLOSE frame. // The connection ID manager removes the active stateless reset token from the packet // handler map when it is closed, so we need to make sure that this happens last. - defer s.connIDManager.Close() - - if s.tracer != nil && s.tracer.ClosedConnection != nil && !errors.As(e, &recreateErr) { - s.tracer.ClosedConnection(e) + defer c.connIDManager.Close() + + if c.qlogger != nil && !errors.As(e, &recreateErr) { + initiator := qlog.InitiatorLocal + if isRemoteClose { + initiator = qlog.InitiatorRemote + } + c.qlogger.RecordEvent(qlog.ConnectionClosed{ + Initiator: initiator, + ConnectionError: transportErrorCode, + ApplicationError: applicationErrorCode, + Trigger: trigger, + Reason: reason, + }) } // If this is a remote close we're done here if isRemoteClose { - s.connIDGenerator.ReplaceWithClosed(nil) + c.connIDGenerator.ReplaceWithClosed(nil, 3*c.rttStats.PTO(false)) return } if closeErr.immediate { - s.connIDGenerator.RemoveAll() + c.connIDGenerator.RemoveAll() return } // Don't send out any CONNECTION_CLOSE if this is an error that occurred // before we even sent out the first packet. - if s.perspective == protocol.PerspectiveClient && !s.sentFirstPacket { - s.connIDGenerator.RemoveAll() + if c.perspective == protocol.PerspectiveClient && !c.sentFirstPacket { + c.connIDGenerator.RemoveAll() return } - connClosePacket, err := s.sendConnectionClose(e) + connClosePacket, err := c.sendConnectionClose(e) if err != nil { - s.logger.Debugf("Error sending CONNECTION_CLOSE: %s", err) + c.logger.Debugf("Error sending CONNECTION_CLOSE: %s", err) } - s.connIDGenerator.ReplaceWithClosed(connClosePacket) + c.connIDGenerator.ReplaceWithClosed(connClosePacket, 3*c.rttStats.PTO(false)) } -func (s *connection) dropEncryptionLevel(encLevel protocol.EncryptionLevel, now time.Time) error { - if s.tracer != nil && s.tracer.DroppedEncryptionLevel != nil { - s.tracer.DroppedEncryptionLevel(encLevel) - } - s.sentPacketHandler.DropPackets(encLevel, now) - s.receivedPacketHandler.DropPackets(encLevel) +func (c *Conn) dropEncryptionLevel(encLevel protocol.EncryptionLevel, now monotime.Time) error { + c.sentPacketHandler.DropPackets(encLevel, now) + c.receivedPacketHandler.DropPackets(encLevel) //nolint:exhaustive // only Initial and 0-RTT need special treatment switch encLevel { case protocol.EncryptionInitial: - s.droppedInitialKeys = true - s.cryptoStreamHandler.DiscardInitialKeys() + c.droppedInitialKeys = true + c.cryptoStreamHandler.DiscardInitialKeys() case protocol.Encryption0RTT: - s.streamsMap.ResetFor0RTT() - s.framer.Handle0RTTRejection() - return s.connFlowController.Reset() + c.streamsMap.ResetFor0RTT() + c.framer.Handle0RTTRejection() + return c.connFlowController.Reset() } - return s.cryptoStreamManager.Drop(encLevel) + return c.cryptoStreamManager.Drop(encLevel) } // is called for the client, when restoring transport parameters saved for 0-RTT -func (s *connection) restoreTransportParameters(params *wire.TransportParameters) { - if s.logger.Debug() { - s.logger.Debugf("Restoring Transport Parameters: %s", params) +func (c *Conn) restoreTransportParameters(params *wire.TransportParameters) { + if c.logger.Debug() { + c.logger.Debugf("Restoring Transport Parameters: %s", params) + } + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.ParametersSet{ + Restore: true, + Initiator: qlog.InitiatorRemote, + SentBy: c.perspective, + OriginalDestinationConnectionID: params.OriginalDestinationConnectionID, + InitialSourceConnectionID: params.InitialSourceConnectionID, + RetrySourceConnectionID: params.RetrySourceConnectionID, + StatelessResetToken: params.StatelessResetToken, + DisableActiveMigration: params.DisableActiveMigration, + MaxIdleTimeout: params.MaxIdleTimeout, + MaxUDPPayloadSize: params.MaxUDPPayloadSize, + AckDelayExponent: params.AckDelayExponent, + MaxAckDelay: params.MaxAckDelay, + ActiveConnectionIDLimit: params.ActiveConnectionIDLimit, + InitialMaxData: params.InitialMaxData, + InitialMaxStreamDataBidiLocal: params.InitialMaxStreamDataBidiLocal, + InitialMaxStreamDataBidiRemote: params.InitialMaxStreamDataBidiRemote, + InitialMaxStreamDataUni: params.InitialMaxStreamDataUni, + InitialMaxStreamsBidi: int64(params.MaxBidiStreamNum), + InitialMaxStreamsUni: int64(params.MaxUniStreamNum), + MaxDatagramFrameSize: params.MaxDatagramFrameSize, + EnableResetStreamAt: params.EnableResetStreamAt, + }) } - s.peerParams = params - s.connIDGenerator.SetMaxActiveConnIDs(params.ActiveConnectionIDLimit) - s.connFlowController.UpdateSendWindow(params.InitialMaxData) - s.streamsMap.UpdateLimits(params) - s.connStateMutex.Lock() - s.connState.SupportsDatagrams = s.supportsDatagrams() - s.connStateMutex.Unlock() + c.peerParams = params + c.connIDGenerator.SetMaxActiveConnIDs(params.ActiveConnectionIDLimit) + c.connFlowController.UpdateSendWindow(params.InitialMaxData) + c.streamsMap.HandleTransportParameters(params) + c.connStateMutex.Lock() + c.connState.SupportsDatagrams = c.supportsDatagrams() + c.connStateMutex.Unlock() } -func (s *connection) handleTransportParameters(params *wire.TransportParameters) error { - if s.tracer != nil && s.tracer.ReceivedTransportParameters != nil { - s.tracer.ReceivedTransportParameters(params) +func (c *Conn) handleTransportParameters(params *wire.TransportParameters) error { + if c.qlogger != nil { + c.qlogTransportParameters(params, c.perspective.Opposite(), false) } - if err := s.checkTransportParameters(params); err != nil { + if err := c.checkTransportParameters(params); err != nil { return &qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: err.Error(), } } - if s.perspective == protocol.PerspectiveClient && s.peerParams != nil && s.ConnectionState().Used0RTT && !params.ValidForUpdate(s.peerParams) { + if c.perspective == protocol.PerspectiveClient && c.peerParams != nil && c.ConnectionState().Used0RTT && !params.ValidForUpdate(c.peerParams) { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "server sent reduced limits after accepting 0-RTT data", } } - s.peerParams = params + c.peerParams = params // On the client side we have to wait for handshake completion. // During a 0-RTT connection, we are only allowed to use the new transport parameters for 1-RTT packets. - if s.perspective == protocol.PerspectiveServer { - s.applyTransportParameters() + if c.perspective == protocol.PerspectiveServer { + c.applyTransportParameters() // On the server side, the early connection is ready as soon as we processed // the client's transport parameters. - close(s.earlyConnReadyChan) + close(c.earlyConnReadyChan) } - s.connStateMutex.Lock() - s.connState.SupportsDatagrams = s.supportsDatagrams() - s.connStateMutex.Unlock() + c.connStateMutex.Lock() + c.connState.SupportsDatagrams = c.supportsDatagrams() + c.connStateMutex.Unlock() return nil } -func (s *connection) checkTransportParameters(params *wire.TransportParameters) error { - if s.logger.Debug() { - s.logger.Debugf("Processed Transport Parameters: %s", params) +func (c *Conn) checkTransportParameters(params *wire.TransportParameters) error { + if c.logger.Debug() { + c.logger.Debugf("Processed Transport Parameters: %s", params) } // check the initial_source_connection_id - if params.InitialSourceConnectionID != s.handshakeDestConnID { - return fmt.Errorf("expected initial_source_connection_id to equal %s, is %s", s.handshakeDestConnID, params.InitialSourceConnectionID) + if params.InitialSourceConnectionID != c.handshakeDestConnID { + return fmt.Errorf("expected initial_source_connection_id to equal %s, is %s", c.handshakeDestConnID, params.InitialSourceConnectionID) } - if s.perspective == protocol.PerspectiveServer { + if c.perspective == protocol.PerspectiveServer { return nil } // check the original_destination_connection_id - if params.OriginalDestinationConnectionID != s.origDestConnID { - return fmt.Errorf("expected original_destination_connection_id to equal %s, is %s", s.origDestConnID, params.OriginalDestinationConnectionID) + if params.OriginalDestinationConnectionID != c.origDestConnID { + return fmt.Errorf("expected original_destination_connection_id to equal %s, is %s", c.origDestConnID, params.OriginalDestinationConnectionID) } - if s.retrySrcConnID != nil { // a Retry was performed + if c.retrySrcConnID != nil { // a Retry was performed if params.RetrySourceConnectionID == nil { return errors.New("missing retry_source_connection_id") } - if *params.RetrySourceConnectionID != *s.retrySrcConnID { - return fmt.Errorf("expected retry_source_connection_id to equal %s, is %s", s.retrySrcConnID, *params.RetrySourceConnectionID) + if *params.RetrySourceConnectionID != *c.retrySrcConnID { + return fmt.Errorf("expected retry_source_connection_id to equal %s, is %s", c.retrySrcConnID, *params.RetrySourceConnectionID) } } else if params.RetrySourceConnectionID != nil { return errors.New("received retry_source_connection_id, although no Retry was performed") @@ -1942,135 +2372,156 @@ func (s *connection) checkTransportParameters(params *wire.TransportParameters) return nil } -func (s *connection) applyTransportParameters() { - params := s.peerParams +func (c *Conn) applyTransportParameters() { + params := c.peerParams // Our local idle timeout will always be > 0. - s.idleTimeout = s.config.MaxIdleTimeout + c.idleTimeout = c.config.MaxIdleTimeout // If the peer advertised an idle timeout, take the minimum of the values. if params.MaxIdleTimeout > 0 { - s.idleTimeout = min(s.idleTimeout, params.MaxIdleTimeout) - } - s.keepAliveInterval = min(s.config.KeepAlivePeriod, s.idleTimeout/2) - s.streamsMap.UpdateLimits(params) - s.frameParser.SetAckDelayExponent(params.AckDelayExponent) - s.connFlowController.UpdateSendWindow(params.InitialMaxData) - s.rttStats.SetMaxAckDelay(params.MaxAckDelay) - s.connIDGenerator.SetMaxActiveConnIDs(params.ActiveConnectionIDLimit) + c.idleTimeout = min(c.idleTimeout, params.MaxIdleTimeout) + } + c.keepAliveInterval = min(c.config.KeepAlivePeriod, c.idleTimeout/2) + c.streamsMap.HandleTransportParameters(params) + c.frameParser.SetAckDelayExponent(params.AckDelayExponent) + c.connFlowController.UpdateSendWindow(params.InitialMaxData) + c.rttStats.SetMaxAckDelay(params.MaxAckDelay) + c.connIDGenerator.SetMaxActiveConnIDs(params.ActiveConnectionIDLimit) if params.StatelessResetToken != nil { - s.connIDManager.SetStatelessResetToken(*params.StatelessResetToken) + c.connIDManager.SetStatelessResetToken(*params.StatelessResetToken) } // We don't support connection migration yet, so we don't have any use for the preferred_address. if params.PreferredAddress != nil { // Retire the connection ID. - s.connIDManager.AddFromPreferredAddress(params.PreferredAddress.ConnectionID, params.PreferredAddress.StatelessResetToken) + c.connIDManager.AddFromPreferredAddress(params.PreferredAddress.ConnectionID, params.PreferredAddress.StatelessResetToken) } maxPacketSize := protocol.ByteCount(protocol.MaxPacketBufferSize) if params.MaxUDPPayloadSize > 0 && params.MaxUDPPayloadSize < maxPacketSize { maxPacketSize = params.MaxUDPPayloadSize } - s.mtuDiscoverer = newMTUDiscoverer( - s.rttStats, - protocol.ByteCount(s.config.InitialPacketSize), + c.mtuDiscoverer = newMTUDiscoverer( + c.rttStats, + protocol.ByteCount(c.config.InitialPacketSize), maxPacketSize, - s.tracer, + c.qlogger, ) } -func (s *connection) triggerSending(now time.Time) error { - s.pacingDeadline = time.Time{} +func (c *Conn) triggerSending(now monotime.Time) error { + c.pacingDeadline = 0 - sendMode := s.sentPacketHandler.SendMode(now) - //nolint:exhaustive // No need to handle pacing limited here. + sendMode := c.sentPacketHandler.SendMode(now) switch sendMode { case ackhandler.SendAny: - return s.sendPackets(now) + return c.sendPackets(now) case ackhandler.SendNone: + c.blocked = blockModeHardBlocked return nil case ackhandler.SendPacingLimited: - deadline := s.sentPacketHandler.TimeUntilSend() + deadline := c.sentPacketHandler.TimeUntilSend() if deadline.IsZero() { deadline = deadlineSendImmediately } - s.pacingDeadline = deadline + c.pacingDeadline = deadline // Allow sending of an ACK if we're pacing limit. // This makes sure that a peer that is mostly receiving data (and thus has an inaccurate cwnd estimate) // sends enough ACKs to allow its peer to utilize the bandwidth. - fallthrough + return c.maybeSendAckOnlyPacket(now) case ackhandler.SendAck: // We can at most send a single ACK only packet. // There will only be a new ACK after receiving new packets. // SendAck is only returned when we're congestion limited, so we don't need to set the pacing timer. - return s.maybeSendAckOnlyPacket(now) + c.blocked = blockModeCongestionLimited + return c.maybeSendAckOnlyPacket(now) case ackhandler.SendPTOInitial, ackhandler.SendPTOHandshake, ackhandler.SendPTOAppData: - if err := s.sendProbePacket(sendMode, now); err != nil { + if err := c.sendProbePacket(sendMode, now); err != nil { return err } - if s.sendQueue.WouldBlock() { - s.scheduleSending() + if c.sendQueue.WouldBlock() { + c.scheduleSending() return nil } - return s.triggerSending(now) + return c.triggerSending(now) default: return fmt.Errorf("BUG: invalid send mode %d", sendMode) } } -func (s *connection) sendPackets(now time.Time) error { +func (c *Conn) sendPackets(now monotime.Time) error { + if c.perspective == protocol.PerspectiveClient && c.handshakeConfirmed { + if pm := c.pathManagerOutgoing.Load(); pm != nil { + connID, frame, tr, ok := pm.NextPathToProbe() + if ok { + probe, buf, err := c.packer.PackPathProbePacket(connID, []ackhandler.Frame{frame}, c.version) + if err != nil { + return err + } + c.logger.Debugf("sending path probe packet from %s", c.LocalAddr()) + c.logShortHeaderPacket(probe.DestConnID, probe.Ack, probe.Frames, probe.StreamFrames, probe.PacketNumber, probe.PacketNumberLen, probe.KeyPhase, protocol.ECNNon, buf.Len(), false) + c.registerPackedShortHeaderPacket(probe, protocol.ECNNon, now) + tr.WriteTo(buf.Data, c.conn.RemoteAddr()) + // There's (likely) more data to send. Loop around again. + c.scheduleSending() + return nil + } + } + } + // Path MTU Discovery // Can't use GSO, since we need to send a single packet that's larger than our current maximum size. // Performance-wise, this doesn't matter, since we only send a very small (<10) number of // MTU probe packets per connection. - if s.handshakeConfirmed && s.mtuDiscoverer != nil && s.mtuDiscoverer.ShouldSendProbe(now) { - ping, size := s.mtuDiscoverer.GetPing(now) - p, buf, err := s.packer.PackMTUProbePacket(ping, size, s.version) + if c.handshakeConfirmed && c.mtuDiscoverer != nil && c.mtuDiscoverer.ShouldSendProbe(now) { + ping, size := c.mtuDiscoverer.GetPing(now) + p, buf, err := c.packer.PackMTUProbePacket(ping, size, c.version) if err != nil { return err } - ecn := s.sentPacketHandler.ECNMode(true) - s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, buf.Len(), false) - s.registerPackedShortHeaderPacket(p, ecn, now) - s.sendQueue.Send(buf, 0, ecn) + ecn := c.sentPacketHandler.ECNMode(true) + c.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, buf.Len(), false) + c.registerPackedShortHeaderPacket(p, ecn, now) + c.sendQueue.Send(buf, 0, ecn) // There's (likely) more data to send. Loop around again. - s.scheduleSending() + c.scheduleSending() return nil } - if offset := s.connFlowController.GetWindowUpdate(now); offset > 0 { - s.framer.QueueControlFrame(&wire.MaxDataFrame{MaximumData: offset}) + if offset := c.connFlowController.GetWindowUpdate(now); offset > 0 { + c.framer.QueueControlFrame(&wire.MaxDataFrame{MaximumData: offset}) } - if cf := s.cryptoStreamManager.GetPostHandshakeData(protocol.MaxPostHandshakeCryptoFrameSize); cf != nil { - s.queueControlFrame(cf) + if cf := c.cryptoStreamManager.GetPostHandshakeData(protocol.MaxPostHandshakeCryptoFrameSize); cf != nil { + c.queueControlFrame(cf) } - if !s.handshakeConfirmed { - packet, err := s.packer.PackCoalescedPacket(false, s.maxPacketSize(), now, s.version) + if !c.handshakeConfirmed { + packet, err := c.packer.PackCoalescedPacket(false, c.maxPacketSize(), now, c.version) if err != nil || packet == nil { return err } - s.sentFirstPacket = true - if err := s.sendPackedCoalescedPacket(packet, s.sentPacketHandler.ECNMode(packet.IsOnlyShortHeaderPacket()), now); err != nil { + c.sentFirstPacket = true + if err := c.sendPackedCoalescedPacket(packet, c.sentPacketHandler.ECNMode(packet.IsOnlyShortHeaderPacket()), now); err != nil { return err } - sendMode := s.sentPacketHandler.SendMode(now) - if sendMode == ackhandler.SendPacingLimited { - s.resetPacingDeadline() - } else if sendMode == ackhandler.SendAny { - s.pacingDeadline = deadlineSendImmediately + //nolint:exhaustive // only need to handle pacing-related events here + switch c.sentPacketHandler.SendMode(now) { + case ackhandler.SendPacingLimited: + c.resetPacingDeadline() + case ackhandler.SendAny: + c.pacingDeadline = deadlineSendImmediately } return nil } - if s.conn.capabilities().GSO { - return s.sendPacketsWithGSO(now) + if c.conn.capabilities().GSO { + return c.sendPacketsWithGSO(now) } - return s.sendPacketsWithoutGSO(now) + return c.sendPacketsWithoutGSO(now) } -func (s *connection) sendPacketsWithoutGSO(now time.Time) error { +func (c *Conn) sendPacketsWithoutGSO(now monotime.Time) error { for { buf := getPacketBuffer() - ecn := s.sentPacketHandler.ECNMode(true) - if _, err := s.appendOneShortHeaderPacket(buf, s.maxPacketSize(), ecn, now); err != nil { + ecn := c.sentPacketHandler.ECNMode(true) + if _, err := c.appendOneShortHeaderPacket(buf, c.maxPacketSize(), ecn, now); err != nil { if err == errNothingToPack { buf.Release() return nil @@ -2078,38 +2529,38 @@ func (s *connection) sendPacketsWithoutGSO(now time.Time) error { return err } - s.sendQueue.Send(buf, 0, ecn) + c.sendQueue.Send(buf, 0, ecn) - if s.sendQueue.WouldBlock() { + if c.sendQueue.WouldBlock() { return nil } - sendMode := s.sentPacketHandler.SendMode(now) + sendMode := c.sentPacketHandler.SendMode(now) if sendMode == ackhandler.SendPacingLimited { - s.resetPacingDeadline() + c.resetPacingDeadline() return nil } if sendMode != ackhandler.SendAny { return nil } // Prioritize receiving of packets over sending out more packets. - s.receivedPacketMx.Lock() - hasPackets := !s.receivedPackets.Empty() - s.receivedPacketMx.Unlock() + c.receivedPacketMx.Lock() + hasPackets := !c.receivedPackets.Empty() + c.receivedPacketMx.Unlock() if hasPackets { - s.pacingDeadline = deadlineSendImmediately + c.pacingDeadline = deadlineSendImmediately return nil } } } -func (s *connection) sendPacketsWithGSO(now time.Time) error { +func (c *Conn) sendPacketsWithGSO(now monotime.Time) error { buf := getLargePacketBuffer() - maxSize := s.maxPacketSize() + maxSize := c.maxPacketSize() - ecn := s.sentPacketHandler.ECNMode(true) + ecn := c.sentPacketHandler.ECNMode(true) for { var dontSendMore bool - size, err := s.appendOneShortHeaderPacket(buf, maxSize, ecn, now) + size, err := c.appendOneShortHeaderPacket(buf, maxSize, ecn, now) if err != nil { if err != errNothingToPack { return err @@ -2122,9 +2573,9 @@ func (s *connection) sendPacketsWithGSO(now time.Time) error { } if !dontSendMore { - sendMode := s.sentPacketHandler.SendMode(now) + sendMode := c.sentPacketHandler.SendMode(now) if sendMode == ackhandler.SendPacingLimited { - s.resetPacingDeadline() + c.resetPacingDeadline() } if sendMode != ackhandler.SendAny { dontSendMore = true @@ -2132,7 +2583,7 @@ func (s *connection) sendPacketsWithGSO(now time.Time) error { } // Don't send more packets in this batch if they require a different ECN marking than the previous ones. - nextECN := s.sentPacketHandler.ECNMode(true) + nextECN := c.sentPacketHandler.ECNMode(true) // Append another packet if // 1. The congestion controller and pacer allow sending more @@ -2143,21 +2594,21 @@ func (s *connection) sendPacketsWithGSO(now time.Time) error { continue } - s.sendQueue.Send(buf, uint16(maxSize), ecn) + c.sendQueue.Send(buf, uint16(maxSize), ecn) if dontSendMore { return nil } - if s.sendQueue.WouldBlock() { + if c.sendQueue.WouldBlock() { return nil } // Prioritize receiving of packets over sending out more packets. - s.receivedPacketMx.Lock() - hasPackets := !s.receivedPackets.Empty() - s.receivedPacketMx.Unlock() + c.receivedPacketMx.Lock() + hasPackets := !c.receivedPackets.Empty() + c.receivedPacketMx.Unlock() if hasPackets { - s.pacingDeadline = deadlineSendImmediately + c.pacingDeadline = deadlineSendImmediately return nil } @@ -2166,42 +2617,42 @@ func (s *connection) sendPacketsWithGSO(now time.Time) error { } } -func (s *connection) resetPacingDeadline() { - deadline := s.sentPacketHandler.TimeUntilSend() +func (c *Conn) resetPacingDeadline() { + deadline := c.sentPacketHandler.TimeUntilSend() if deadline.IsZero() { deadline = deadlineSendImmediately } - s.pacingDeadline = deadline + c.pacingDeadline = deadline } -func (s *connection) maybeSendAckOnlyPacket(now time.Time) error { - if !s.handshakeConfirmed { - ecn := s.sentPacketHandler.ECNMode(false) - packet, err := s.packer.PackCoalescedPacket(true, s.maxPacketSize(), now, s.version) +func (c *Conn) maybeSendAckOnlyPacket(now monotime.Time) error { + if !c.handshakeConfirmed { + ecn := c.sentPacketHandler.ECNMode(false) + packet, err := c.packer.PackCoalescedPacket(true, c.maxPacketSize(), now, c.version) if err != nil { return err } if packet == nil { return nil } - return s.sendPackedCoalescedPacket(packet, ecn, now) + return c.sendPackedCoalescedPacket(packet, ecn, now) } - ecn := s.sentPacketHandler.ECNMode(true) - p, buf, err := s.packer.PackAckOnlyPacket(s.maxPacketSize(), now, s.version) + ecn := c.sentPacketHandler.ECNMode(true) + p, buf, err := c.packer.PackAckOnlyPacket(c.maxPacketSize(), now, c.version) if err != nil { if err == errNothingToPack { return nil } return err } - s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, buf.Len(), false) - s.registerPackedShortHeaderPacket(p, ecn, now) - s.sendQueue.Send(buf, 0, ecn) + c.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, buf.Len(), false) + c.registerPackedShortHeaderPacket(p, ecn, now) + c.sendQueue.Send(buf, 0, ecn) return nil } -func (s *connection) sendProbePacket(sendMode ackhandler.SendMode, now time.Time) error { +func (c *Conn) sendProbePacket(sendMode ackhandler.SendMode, now monotime.Time) error { var encLevel protocol.EncryptionLevel //nolint:exhaustive // We only need to handle the PTO send modes here. switch sendMode { @@ -2217,50 +2668,46 @@ func (s *connection) sendProbePacket(sendMode ackhandler.SendMode, now time.Time // Queue probe packets until we actually send out a packet, // or until there are no more packets to queue. var packet *coalescedPacket - for { - if wasQueued := s.sentPacketHandler.QueueProbePacket(encLevel); !wasQueued { + for packet == nil { + if wasQueued := c.sentPacketHandler.QueueProbePacket(encLevel); !wasQueued { break } var err error - packet, err = s.packer.MaybePackPTOProbePacket(encLevel, s.maxPacketSize(), now, s.version) + packet, err = c.packer.PackPTOProbePacket(encLevel, c.maxPacketSize(), false, now, c.version) if err != nil { return err } - if packet != nil { - break - } } if packet == nil { - s.retransmissionQueue.AddPing(encLevel) var err error - packet, err = s.packer.MaybePackPTOProbePacket(encLevel, s.maxPacketSize(), now, s.version) + packet, err = c.packer.PackPTOProbePacket(encLevel, c.maxPacketSize(), true, now, c.version) if err != nil { return err } } if packet == nil || (len(packet.longHdrPackets) == 0 && packet.shortHdrPacket == nil) { - return fmt.Errorf("connection BUG: couldn't pack %s probe packet", encLevel) + return fmt.Errorf("connection BUG: couldn't pack %s probe packet: %v", encLevel, packet) } - return s.sendPackedCoalescedPacket(packet, s.sentPacketHandler.ECNMode(packet.IsOnlyShortHeaderPacket()), now) + return c.sendPackedCoalescedPacket(packet, c.sentPacketHandler.ECNMode(packet.IsOnlyShortHeaderPacket()), now) } // appendOneShortHeaderPacket appends a new packet to the given packetBuffer. // If there was nothing to pack, the returned size is 0. -func (s *connection) appendOneShortHeaderPacket(buf *packetBuffer, maxSize protocol.ByteCount, ecn protocol.ECN, now time.Time) (protocol.ByteCount, error) { +func (c *Conn) appendOneShortHeaderPacket(buf *packetBuffer, maxSize protocol.ByteCount, ecn protocol.ECN, now monotime.Time) (protocol.ByteCount, error) { startLen := buf.Len() - p, err := s.packer.AppendPacket(buf, maxSize, now, s.version) + p, err := c.packer.AppendPacket(buf, maxSize, now, c.version) if err != nil { return 0, err } size := buf.Len() - startLen - s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, size, false) - s.registerPackedShortHeaderPacket(p, ecn, now) + c.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, size, false) + c.registerPackedShortHeaderPacket(p, ecn, now) return size, nil } -func (s *connection) registerPackedShortHeaderPacket(p shortHeaderPacket, ecn protocol.ECN, now time.Time) { +func (c *Conn) registerPackedShortHeaderPacket(p shortHeaderPacket, ecn protocol.ECN, now monotime.Time) { if p.IsPathProbePacket { - s.sentPacketHandler.SentPacket( + c.sentPacketHandler.SentPacket( now, p.PacketNumber, protocol.InvalidPacketNumber, @@ -2274,15 +2721,15 @@ func (s *connection) registerPackedShortHeaderPacket(p shortHeaderPacket, ecn pr ) return } - if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && (len(p.StreamFrames) > 0 || ackhandler.HasAckElicitingFrames(p.Frames)) { - s.firstAckElicitingPacketAfterIdleSentTime = now + if c.firstAckElicitingPacketAfterIdleSentTime.IsZero() && (len(p.StreamFrames) > 0 || ackhandler.HasAckElicitingFrames(p.Frames)) { + c.firstAckElicitingPacketAfterIdleSentTime = now } largestAcked := protocol.InvalidPacketNumber if p.Ack != nil { largestAcked = p.Ack.LargestAcked() } - s.sentPacketHandler.SentPacket( + c.sentPacketHandler.SentPacket( now, p.PacketNumber, largestAcked, @@ -2294,20 +2741,20 @@ func (s *connection) registerPackedShortHeaderPacket(p shortHeaderPacket, ecn pr p.IsPathMTUProbePacket, false, ) - s.connIDManager.SentPacket() + c.connIDManager.SentPacket() } -func (s *connection) sendPackedCoalescedPacket(packet *coalescedPacket, ecn protocol.ECN, now time.Time) error { - s.logCoalescedPacket(packet, ecn) +func (c *Conn) sendPackedCoalescedPacket(packet *coalescedPacket, ecn protocol.ECN, now monotime.Time) error { + c.logCoalescedPacket(packet, ecn) for _, p := range packet.longHdrPackets { - if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && p.IsAckEliciting() { - s.firstAckElicitingPacketAfterIdleSentTime = now + if c.firstAckElicitingPacketAfterIdleSentTime.IsZero() && p.IsAckEliciting() { + c.firstAckElicitingPacketAfterIdleSentTime = now } largestAcked := protocol.InvalidPacketNumber if p.ack != nil { largestAcked = p.ack.LargestAcked() } - s.sentPacketHandler.SentPacket( + c.sentPacketHandler.SentPacket( now, p.header.PacketNumber, largestAcked, @@ -2319,24 +2766,24 @@ func (s *connection) sendPackedCoalescedPacket(packet *coalescedPacket, ecn prot false, false, ) - if s.perspective == protocol.PerspectiveClient && p.EncryptionLevel() == protocol.EncryptionHandshake && - !s.droppedInitialKeys { + if c.perspective == protocol.PerspectiveClient && p.EncryptionLevel() == protocol.EncryptionHandshake && + !c.droppedInitialKeys { // On the client side, Initial keys are dropped as soon as the first Handshake packet is sent. // See Section 4.9.1 of RFC 9001. - if err := s.dropEncryptionLevel(protocol.EncryptionInitial, now); err != nil { + if err := c.dropEncryptionLevel(protocol.EncryptionInitial, now); err != nil { return err } } } if p := packet.shortHdrPacket; p != nil { - if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && p.IsAckEliciting() { - s.firstAckElicitingPacketAfterIdleSentTime = now + if c.firstAckElicitingPacketAfterIdleSentTime.IsZero() && p.IsAckEliciting() { + c.firstAckElicitingPacketAfterIdleSentTime = now } largestAcked := protocol.InvalidPacketNumber if p.Ack != nil { largestAcked = p.Ack.LargestAcked() } - s.sentPacketHandler.SentPacket( + c.sentPacketHandler.SentPacket( now, p.PacketNumber, largestAcked, @@ -2349,41 +2796,41 @@ func (s *connection) sendPackedCoalescedPacket(packet *coalescedPacket, ecn prot false, ) } - s.connIDManager.SentPacket() - s.sendQueue.Send(packet.buffer, 0, ecn) + c.connIDManager.SentPacket() + c.sendQueue.Send(packet.buffer, 0, ecn) return nil } -func (s *connection) sendConnectionClose(e error) ([]byte, error) { +func (c *Conn) sendConnectionClose(e error) ([]byte, error) { var packet *coalescedPacket var err error var transportErr *qerr.TransportError var applicationErr *qerr.ApplicationError if errors.As(e, &transportErr) { - packet, err = s.packer.PackConnectionClose(transportErr, s.maxPacketSize(), s.version) + packet, err = c.packer.PackConnectionClose(transportErr, c.maxPacketSize(), c.version) } else if errors.As(e, &applicationErr) { - packet, err = s.packer.PackApplicationClose(applicationErr, s.maxPacketSize(), s.version) + packet, err = c.packer.PackApplicationClose(applicationErr, c.maxPacketSize(), c.version) } else { - packet, err = s.packer.PackConnectionClose(&qerr.TransportError{ + packet, err = c.packer.PackConnectionClose(&qerr.TransportError{ ErrorCode: qerr.InternalError, ErrorMessage: fmt.Sprintf("connection BUG: unspecified error type (msg: %s)", e.Error()), - }, s.maxPacketSize(), s.version) + }, c.maxPacketSize(), c.version) } if err != nil { return nil, err } - ecn := s.sentPacketHandler.ECNMode(packet.IsOnlyShortHeaderPacket()) - s.logCoalescedPacket(packet, ecn) - return packet.buffer.Data, s.conn.Write(packet.buffer.Data, 0, ecn) + ecn := c.sentPacketHandler.ECNMode(packet.IsOnlyShortHeaderPacket()) + c.logCoalescedPacket(packet, ecn) + return packet.buffer.Data, c.conn.Write(packet.buffer.Data, 0, ecn) } -func (s *connection) maxPacketSize() protocol.ByteCount { - if s.mtuDiscoverer == nil { +func (c *Conn) maxPacketSize() protocol.ByteCount { + if c.mtuDiscoverer == nil { // Use the configured packet size on the client side. // If the server sends a max_udp_payload_size that's smaller than this size, we can ignore this: // Apparently the server still processed the (fully padded) Initial packet anyway. - if s.perspective == protocol.PerspectiveClient { - return protocol.ByteCount(s.config.InitialPacketSize) + if c.perspective == protocol.PerspectiveClient { + return protocol.ByteCount(c.config.InitialPacketSize) } // On the server side, there's no downside to using 1200 bytes until we received the client's transport // parameters: @@ -2392,109 +2839,150 @@ func (s *connection) maxPacketSize() protocol.ByteCount { // * If it did, we will have processed the transport parameters and initialized the MTU discoverer. return protocol.MinInitialPacketSize } - return s.mtuDiscoverer.CurrentSize() + return c.mtuDiscoverer.CurrentSize() } -// AcceptStream returns the next stream openend by the peer -func (s *connection) AcceptStream(ctx context.Context) (Stream, error) { - return s.streamsMap.AcceptStream(ctx) +// AcceptStream returns the next stream opened by the peer, blocking until one is available. +func (c *Conn) AcceptStream(ctx context.Context) (*Stream, error) { + return c.streamsMap.AcceptStream(ctx) } -func (s *connection) AcceptUniStream(ctx context.Context) (ReceiveStream, error) { - return s.streamsMap.AcceptUniStream(ctx) +// AcceptUniStream returns the next unidirectional stream opened by the peer, blocking until one is available. +func (c *Conn) AcceptUniStream(ctx context.Context) (*ReceiveStream, error) { + return c.streamsMap.AcceptUniStream(ctx) } -// OpenStream opens a stream -func (s *connection) OpenStream() (Stream, error) { - return s.streamsMap.OpenStream() +// OpenStream opens a new bidirectional QUIC stream. +// There is no signaling to the peer about new streams: +// The peer can only accept the stream after data has been sent on the stream, +// or the stream has been reset or closed. +// When reaching the peer's stream limit, it is not possible to open a new stream until the +// peer raises the stream limit. In that case, a [StreamLimitReachedError] is returned. +func (c *Conn) OpenStream() (*Stream, error) { + return c.streamsMap.OpenStream() } -func (s *connection) OpenStreamSync(ctx context.Context) (Stream, error) { - return s.streamsMap.OpenStreamSync(ctx) +// OpenStreamSync opens a new bidirectional QUIC stream. +// It blocks until a new stream can be opened. +// There is no signaling to the peer about new streams: +// The peer can only accept the stream after data has been sent on the stream, +// or the stream has been reset or closed. +func (c *Conn) OpenStreamSync(ctx context.Context) (*Stream, error) { + return c.streamsMap.OpenStreamSync(ctx) } -func (s *connection) OpenUniStream() (SendStream, error) { - return s.streamsMap.OpenUniStream() +// OpenUniStream opens a new outgoing unidirectional QUIC stream. +// There is no signaling to the peer about new streams: +// The peer can only accept the stream after data has been sent on the stream, +// or the stream has been reset or closed. +// When reaching the peer's stream limit, it is not possible to open a new stream until the +// peer raises the stream limit. In that case, a [StreamLimitReachedError] is returned. +func (c *Conn) OpenUniStream() (*SendStream, error) { + return c.streamsMap.OpenUniStream() } -func (s *connection) OpenUniStreamSync(ctx context.Context) (SendStream, error) { - return s.streamsMap.OpenUniStreamSync(ctx) +// OpenUniStreamSync opens a new outgoing unidirectional QUIC stream. +// It blocks until a new stream can be opened. +// There is no signaling to the peer about new streams: +// The peer can only accept the stream after data has been sent on the stream, +// or the stream has been reset or closed. +func (c *Conn) OpenUniStreamSync(ctx context.Context) (*SendStream, error) { + return c.streamsMap.OpenUniStreamSync(ctx) } -func (s *connection) newFlowController(id protocol.StreamID) flowcontrol.StreamFlowController { - initialSendWindow := s.peerParams.InitialMaxStreamDataUni +func (c *Conn) newFlowController(id protocol.StreamID) flowcontrol.StreamFlowController { + initialSendWindow := c.peerParams.InitialMaxStreamDataUni if id.Type() == protocol.StreamTypeBidi { - if id.InitiatedBy() == s.perspective { - initialSendWindow = s.peerParams.InitialMaxStreamDataBidiRemote + if id.InitiatedBy() == c.perspective { + initialSendWindow = c.peerParams.InitialMaxStreamDataBidiRemote } else { - initialSendWindow = s.peerParams.InitialMaxStreamDataBidiLocal + initialSendWindow = c.peerParams.InitialMaxStreamDataBidiLocal } } return flowcontrol.NewStreamFlowController( id, - s.connFlowController, - protocol.ByteCount(s.config.InitialStreamReceiveWindow), - protocol.ByteCount(s.config.MaxStreamReceiveWindow), + c.connFlowController, + protocol.ByteCount(c.config.InitialStreamReceiveWindow), + protocol.ByteCount(c.config.MaxStreamReceiveWindow), initialSendWindow, - s.rttStats, - s.logger, + c.rttStats, + c.logger, ) } // scheduleSending signals that we have data for sending -func (s *connection) scheduleSending() { +func (c *Conn) scheduleSending() { select { - case s.sendingScheduled <- struct{}{}: + case c.sendingScheduled <- struct{}{}: default: } } // tryQueueingUndecryptablePacket queues a packet for which we're missing the decryption keys. -// The logging.PacketType is only used for logging purposes. -func (s *connection) tryQueueingUndecryptablePacket(p receivedPacket, pt logging.PacketType) { - if s.handshakeComplete { +// The qlogevents.PacketType is only used for logging purposes. +func (c *Conn) tryQueueingUndecryptablePacket(p receivedPacket, pt qlog.PacketType) { + if c.handshakeComplete { panic("shouldn't queue undecryptable packets after handshake completion") } - if len(s.undecryptablePackets)+1 > protocol.MaxUndecryptablePackets { - if s.tracer != nil && s.tracer.DroppedPacket != nil { - s.tracer.DroppedPacket(pt, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropDOSPrevention) + if len(c.undecryptablePackets)+1 > protocol.MaxUndecryptablePackets { + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketDropped{ + Header: qlog.PacketHeader{ + PacketType: pt, + PacketNumber: protocol.InvalidPacketNumber, + }, + Raw: qlog.RawInfo{Length: int(p.Size())}, + Trigger: qlog.PacketDropDOSPrevention, + }) } - s.logger.Infof("Dropping undecryptable packet (%d bytes). Undecryptable packet queue full.", p.Size()) + c.logger.Infof("Dropping undecryptable packet (%d bytes). Undecryptable packet queue full.", p.Size()) return } - s.logger.Infof("Queueing packet (%d bytes) for later decryption", p.Size()) - if s.tracer != nil && s.tracer.BufferedPacket != nil { - s.tracer.BufferedPacket(pt, p.Size()) + c.logger.Infof("Queueing packet (%d bytes) for later decryption", p.Size()) + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.PacketBuffered{ + Header: qlog.PacketHeader{ + PacketType: pt, + PacketNumber: protocol.InvalidPacketNumber, + }, + Raw: qlog.RawInfo{Length: int(p.Size())}, + }) } - s.undecryptablePackets = append(s.undecryptablePackets, p) + c.undecryptablePackets = append(c.undecryptablePackets, p) } -func (s *connection) queueControlFrame(f wire.Frame) { - s.framer.QueueControlFrame(f) - s.scheduleSending() +func (c *Conn) queueControlFrame(f wire.Frame) { + c.framer.QueueControlFrame(f) + c.scheduleSending() } -func (s *connection) onHasConnectionData() { s.scheduleSending() } +func (c *Conn) onHasConnectionData() { c.scheduleSending() } -func (s *connection) onHasStreamData(id protocol.StreamID, str sendStreamI) { - s.framer.AddActiveStream(id, str) - s.scheduleSending() +func (c *Conn) onHasStreamData(id protocol.StreamID, str *SendStream) { + c.framer.AddActiveStream(id, str) + c.scheduleSending() } -func (s *connection) onHasStreamControlFrame(id protocol.StreamID, str streamControlFrameGetter) { - s.framer.AddStreamWithControlFrames(id, str) - s.scheduleSending() +func (c *Conn) onHasStreamControlFrame(id protocol.StreamID, str streamControlFrameGetter) { + c.framer.AddStreamWithControlFrames(id, str) + c.scheduleSending() } -func (s *connection) onStreamCompleted(id protocol.StreamID) { - if err := s.streamsMap.DeleteStream(id); err != nil { - s.closeLocal(err) +func (c *Conn) onStreamCompleted(id protocol.StreamID) { + if err := c.streamsMap.DeleteStream(id); err != nil { + c.closeLocal(err) } - s.framer.RemoveActiveStream(id) + c.framer.RemoveActiveStream(id) } -func (s *connection) SendDatagram(p []byte) error { - if !s.supportsDatagrams() { +// SendDatagram sends a message using a QUIC datagram, as specified in RFC 9221, +// if the peer enabled datagram support. +// There is no delivery guarantee for DATAGRAM frames, they are not retransmitted if lost. +// The payload of the datagram needs to fit into a single QUIC packet. +// In addition, a datagram may be dropped before being sent out if the available packet size suddenly decreases. +// If the payload is too large to be sent at the current time, a DatagramTooLargeError is returned. +func (c *Conn) SendDatagram(p []byte) error { + if !c.supportsDatagrams() { return errors.New("datagram support disabled") } @@ -2502,38 +2990,113 @@ func (s *connection) SendDatagram(p []byte) error { // The payload size estimate is conservative. // Under many circumstances we could send a few more bytes. maxDataLen := min( - f.MaxDataLen(s.peerParams.MaxDatagramFrameSize, s.version), - protocol.ByteCount(s.currentMTUEstimate.Load()), + f.MaxDataLen(c.peerParams.MaxDatagramFrameSize, c.version), + protocol.ByteCount(c.currentMTUEstimate.Load()), ) if protocol.ByteCount(len(p)) > maxDataLen { return &DatagramTooLargeError{MaxDatagramPayloadSize: int64(maxDataLen)} } f.Data = make([]byte, len(p)) copy(f.Data, p) - return s.datagramQueue.Add(f) + return c.datagramQueue.Add(f) } -func (s *connection) ReceiveDatagram(ctx context.Context) ([]byte, error) { - if !s.config.EnableDatagrams { +// ReceiveDatagram gets a message received in a QUIC datagram, as specified in RFC 9221. +func (c *Conn) ReceiveDatagram(ctx context.Context) ([]byte, error) { + if !c.config.EnableDatagrams { return nil, errors.New("datagram support disabled") } - return s.datagramQueue.Receive(ctx) + return c.datagramQueue.Receive(ctx) +} + +// LocalAddr returns the local address of the QUIC connection. +func (c *Conn) LocalAddr() net.Addr { return c.conn.LocalAddr() } + +// RemoteAddr returns the remote address of the QUIC connection. +func (c *Conn) RemoteAddr() net.Addr { return c.conn.RemoteAddr() } + +// getPathManager lazily initializes the Conn's pathManagerOutgoing. +// May create multiple pathManagerOutgoing objects if called concurrently. +func (c *Conn) getPathManager() *pathManagerOutgoing { + old := c.pathManagerOutgoing.Load() + if old != nil { + // Path manager is already initialized + return old + } + + // Initialize the path manager + new := newPathManagerOutgoing( + c.connIDManager.GetConnIDForPath, + c.connIDManager.RetireConnIDForPath, + c.scheduleSending, + ) + if c.pathManagerOutgoing.CompareAndSwap(old, new) { + return new + } + + // Swap failed. A concurrent writer wrote first, use their value. + return c.pathManagerOutgoing.Load() +} + +func (c *Conn) AddPath(t *Transport) (*Path, error) { + if c.perspective == protocol.PerspectiveServer { + return nil, errors.New("server cannot initiate connection migration") + } + if c.peerParams.DisableActiveMigration { + return nil, errors.New("server disabled connection migration") + } + if err := t.init(false); err != nil { + return nil, err + } + return c.getPathManager().NewPath( + t, + 200*time.Millisecond, // initial RTT estimate + func() { + runner := (*packetHandlerMap)(t) + c.connIDGenerator.AddConnRunner( + runner, + connRunnerCallbacks{ + AddConnectionID: func(connID protocol.ConnectionID) { runner.Add(connID, c) }, + RemoveConnectionID: runner.Remove, + ReplaceWithClosed: runner.ReplaceWithClosed, + }, + ) + }, + ), nil } -func (s *connection) LocalAddr() net.Addr { return s.conn.LocalAddr() } -func (s *connection) RemoteAddr() net.Addr { return s.conn.RemoteAddr() } +// HandshakeComplete blocks until the handshake completes (or fails). +// For the client, data sent before completion of the handshake is encrypted with 0-RTT keys. +// For the server, data sent before completion of the handshake is encrypted with 1-RTT keys, +// however the client's identity is only verified once the handshake completes. +func (c *Conn) HandshakeComplete() <-chan struct{} { + return c.handshakeCompleteChan +} + +// QlogTrace returns the qlog trace of the QUIC connection. +// It is nil if qlog is not enabled. +func (c *Conn) QlogTrace() qlogwriter.Trace { + return c.qlogTrace +} -func (s *connection) NextConnection(ctx context.Context) (Connection, error) { +// NextConnection transitions a connection to be usable after a 0-RTT rejection. +// It waits for the handshake to complete and then enables the connection for normal use. +// This should be called when the server rejects 0-RTT and the application receives +// [Err0RTTRejected] errors. +// +// Note that 0-RTT rejection invalidates all data sent in 0-RTT packets. It is the +// application's responsibility to handle this (for example by resending the data). +func (c *Conn) NextConnection(ctx context.Context) (*Conn, error) { // The handshake might fail after the server rejected 0-RTT. // This could happen if the Finished message is malformed or never received. select { case <-ctx.Done(): return nil, context.Cause(ctx) - case <-s.Context().Done(): - case <-s.HandshakeComplete(): - s.streamsMap.UseResetMaps() + case <-c.Context().Done(): + case <-c.HandshakeComplete(): + c.streamsMap.UseResetMaps() } - return s, nil + return c, nil } // estimateMaxPayloadSize estimates the maximum payload size for short header packets. diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/connection_logging.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/connection_logging.go index a314a6cd6..0c6221d5b 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/connection_logging.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/connection_logging.go @@ -1,46 +1,54 @@ package quic import ( + "net" + "net/netip" "slices" "github.com/quic-go/quic-go/internal/ackhandler" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" - "github.com/quic-go/quic-go/logging" + "github.com/quic-go/quic-go/qlog" ) // ConvertFrame converts a wire.Frame into a logging.Frame. // This makes it possible for external packages to access the frames. // Furthermore, it removes the data slices from CRYPTO and STREAM frames. -func toLoggingFrame(frame wire.Frame) logging.Frame { +func toQlogFrame(frame wire.Frame) qlog.Frame { switch f := frame.(type) { case *wire.AckFrame: // We use a pool for ACK frames. // Implementations of the tracer interface may hold on to frames, so we need to make a copy here. - return toLoggingAckFrame(f) + return qlog.Frame{Frame: toQlogAckFrame(f)} case *wire.CryptoFrame: - return &logging.CryptoFrame{ - Offset: f.Offset, - Length: protocol.ByteCount(len(f.Data)), + return qlog.Frame{ + Frame: &qlog.CryptoFrame{ + Offset: int64(f.Offset), + Length: int64(len(f.Data)), + }, } case *wire.StreamFrame: - return &logging.StreamFrame{ - StreamID: f.StreamID, - Offset: f.Offset, - Length: f.DataLen(), - Fin: f.Fin, + return qlog.Frame{ + Frame: &qlog.StreamFrame{ + StreamID: f.StreamID, + Offset: int64(f.Offset), + Length: int64(f.DataLen()), + Fin: f.Fin, + }, } case *wire.DatagramFrame: - return &logging.DatagramFrame{ - Length: logging.ByteCount(len(f.Data)), + return qlog.Frame{ + Frame: &qlog.DatagramFrame{ + Length: int64(len(f.Data)), + }, } default: - return logging.Frame(frame) + return qlog.Frame{Frame: frame} } } -func toLoggingAckFrame(f *wire.AckFrame) *logging.AckFrame { - ack := &logging.AckFrame{ +func toQlogAckFrame(f *wire.AckFrame) *qlog.AckFrame { + ack := &qlog.AckFrame{ AckRanges: slices.Clone(f.AckRanges), DelayTime: f.DelayTime, ECNCE: f.ECNCE, @@ -50,39 +58,57 @@ func toLoggingAckFrame(f *wire.AckFrame) *logging.AckFrame { return ack } -func (s *connection) logLongHeaderPacket(p *longHeaderPacket, ecn protocol.ECN) { +func (c *Conn) logLongHeaderPacket(p *longHeaderPacket, ecn protocol.ECN) { // quic-go logging - if s.logger.Debug() { - p.header.Log(s.logger) + if c.logger.Debug() { + p.header.Log(c.logger) if p.ack != nil { - wire.LogFrame(s.logger, p.ack, true) + wire.LogFrame(c.logger, p.ack, true) } for _, frame := range p.frames { - wire.LogFrame(s.logger, frame.Frame, true) + wire.LogFrame(c.logger, frame.Frame, true) } for _, frame := range p.streamFrames { - wire.LogFrame(s.logger, frame.Frame, true) + wire.LogFrame(c.logger, frame.Frame, true) } } // tracing - if s.tracer != nil && s.tracer.SentLongHeaderPacket != nil { - frames := make([]logging.Frame, 0, len(p.frames)) + if c.qlogger != nil { + numFrames := len(p.frames) + len(p.streamFrames) + if p.ack != nil { + numFrames++ + } + frames := make([]qlog.Frame, 0, numFrames) + if p.ack != nil { + frames = append(frames, toQlogFrame(p.ack)) + } for _, f := range p.frames { - frames = append(frames, toLoggingFrame(f.Frame)) + frames = append(frames, toQlogFrame(f.Frame)) } for _, f := range p.streamFrames { - frames = append(frames, toLoggingFrame(f.Frame)) - } - var ack *logging.AckFrame - if p.ack != nil { - ack = toLoggingAckFrame(p.ack) + frames = append(frames, toQlogFrame(f.Frame)) } - s.tracer.SentLongHeaderPacket(p.header, p.length, ecn, ack, frames) + c.qlogger.RecordEvent(qlog.PacketSent{ + Header: qlog.PacketHeader{ + PacketType: toQlogPacketType(p.header.Type), + KeyPhaseBit: p.header.KeyPhase, + PacketNumber: p.header.PacketNumber, + Version: p.header.Version, + SrcConnectionID: p.header.SrcConnectionID, + DestConnectionID: p.header.DestConnectionID, + }, + Raw: qlog.RawInfo{ + Length: int(p.length), + PayloadLength: int(p.header.Length), + }, + Frames: frames, + ECN: toQlogECN(ecn), + }) } } -func (s *connection) logShortHeaderPacket( +func (c *Conn) logShortHeaderPacket( destConnID protocol.ConnectionID, ackFrame *wire.AckFrame, frames []ackhandler.Frame, @@ -94,52 +120,63 @@ func (s *connection) logShortHeaderPacket( size protocol.ByteCount, isCoalesced bool, ) { - if s.logger.Debug() && !isCoalesced { - s.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, 1-RTT (ECN: %s)", pn, size, s.logID, ecn) + if c.logger.Debug() && !isCoalesced { + c.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, 1-RTT (ECN: %s)", pn, size, c.logID, ecn) } // quic-go logging - if s.logger.Debug() { - wire.LogShortHeader(s.logger, destConnID, pn, pnLen, kp) + if c.logger.Debug() { + wire.LogShortHeader(c.logger, destConnID, pn, pnLen, kp) if ackFrame != nil { - wire.LogFrame(s.logger, ackFrame, true) + wire.LogFrame(c.logger, ackFrame, true) } for _, f := range frames { - wire.LogFrame(s.logger, f.Frame, true) + wire.LogFrame(c.logger, f.Frame, true) } for _, f := range streamFrames { - wire.LogFrame(s.logger, f.Frame, true) + wire.LogFrame(c.logger, f.Frame, true) } } // tracing - if s.tracer != nil && s.tracer.SentShortHeaderPacket != nil { - fs := make([]logging.Frame, 0, len(frames)+len(streamFrames)) + if c.qlogger != nil { + numFrames := len(frames) + len(streamFrames) + if ackFrame != nil { + numFrames++ + } + fs := make([]qlog.Frame, 0, numFrames) + if ackFrame != nil { + fs = append(fs, toQlogFrame(ackFrame)) + } for _, f := range frames { - fs = append(fs, toLoggingFrame(f.Frame)) + fs = append(fs, toQlogFrame(f.Frame)) } for _, f := range streamFrames { - fs = append(fs, toLoggingFrame(f.Frame)) + fs = append(fs, toQlogFrame(f.Frame)) } - var ack *logging.AckFrame - if ackFrame != nil { - ack = toLoggingAckFrame(ackFrame) - } - s.tracer.SentShortHeaderPacket( - &logging.ShortHeader{DestConnectionID: destConnID, PacketNumber: pn, PacketNumberLen: pnLen, KeyPhase: kp}, - size, - ecn, - ack, - fs, - ) + c.qlogger.RecordEvent(qlog.PacketSent{ + Header: qlog.PacketHeader{ + PacketType: qlog.PacketType1RTT, + KeyPhaseBit: kp, + PacketNumber: pn, + Version: c.version, + DestConnectionID: destConnID, + }, + Raw: qlog.RawInfo{ + Length: int(size), + PayloadLength: int(size - wire.ShortHeaderLen(destConnID, pnLen)), + }, + Frames: fs, + ECN: toQlogECN(ecn), + }) } } -func (s *connection) logCoalescedPacket(packet *coalescedPacket, ecn protocol.ECN) { - if s.logger.Debug() { +func (c *Conn) logCoalescedPacket(packet *coalescedPacket, ecn protocol.ECN) { + if c.logger.Debug() { // There's a short period between dropping both Initial and Handshake keys and completion of the handshake, // during which we might call PackCoalescedPacket but just pack a short header packet. if len(packet.longHdrPackets) == 0 && packet.shortHdrPacket != nil { - s.logShortHeaderPacket( + c.logShortHeaderPacket( packet.shortHdrPacket.DestConnID, packet.shortHdrPacket.Ack, packet.shortHdrPacket.Frames, @@ -154,15 +191,132 @@ func (s *connection) logCoalescedPacket(packet *coalescedPacket, ecn protocol.EC return } if len(packet.longHdrPackets) > 1 { - s.logger.Debugf("-> Sending coalesced packet (%d parts, %d bytes) for connection %s", len(packet.longHdrPackets), packet.buffer.Len(), s.logID) + c.logger.Debugf("-> Sending coalesced packet (%d parts, %d bytes) for connection %s", len(packet.longHdrPackets), packet.buffer.Len(), c.logID) } else { - s.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, %s", packet.longHdrPackets[0].header.PacketNumber, packet.buffer.Len(), s.logID, packet.longHdrPackets[0].EncryptionLevel()) + c.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, %s", packet.longHdrPackets[0].header.PacketNumber, packet.buffer.Len(), c.logID, packet.longHdrPackets[0].EncryptionLevel()) } } for _, p := range packet.longHdrPackets { - s.logLongHeaderPacket(p, ecn) + c.logLongHeaderPacket(p, ecn) } if p := packet.shortHdrPacket; p != nil { - s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, p.Length, true) + c.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, p.Length, true) + } +} + +func (c *Conn) qlogTransportParameters(tp *wire.TransportParameters, sentBy protocol.Perspective, restore bool) { + ev := qlog.ParametersSet{ + Restore: restore, + OriginalDestinationConnectionID: tp.OriginalDestinationConnectionID, + InitialSourceConnectionID: tp.InitialSourceConnectionID, + RetrySourceConnectionID: tp.RetrySourceConnectionID, + StatelessResetToken: tp.StatelessResetToken, + DisableActiveMigration: tp.DisableActiveMigration, + MaxIdleTimeout: tp.MaxIdleTimeout, + MaxUDPPayloadSize: tp.MaxUDPPayloadSize, + AckDelayExponent: tp.AckDelayExponent, + MaxAckDelay: tp.MaxAckDelay, + ActiveConnectionIDLimit: tp.ActiveConnectionIDLimit, + InitialMaxData: tp.InitialMaxData, + InitialMaxStreamDataBidiLocal: tp.InitialMaxStreamDataBidiLocal, + InitialMaxStreamDataBidiRemote: tp.InitialMaxStreamDataBidiRemote, + InitialMaxStreamDataUni: tp.InitialMaxStreamDataUni, + InitialMaxStreamsBidi: int64(tp.MaxBidiStreamNum), + InitialMaxStreamsUni: int64(tp.MaxUniStreamNum), + MaxDatagramFrameSize: tp.MaxDatagramFrameSize, + EnableResetStreamAt: tp.EnableResetStreamAt, + } + if sentBy == c.perspective { + ev.Initiator = qlog.InitiatorLocal + } else { + ev.Initiator = qlog.InitiatorRemote + } + if tp.PreferredAddress != nil { + ev.PreferredAddress = &qlog.PreferredAddress{ + IPv4: tp.PreferredAddress.IPv4, + IPv6: tp.PreferredAddress.IPv6, + ConnectionID: tp.PreferredAddress.ConnectionID, + StatelessResetToken: tp.PreferredAddress.StatelessResetToken, + } + } + c.qlogger.RecordEvent(ev) +} + +func toQlogECN(ecn protocol.ECN) qlog.ECN { + //nolint:exhaustive // only need to handle the 3 valid values + switch ecn { + case protocol.ECT0: + return qlog.ECT0 + case protocol.ECT1: + return qlog.ECT1 + case protocol.ECNCE: + return qlog.ECNCE + default: + return qlog.ECNUnsupported + } +} + +func toQlogPacketType(pt protocol.PacketType) qlog.PacketType { + var qpt qlog.PacketType + switch pt { + case protocol.PacketTypeInitial: + qpt = qlog.PacketTypeInitial + case protocol.PacketTypeHandshake: + qpt = qlog.PacketTypeHandshake + case protocol.PacketType0RTT: + qpt = qlog.PacketType0RTT + case protocol.PacketTypeRetry: + qpt = qlog.PacketTypeRetry + } + return qpt +} + +func toPathEndpointInfo(addr *net.UDPAddr) qlog.PathEndpointInfo { + if addr == nil { + return qlog.PathEndpointInfo{} + } + + var info qlog.PathEndpointInfo + if addr.IP == nil || addr.IP.To4() != nil { + addrPort := netip.AddrPortFrom(netip.AddrFrom4([4]byte(addr.IP.To4())), uint16(addr.Port)) + if addrPort.IsValid() { + info.IPv4 = addrPort + } + } else { + addrPort := netip.AddrPortFrom(netip.AddrFrom16([16]byte(addr.IP.To16())), uint16(addr.Port)) + if addrPort.IsValid() { + info.IPv6 = addrPort + } + } + return info +} + +// startedConnectionEvent builds a StartedConnection event using consistent logic +// for both endpoints. If the local address is unspecified (e.g., dual-stack +// listener), it selects the family based on the remote address and uses the +// unspecified address of that family with the local port. +func startedConnectionEvent(local, remote *net.UDPAddr) qlog.StartedConnection { + var localInfo, remoteInfo qlog.PathEndpointInfo + if remote != nil { + remoteInfo = toPathEndpointInfo(remote) + } + if local != nil { + if local.IP == nil || local.IP.IsUnspecified() { + // Choose local family based on the remote address family. + if remote != nil && remote.IP.To4() != nil { + ap := netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), uint16(local.Port)) + if ap.IsValid() { + localInfo.IPv4 = ap + } + } else if remote != nil && remote.IP.To16() != nil && remote.IP.To4() == nil { + ap := netip.AddrPortFrom(netip.AddrFrom16([16]byte{}), uint16(local.Port)) + if ap.IsValid() { + localInfo.IPv6 = ap + } + } + } else { + localInfo = toPathEndpointInfo(local) + } } + return qlog.StartedConnection{Local: localInfo, Remote: remoteInfo} } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/connection_timer.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/connection_timer.go deleted file mode 100644 index 171fdd013..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/connection_timer.go +++ /dev/null @@ -1,51 +0,0 @@ -package quic - -import ( - "time" - - "github.com/quic-go/quic-go/internal/utils" -) - -var deadlineSendImmediately = time.Time{}.Add(42 * time.Millisecond) // any value > time.Time{} and before time.Now() is fine - -type connectionTimer struct { - timer *utils.Timer - last time.Time -} - -func newTimer() *connectionTimer { - return &connectionTimer{timer: utils.NewTimer()} -} - -func (t *connectionTimer) SetRead() { - if deadline := t.timer.Deadline(); deadline != deadlineSendImmediately { - t.last = deadline - } - t.timer.SetRead() -} - -func (t *connectionTimer) Chan() <-chan time.Time { - return t.timer.Chan() -} - -// SetTimer resets the timer. -// It makes sure that the deadline is strictly increasing. -// This prevents busy-looping in cases where the timer fires, but we can't actually send out a packet. -// This doesn't apply to the pacing deadline, which can be set multiple times to deadlineSendImmediately. -func (t *connectionTimer) SetTimer(idleTimeoutOrKeepAlive, ackAlarm, lossTime, pacing time.Time) { - deadline := idleTimeoutOrKeepAlive - if !ackAlarm.IsZero() && ackAlarm.Before(deadline) && ackAlarm.After(t.last) { - deadline = ackAlarm - } - if !lossTime.IsZero() && lossTime.Before(deadline) && lossTime.After(t.last) { - deadline = lossTime - } - if !pacing.IsZero() && pacing.Before(deadline) { - deadline = pacing - } - t.timer.Reset(deadline) -} - -func (t *connectionTimer) Stop() { - t.timer.Stop() -} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/crypto_stream.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/crypto_stream.go index 9a387baa4..6d39aa09e 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/crypto_stream.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/crypto_stream.go @@ -1,14 +1,23 @@ package quic import ( + "errors" "fmt" + "io" + "os" + "slices" + "strconv" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/wire" ) -type cryptoStream struct { +const disableClientHelloScramblingEnv = "QUIC_GO_DISABLE_CLIENTHELLO_SCRAMBLING" + +// The baseCryptoStream is used by the cryptoStream and the initialCryptoStream. +// This allows us to implement different logic for PopCryptoFrame for the two streams. +type baseCryptoStream struct { queue frameSorter highestOffset protocol.ByteCount @@ -19,10 +28,10 @@ type cryptoStream struct { } func newCryptoStream() *cryptoStream { - return &cryptoStream{queue: *newFrameSorter()} + return &cryptoStream{baseCryptoStream{queue: *newFrameSorter()}} } -func (s *cryptoStream) HandleCryptoFrame(f *wire.CryptoFrame) error { +func (s *baseCryptoStream) HandleCryptoFrame(f *wire.CryptoFrame) error { highestOffset := f.Offset + protocol.ByteCount(len(f.Data)) if maxOffset := highestOffset; maxOffset > protocol.MaxCryptoStreamOffset { return &qerr.TransportError{ @@ -47,12 +56,12 @@ func (s *cryptoStream) HandleCryptoFrame(f *wire.CryptoFrame) error { } // GetCryptoData retrieves data that was received in CRYPTO frames -func (s *cryptoStream) GetCryptoData() []byte { +func (s *baseCryptoStream) GetCryptoData() []byte { _, data, _ := s.queue.Pop() return data } -func (s *cryptoStream) Finish() error { +func (s *baseCryptoStream) Finish() error { if s.queue.HasMoreData() { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, @@ -64,20 +73,177 @@ func (s *cryptoStream) Finish() error { } // Writes writes data that should be sent out in CRYPTO frames -func (s *cryptoStream) Write(p []byte) (int, error) { +func (s *baseCryptoStream) Write(p []byte) (int, error) { s.writeBuf = append(s.writeBuf, p...) return len(p), nil } -func (s *cryptoStream) HasData() bool { +func (s *baseCryptoStream) HasData() bool { return len(s.writeBuf) > 0 } -func (s *cryptoStream) PopCryptoFrame(maxLen protocol.ByteCount) *wire.CryptoFrame { +func (s *baseCryptoStream) PopCryptoFrame(maxLen protocol.ByteCount) *wire.CryptoFrame { f := &wire.CryptoFrame{Offset: s.writeOffset} n := min(f.MaxDataLen(maxLen), protocol.ByteCount(len(s.writeBuf))) + if n <= 0 { + return nil + } f.Data = s.writeBuf[:n] s.writeBuf = s.writeBuf[n:] s.writeOffset += n return f } + +type cryptoStream struct { + baseCryptoStream +} + +type clientHelloCut struct { + start protocol.ByteCount + end protocol.ByteCount +} + +type initialCryptoStream struct { + baseCryptoStream + + scramble bool + end protocol.ByteCount + cuts [2]clientHelloCut +} + +func newInitialCryptoStream(isClient bool) *initialCryptoStream { + var scramble bool + if isClient { + disabled, err := strconv.ParseBool(os.Getenv(disableClientHelloScramblingEnv)) + scramble = err != nil || !disabled + } + s := &initialCryptoStream{ + baseCryptoStream: baseCryptoStream{queue: *newFrameSorter()}, + scramble: scramble, + } + for i := range len(s.cuts) { + s.cuts[i].start = protocol.InvalidByteCount + s.cuts[i].end = protocol.InvalidByteCount + } + return s +} + +func (s *initialCryptoStream) HasData() bool { + // The ClientHello might be written in multiple parts. + // In order to correctly split the ClientHello, we need the entire ClientHello has been queued. + if s.scramble && s.writeOffset == 0 && s.cuts[0].start == protocol.InvalidByteCount { + return false + } + return s.baseCryptoStream.HasData() +} + +func (s *initialCryptoStream) Write(p []byte) (int, error) { + s.writeBuf = append(s.writeBuf, p...) + if !s.scramble { + return len(p), nil + } + if s.cuts[0].start == protocol.InvalidByteCount { + sniPos, sniLen, echPos, err := findSNIAndECH(s.writeBuf) + if errors.Is(err, io.ErrUnexpectedEOF) { + return len(p), nil + } + if err != nil { + return len(p), err + } + if sniPos == -1 && echPos == -1 { + // Neither SNI nor ECH found. + // There's nothing to scramble. + s.scramble = false + return len(p), nil + } + s.end = protocol.ByteCount(len(s.writeBuf)) + s.cuts[0].start = protocol.ByteCount(sniPos + sniLen/2) // right in the middle + s.cuts[0].end = protocol.ByteCount(sniPos + sniLen) + if echPos > 0 { + // ECH extension found, cut the ECH extension type value (a uint16) in half + start := protocol.ByteCount(echPos + 1) + s.cuts[1].start = start + // cut somewhere (16 bytes), most likely in the ECH extension value + s.cuts[1].end = min(start+16, s.end) + } + slices.SortFunc(s.cuts[:], func(a, b clientHelloCut) int { + if a.start == protocol.InvalidByteCount { + return 1 + } + if a.start > b.start { + return 1 + } + return -1 + }) + } + return len(p), nil +} + +func (s *initialCryptoStream) PopCryptoFrame(maxLen protocol.ByteCount) *wire.CryptoFrame { + if !s.scramble { + return s.baseCryptoStream.PopCryptoFrame(maxLen) + } + + // send out the skipped parts + if s.writeOffset == s.end { + var foundCuts bool + var f *wire.CryptoFrame + for i, c := range s.cuts { + if c.start == protocol.InvalidByteCount { + continue + } + foundCuts = true + if f != nil { + break + } + f = &wire.CryptoFrame{Offset: c.start} + n := min(f.MaxDataLen(maxLen), c.end-c.start) + if n <= 0 { + return nil + } + f.Data = s.writeBuf[c.start : c.start+n] + s.cuts[i].start += n + if s.cuts[i].start == c.end { + s.cuts[i].start = protocol.InvalidByteCount + s.cuts[i].end = protocol.InvalidByteCount + foundCuts = false + } + } + if !foundCuts { + // no more cuts found, we're done sending out everything up until s.end + s.writeBuf = s.writeBuf[s.end:] + s.end = protocol.InvalidByteCount + s.scramble = false + } + return f + } + + nextCut := clientHelloCut{start: protocol.InvalidByteCount, end: protocol.InvalidByteCount} + for _, c := range s.cuts { + if c.start == protocol.InvalidByteCount { + continue + } + if c.start > s.writeOffset { + nextCut = c + break + } + } + f := &wire.CryptoFrame{Offset: s.writeOffset} + maxOffset := nextCut.start + if maxOffset == protocol.InvalidByteCount { + maxOffset = s.end + } + n := min(f.MaxDataLen(maxLen), maxOffset-s.writeOffset) + if n <= 0 { + return nil + } + f.Data = s.writeBuf[s.writeOffset : s.writeOffset+n] + // Don't reslice the writeBuf yet. + // This is done once all parts have been sent out. + s.writeOffset += n + if s.writeOffset == nextCut.start { + s.writeOffset = nextCut.end + } + + return f +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/crypto_stream_manager.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/crypto_stream_manager.go index d70b9b007..1e7dbf747 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/crypto_stream_manager.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/crypto_stream_manager.go @@ -8,13 +8,13 @@ import ( ) type cryptoStreamManager struct { - initialStream *cryptoStream + initialStream *initialCryptoStream handshakeStream *cryptoStream oneRTTStream *cryptoStream } func newCryptoStreamManager( - initialStream *cryptoStream, + initialStream *initialCryptoStream, handshakeStream *cryptoStream, oneRTTStream *cryptoStream, ) *cryptoStreamManager { @@ -26,35 +26,31 @@ func newCryptoStreamManager( } func (m *cryptoStreamManager) HandleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error { - var str *cryptoStream //nolint:exhaustive // CRYPTO frames cannot be sent in 0-RTT packets. switch encLevel { case protocol.EncryptionInitial: - str = m.initialStream + return m.initialStream.HandleCryptoFrame(frame) case protocol.EncryptionHandshake: - str = m.handshakeStream + return m.handshakeStream.HandleCryptoFrame(frame) case protocol.Encryption1RTT: - str = m.oneRTTStream + return m.oneRTTStream.HandleCryptoFrame(frame) default: return fmt.Errorf("received CRYPTO frame with unexpected encryption level: %s", encLevel) } - return str.HandleCryptoFrame(frame) } func (m *cryptoStreamManager) GetCryptoData(encLevel protocol.EncryptionLevel) []byte { - var str *cryptoStream //nolint:exhaustive // CRYPTO frames cannot be sent in 0-RTT packets. switch encLevel { case protocol.EncryptionInitial: - str = m.initialStream + return m.initialStream.GetCryptoData() case protocol.EncryptionHandshake: - str = m.handshakeStream + return m.handshakeStream.GetCryptoData() case protocol.Encryption1RTT: - str = m.oneRTTStream + return m.oneRTTStream.GetCryptoData() default: panic(fmt.Sprintf("received CRYPTO frame with unexpected encryption level: %s", encLevel)) } - return str.GetCryptoData() } func (m *cryptoStreamManager) GetPostHandshakeData(maxSize protocol.ByteCount) *wire.CryptoFrame { diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/errors.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/errors.go index 4a69a7f19..829730fec 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/errors.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/errors.go @@ -7,42 +7,72 @@ import ( ) type ( - TransportError = qerr.TransportError - ApplicationError = qerr.ApplicationError + // TransportError indicates an error that occurred on the QUIC transport layer. + // Every transport error other than CONNECTION_REFUSED and APPLICATION_ERROR is + // likely a bug in the implementation. + TransportError = qerr.TransportError + // ApplicationError is an application-defined error. + ApplicationError = qerr.ApplicationError + // VersionNegotiationError indicates a failure to negotiate a QUIC version. VersionNegotiationError = qerr.VersionNegotiationError - StatelessResetError = qerr.StatelessResetError - IdleTimeoutError = qerr.IdleTimeoutError - HandshakeTimeoutError = qerr.HandshakeTimeoutError + // StatelessResetError indicates a stateless reset was received. + // This can happen when the peer reboots, or when packets are misrouted. + // See section 10.3 of RFC 9000 for details. + StatelessResetError = qerr.StatelessResetError + // IdleTimeoutError indicates that the connection timed out because it was inactive for too long. + IdleTimeoutError = qerr.IdleTimeoutError + // HandshakeTimeoutError indicates that the connection timed out before completing the handshake. + HandshakeTimeoutError = qerr.HandshakeTimeoutError ) type ( - TransportErrorCode = qerr.TransportErrorCode + // TransportErrorCode is a QUIC transport error code, see section 20 of RFC 9000. + TransportErrorCode = qerr.TransportErrorCode + // ApplicationErrorCode is an QUIC application error code. ApplicationErrorCode = qerr.ApplicationErrorCode - StreamErrorCode = qerr.StreamErrorCode + // StreamErrorCode is a QUIC stream error code. The meaning of the value is defined by the application. + StreamErrorCode = qerr.StreamErrorCode ) const ( - NoError = qerr.NoError - InternalError = qerr.InternalError - ConnectionRefused = qerr.ConnectionRefused - FlowControlError = qerr.FlowControlError - StreamLimitError = qerr.StreamLimitError - StreamStateError = qerr.StreamStateError - FinalSizeError = qerr.FinalSizeError - FrameEncodingError = qerr.FrameEncodingError - TransportParameterError = qerr.TransportParameterError - ConnectionIDLimitError = qerr.ConnectionIDLimitError - ProtocolViolation = qerr.ProtocolViolation - InvalidToken = qerr.InvalidToken + // NoError is the NO_ERROR transport error code. + NoError = qerr.NoError + // InternalError is the INTERNAL_ERROR transport error code. + InternalError = qerr.InternalError + // ConnectionRefused is the CONNECTION_REFUSED transport error code. + ConnectionRefused = qerr.ConnectionRefused + // FlowControlError is the FLOW_CONTROL_ERROR transport error code. + FlowControlError = qerr.FlowControlError + // StreamLimitError is the STREAM_LIMIT_ERROR transport error code. + StreamLimitError = qerr.StreamLimitError + // StreamStateError is the STREAM_STATE_ERROR transport error code. + StreamStateError = qerr.StreamStateError + // FinalSizeError is the FINAL_SIZE_ERROR transport error code. + FinalSizeError = qerr.FinalSizeError + // FrameEncodingError is the FRAME_ENCODING_ERROR transport error code. + FrameEncodingError = qerr.FrameEncodingError + // TransportParameterError is the TRANSPORT_PARAMETER_ERROR transport error code. + TransportParameterError = qerr.TransportParameterError + // ConnectionIDLimitError is the CONNECTION_ID_LIMIT_ERROR transport error code. + ConnectionIDLimitError = qerr.ConnectionIDLimitError + // ProtocolViolation is the PROTOCOL_VIOLATION transport error code. + ProtocolViolation = qerr.ProtocolViolation + // InvalidToken is the INVALID_TOKEN transport error code. + InvalidToken = qerr.InvalidToken + // ApplicationErrorErrorCode is the APPLICATION_ERROR transport error code. ApplicationErrorErrorCode = qerr.ApplicationErrorErrorCode - CryptoBufferExceeded = qerr.CryptoBufferExceeded - KeyUpdateError = qerr.KeyUpdateError - AEADLimitReached = qerr.AEADLimitReached - NoViablePathError = qerr.NoViablePathError + // CryptoBufferExceeded is the CRYPTO_BUFFER_EXCEEDED transport error code. + CryptoBufferExceeded = qerr.CryptoBufferExceeded + // KeyUpdateError is the KEY_UPDATE_ERROR transport error code. + KeyUpdateError = qerr.KeyUpdateError + // AEADLimitReached is the AEAD_LIMIT_REACHED transport error code. + AEADLimitReached = qerr.AEADLimitReached + // NoViablePathError is the NO_VIABLE_PATH_ERROR transport error code. + NoViablePathError = qerr.NoViablePathError ) -// A StreamError is used for Stream.CancelRead and Stream.CancelWrite. -// It is also returned from Stream.Read and Stream.Write if the peer canceled reading or writing. +// A StreamError is used to signal stream cancellations. +// It is returned from the Read and Write methods of the [ReceiveStream], [SendStream] and [Stream]. type StreamError struct { StreamID StreamID ErrorCode StreamErrorCode @@ -62,7 +92,7 @@ func (e *StreamError) Error() string { return fmt.Sprintf("stream %d canceled by %s with error code %d", e.StreamID, pers, e.ErrorCode) } -// DatagramTooLargeError is returned from Connection.SendDatagram if the payload is too large to be sent. +// DatagramTooLargeError is returned from Conn.SendDatagram if the payload is too large to be sent. type DatagramTooLargeError struct { MaxDatagramPayloadSize int64 } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/framer.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/framer.go index fee316315..ca23f0aac 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/framer.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/framer.go @@ -3,10 +3,10 @@ package quic import ( "slices" "sync" - "time" "github.com/quic-go/quic-go/internal/ackhandler" "github.com/quic-go/quic-go/internal/flowcontrol" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils/ringbuffer" "github.com/quic-go/quic-go/internal/wire" @@ -22,14 +22,18 @@ const ( // (which is the RESET_STREAM frame). const maxStreamControlFrameSize = 25 +type streamFrameGetter interface { + popStreamFrame(protocol.ByteCount, protocol.Version) (ackhandler.StreamFrame, *wire.StreamDataBlockedFrame, bool) +} + type streamControlFrameGetter interface { - getControlFrame(time.Time) (_ ackhandler.Frame, ok, hasMore bool) + getControlFrame(monotime.Time) (_ ackhandler.Frame, ok, hasMore bool) } type framer struct { mutex sync.Mutex - activeStreams map[protocol.StreamID]sendStreamI + activeStreams map[protocol.StreamID]streamFrameGetter streamQueue ringbuffer.RingBuffer[protocol.StreamID] streamsWithControlFrames map[protocol.StreamID]streamControlFrameGetter @@ -42,7 +46,7 @@ type framer struct { func newFramer(connFlowController flowcontrol.ConnectionFlowController) *framer { return &framer{ - activeStreams: make(map[protocol.StreamID]sendStreamI), + activeStreams: make(map[protocol.StreamID]streamFrameGetter), streamsWithControlFrames: make(map[protocol.StreamID]streamControlFrameGetter), connFlowController: connFlowController, } @@ -86,7 +90,7 @@ func (f *framer) Append( frames []ackhandler.Frame, streamFrames []ackhandler.StreamFrame, maxLen protocol.ByteCount, - now time.Time, + now monotime.Time, v protocol.Version, ) ([]ackhandler.Frame, []ackhandler.StreamFrame, protocol.ByteCount) { f.controlFrameMutex.Lock() @@ -153,7 +157,7 @@ func (f *framer) Append( func (f *framer) appendControlFrames( frames []ackhandler.Frame, maxLen protocol.ByteCount, - now time.Time, + now monotime.Time, v protocol.Version, ) ([]ackhandler.Frame, protocol.ByteCount) { var length protocol.ByteCount @@ -214,7 +218,7 @@ func (f *framer) QueuedTooManyControlFrames() bool { return f.queuedTooManyControlFrames } -func (f *framer) AddActiveStream(id protocol.StreamID, str sendStreamI) { +func (f *framer) AddActiveStream(id protocol.StreamID, str streamFrameGetter) { f.mutex.Lock() if _, ok := f.activeStreams[id]; !ok { f.streamQueue.PushBack(id) diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/body.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/body.go index 698275fe8..356f726a8 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/body.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/body.go @@ -9,24 +9,24 @@ import ( "github.com/quic-go/quic-go" ) -// A Hijacker allows hijacking of the stream creating part of a quic.Session from a http.Response.Body. +// A Hijacker allows hijacking of the stream creating part of a quic.Conn from a http.ResponseWriter. // It is used by WebTransport to create WebTransport streams after a session has been established. type Hijacker interface { - Connection() Connection + Connection() *Conn } var errTooMuchData = errors.New("peer sent too much data") // The body is used in the requestBody (for a http.Request) and the responseBody (for a http.Response). type body struct { - str *stream + str *Stream remainingContentLength int64 violatedContentLength bool hasContentLength bool } -func newBody(str *stream, contentLength int64) *body { +func newBody(str *Stream, contentLength int64) *body { b := &body{str: str} if contentLength >= 0 { b.hasContentLength = true @@ -81,7 +81,7 @@ type requestBody struct { var _ io.ReadCloser = &requestBody{} -func newRequestBody(str *stream, contentLength int64, connCtx context.Context, rcvdSettings <-chan struct{}, getSettings func() *Settings) *requestBody { +func newRequestBody(str *Stream, contentLength int64, connCtx context.Context, rcvdSettings <-chan struct{}, getSettings func() *Settings) *requestBody { return &requestBody{ body: *newBody(str, contentLength), connCtx: connCtx, @@ -102,7 +102,7 @@ type hijackableBody struct { var _ io.ReadCloser = &hijackableBody{} -func newResponseBody(str *stream, contentLength int64, done chan<- struct{}) *hijackableBody { +func newResponseBody(str *Stream, contentLength int64, done chan<- struct{}) *hijackableBody { return &hijackableBody{ body: *newBody(str, contentLength), reqDone: done, diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/capsule.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/capsule.go index 132e6c32e..224ff2300 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/capsule.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/capsule.go @@ -24,29 +24,16 @@ func (r *exactReader) Read(b []byte) (int, error) { return n, err } -type countingByteReader struct { - io.ByteReader - Read int -} - -func (r *countingByteReader) ReadByte() (byte, error) { - b, err := r.ByteReader.ReadByte() - if err == nil { - r.Read++ - } - return b, err -} - // ParseCapsule parses the header of a Capsule. // It returns an io.Reader that can be used to read the Capsule value. // The Capsule value must be read entirely (i.e. until the io.EOF) before using r again. func ParseCapsule(r quicvarint.Reader) (CapsuleType, io.Reader, error) { - cbr := countingByteReader{ByteReader: r} + cbr := countingByteReader{Reader: r} ct, err := quicvarint.Read(&cbr) if err != nil { // If an io.EOF is returned without consuming any bytes, return it unmodified. // Otherwise, return an io.ErrUnexpectedEOF. - if err == io.EOF && cbr.Read > 0 { + if err == io.EOF && cbr.NumRead > 0 { return 0, nil, io.ErrUnexpectedEOF } return 0, nil, err diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/client.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/client.go index 9214bb806..1b8a16f35 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/client.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/client.go @@ -6,13 +6,14 @@ import ( "fmt" "io" "log/slog" + "maps" "net/http" "net/http/httptrace" "net/textproto" "time" "github.com/quic-go/quic-go" - "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/http3/qlog" "github.com/quic-go/quic-go/quicvarint" "github.com/quic-go/qpack" @@ -32,6 +33,13 @@ const ( defaultMaxResponseHeaderBytes = 10 * 1 << 20 // 10 MB ) +type errConnUnusable struct{ e error } + +func (e *errConnUnusable) Unwrap() error { return e.e } +func (e *errConnUnusable) Error() string { return fmt.Sprintf("http3: conn unusable: %s", e.e.Error()) } + +const max1xxResponses = 5 // arbitrary bound on number of informational responses + var defaultQuicConfig = &quic.Config{ MaxIncomingStreams: -1, // don't allow the server to create bidirectional streams KeepAlivePeriod: 10 * time.Second, @@ -39,7 +47,7 @@ var defaultQuicConfig = &quic.Config{ // ClientConn is an HTTP/3 client doing requests to a single remote server. type ClientConn struct { - connection + conn *Conn // Enable support for HTTP/3 datagrams (RFC 9297). // If a QUICConfig is set, datagram support also needs to be enabled on the QUIC layer by setting enableDatagrams. @@ -51,7 +59,7 @@ type ClientConn struct { // maxResponseHeaderBytes specifies a limit on how many response bytes are // allowed in the server's response header. - maxResponseHeaderBytes uint64 + maxResponseHeaderBytes int // disableCompression, if true, prevents the Transport from requesting compression with an // "Accept-Encoding: gzip" request header when the Request contains no existing Accept-Encoding value. @@ -68,17 +76,13 @@ type ClientConn struct { var _ http.RoundTripper = &ClientConn{} -// Deprecated: SingleDestinationRoundTripper was renamed to ClientConn. -// It can be obtained by calling NewClientConn on a Transport. -type SingleDestinationRoundTripper = ClientConn - func newClientConn( - conn quic.Connection, + conn *quic.Conn, enableDatagrams bool, additionalSettings map[uint64]uint64, - streamHijacker func(FrameType, quic.ConnectionTracingID, quic.Stream, error) (hijacked bool, err error), - uniStreamHijacker func(StreamType, quic.ConnectionTracingID, quic.ReceiveStream, error) (hijacked bool), - maxResponseHeaderBytes int64, + streamHijacker func(FrameType, quic.ConnectionTracingID, *quic.Stream, error) (hijacked bool, err error), + uniStreamHijacker func(StreamType, quic.ConnectionTracingID, *quic.ReceiveStream, error) (hijacked bool), + maxResponseHeaderBytes int, disableCompression bool, logger *slog.Logger, ) *ClientConn { @@ -91,15 +95,15 @@ func newClientConn( if maxResponseHeaderBytes <= 0 { c.maxResponseHeaderBytes = defaultMaxResponseHeaderBytes } else { - c.maxResponseHeaderBytes = uint64(maxResponseHeaderBytes) + c.maxResponseHeaderBytes = maxResponseHeaderBytes } - c.decoder = qpack.NewDecoder(func(hf qpack.HeaderField) {}) + c.decoder = qpack.NewDecoder() c.requestWriter = newRequestWriter() - c.connection = *newConnection( + c.conn = newConnection( conn.Context(), conn, c.enableDatagrams, - protocol.PerspectiveClient, + false, // client c.logger, 0, ) @@ -109,38 +113,56 @@ func newClientConn( if c.logger != nil { c.logger.Debug("Setting up connection failed", "error", err) } - c.connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeInternalError), "") + c.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeInternalError), "") } }() if streamHijacker != nil { go c.handleBidirectionalStreams(streamHijacker) } - go c.connection.handleUnidirectionalStreams(uniStreamHijacker) + go c.conn.handleUnidirectionalStreams(uniStreamHijacker) return c } // OpenRequestStream opens a new request stream on the HTTP/3 connection. -func (c *ClientConn) OpenRequestStream(ctx context.Context) (RequestStream, error) { - return c.connection.openRequestStream(ctx, c.requestWriter, nil, c.disableCompression, c.maxResponseHeaderBytes) +func (c *ClientConn) OpenRequestStream(ctx context.Context) (*RequestStream, error) { + return c.conn.openRequestStream(ctx, c.requestWriter, nil, c.disableCompression, c.maxResponseHeaderBytes) } func (c *ClientConn) setupConn() error { // open the control stream - str, err := c.connection.OpenUniStream() + str, err := c.conn.OpenUniStream() if err != nil { return err } b := make([]byte, 0, 64) b = quicvarint.Append(b, streamTypeControlStream) // send the SETTINGS frame - b = (&settingsFrame{Datagram: c.enableDatagrams, Other: c.additionalSettings}).Append(b) + b = (&settingsFrame{ + Datagram: c.enableDatagrams, + Other: c.additionalSettings, + MaxFieldSectionSize: int64(c.maxResponseHeaderBytes), + }).Append(b) + if c.conn.qlogger != nil { + sf := qlog.SettingsFrame{ + MaxFieldSectionSize: int64(c.maxResponseHeaderBytes), + Other: maps.Clone(c.additionalSettings), + } + if c.enableDatagrams { + sf.Datagram = pointer(true) + } + c.conn.qlogger.RecordEvent(qlog.FrameCreated{ + StreamID: str.StreamID(), + Raw: qlog.RawInfo{Length: len(b)}, + Frame: qlog.Frame{Frame: sf}, + }) + } _, err = str.Write(b) return err } -func (c *ClientConn) handleBidirectionalStreams(streamHijacker func(FrameType, quic.ConnectionTracingID, quic.Stream, error) (hijacked bool, err error)) { +func (c *ClientConn) handleBidirectionalStreams(streamHijacker func(FrameType, quic.ConnectionTracingID, *quic.Stream, error) (hijacked bool, err error)) { for { - str, err := c.connection.AcceptStream(context.Background()) + str, err := c.conn.conn.AcceptStream(context.Background()) if err != nil { if c.logger != nil { c.logger.Debug("accepting bidirectional stream failed", "error", err) @@ -148,15 +170,15 @@ func (c *ClientConn) handleBidirectionalStreams(streamHijacker func(FrameType, q return } fp := &frameParser{ - r: str, - conn: &c.connection, + r: str, + closeConn: c.conn.CloseWithError, unknownFrameHandler: func(ft FrameType, e error) (processed bool, err error) { - id := c.connection.Context().Value(quic.ConnectionTracingKey).(quic.ConnectionTracingID) + id := c.conn.Context().Value(quic.ConnectionTracingKey).(quic.ConnectionTracingID) return streamHijacker(ft, id, str, e) }, } go func() { - if _, err := fp.ParseNext(); err == errHijacked { + if _, err := fp.ParseNext(c.conn.qlogger); err == errHijacked { return } if err != nil { @@ -164,7 +186,7 @@ func (c *ClientConn) handleBidirectionalStreams(streamHijacker func(FrameType, q c.logger.Debug("error handling stream", "error", err) } } - c.connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "received HTTP/3 frame on bidirectional stream") + c.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "received HTTP/3 frame on bidirectional stream") }() } } @@ -194,33 +216,30 @@ func (c *ClientConn) roundTrip(req *http.Request) (*http.Response, error) { req.Method = http.MethodHead default: // wait for the handshake to complete - earlyConn, ok := c.Connection.(quic.EarlyConnection) - if ok { - select { - case <-earlyConn.HandshakeComplete(): - case <-req.Context().Done(): - return nil, req.Context().Err() - } + select { + case <-c.conn.HandshakeComplete(): + case <-req.Context().Done(): + return nil, req.Context().Err() } } // It is only possible to send an Extended CONNECT request once the SETTINGS were received. // See section 3 of RFC 8441. if isExtendedConnectRequest(req) { - connCtx := c.Connection.Context() + connCtx := c.conn.Context() // wait for the server's SETTINGS frame to arrive select { - case <-c.connection.ReceivedSettings(): + case <-c.conn.ReceivedSettings(): case <-connCtx.Done(): return nil, context.Cause(connCtx) } - if !c.connection.Settings().EnableExtendedConnect { + if !c.conn.Settings().EnableExtendedConnect { return nil, errors.New("http3: server didn't enable Extended CONNECT") } } reqDone := make(chan struct{}) - str, err := c.connection.openRequestStream( + str, err := c.conn.openRequestStream( req.Context(), c.requestWriter, reqDone, @@ -228,7 +247,7 @@ func (c *ClientConn) roundTrip(req *http.Request) (*http.Response, error) { c.maxResponseHeaderBytes, ) if err != nil { - return nil, err + return nil, &errConnUnusable{e: err} } // Request Cancellation: @@ -254,11 +273,34 @@ func (c *ClientConn) roundTrip(req *http.Request) (*http.Response, error) { return rsp, maybeReplaceError(err) } +// ReceivedSettings returns a channel that is closed once the server's HTTP/3 settings were received. +// Settings can be obtained from the Settings method after the channel was closed. +func (c *ClientConn) ReceivedSettings() <-chan struct{} { + return c.conn.ReceivedSettings() +} + +// Settings returns the HTTP/3 settings for this connection. +// It is only valid to call this function after the channel returned by ReceivedSettings was closed. +func (c *ClientConn) Settings() *Settings { + return c.conn.Settings() +} + +// CloseWithError closes the connection with the given error code and message. +// It is invalid to call this function after the connection was closed. +func (c *ClientConn) CloseWithError(code ErrCode, msg string) error { + return c.conn.CloseWithError(quic.ApplicationErrorCode(code), msg) +} + +// Context returns a context that is cancelled when the connection is closed. +func (c *ClientConn) Context() context.Context { + return c.conn.Context() +} + // cancelingReader reads from the io.Reader. // It cancels writing on the stream if any error other than io.EOF occurs. type cancelingReader struct { r io.Reader - str Stream + str *RequestStream } func (r *cancelingReader) Read(b []byte) (int, error) { @@ -269,7 +311,7 @@ func (r *cancelingReader) Read(b []byte) (int, error) { return n, err } -func (c *ClientConn) sendRequestBody(str Stream, body io.ReadCloser, contentLength int64) error { +func (c *ClientConn) sendRequestBody(str *RequestStream, body io.ReadCloser, contentLength int64) error { defer body.Close() buf := make([]byte, bodyCopyBufferSize) sr := &cancelingReader{str: str, r: body} @@ -293,39 +335,43 @@ func (c *ClientConn) sendRequestBody(str Stream, body io.ReadCloser, contentLeng return err } -func (c *ClientConn) doRequest(req *http.Request, str *requestStream) (*http.Response, error) { +func (c *ClientConn) doRequest(req *http.Request, str *RequestStream) (*http.Response, error) { trace := httptrace.ContextClientTrace(req.Context()) - if err := str.SendRequestHeader(req); err != nil { + var sendingReqFailed bool + if err := str.sendRequestHeader(req); err != nil { traceWroteRequest(trace, err) - return nil, err + if c.logger != nil { + c.logger.Debug("error writing request", "error", err) + } + sendingReqFailed = true } - if req.Body == nil { - traceWroteRequest(trace, nil) - str.Close() - } else { - // send the request body asynchronously - go func() { - contentLength := int64(-1) - // According to the documentation for http.Request.ContentLength, - // a value of 0 with a non-nil Body is also treated as unknown content length. - if req.ContentLength > 0 { - contentLength = req.ContentLength - } - err := c.sendRequestBody(str, req.Body, contentLength) - traceWroteRequest(trace, err) - if err != nil { - if c.logger != nil { - c.logger.Debug("error writing request", "error", err) - } - } + if !sendingReqFailed { + if req.Body == nil { + traceWroteRequest(trace, nil) str.Close() - }() + } else { + // send the request body asynchronously + go func() { + contentLength := int64(-1) + // According to the documentation for http.Request.ContentLength, + // a value of 0 with a non-nil Body is also treated as unknown content length. + if req.ContentLength > 0 { + contentLength = req.ContentLength + } + err := c.sendRequestBody(str, req.Body, contentLength) + traceWroteRequest(trace, err) + if err != nil { + if c.logger != nil { + c.logger.Debug("error writing request", "error", err) + } + } + str.Close() + }() + } } // copy from net/http: support 1xx responses - num1xx := 0 // number of informational 1xx headers received - const max1xxResponses = 5 // arbitrary bound on number of informational responses - + var num1xx int // number of informational 1xx headers received var res *http.Response for { var err error @@ -340,18 +386,27 @@ func (c *ClientConn) doRequest(req *http.Request, str *requestStream) (*http.Res if is1xxNonTerminal { num1xx++ if num1xx > max1xxResponses { - return nil, errors.New("http: too many 1xx informational responses") + str.CancelRead(quic.StreamErrorCode(ErrCodeExcessiveLoad)) + str.CancelWrite(quic.StreamErrorCode(ErrCodeExcessiveLoad)) + return nil, errors.New("http3: too many 1xx informational responses") } traceGot1xxResponse(trace, resCode, textproto.MIMEHeader(res.Header)) - if resCode == 100 { + if resCode == http.StatusContinue { traceGot100Continue(trace) } continue } break } - connState := c.connection.ConnectionState().TLS + connState := c.conn.ConnectionState().TLS res.TLS = &connState res.Request = req return res, nil } + +// Conn returns the underlying HTTP/3 connection. +// This method is only useful for advanced use cases, such as when the application needs to +// open streams on the HTTP/3 connection (e.g. WebTransport). +func (c *ClientConn) Conn() *Conn { + return c.conn +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/conn.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/conn.go index bb17a5e5f..edadbc234 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/conn.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/conn.go @@ -2,6 +2,7 @@ package http3 import ( "context" + "errors" "fmt" "io" "log/slog" @@ -13,71 +14,74 @@ import ( "time" "github.com/quic-go/quic-go" - "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/http3/qlog" + "github.com/quic-go/quic-go/qlogwriter" "github.com/quic-go/quic-go/quicvarint" "github.com/quic-go/qpack" ) -// Connection is an HTTP/3 connection. -// It has all methods from the quic.Connection expect for AcceptStream, AcceptUniStream, +const maxQuarterStreamID = 1<<60 - 1 + +var errGoAway = errors.New("connection in graceful shutdown") + +// invalidStreamID is a stream ID that is invalid. The first valid stream ID in QUIC is 0. +const invalidStreamID = quic.StreamID(-1) + +// Conn is an HTTP/3 connection. +// It has all methods from the quic.Conn expect for AcceptStream, AcceptUniStream, // SendDatagram and ReceiveDatagram. -type Connection interface { - OpenStream() (quic.Stream, error) - OpenStreamSync(context.Context) (quic.Stream, error) - OpenUniStream() (quic.SendStream, error) - OpenUniStreamSync(context.Context) (quic.SendStream, error) - LocalAddr() net.Addr - RemoteAddr() net.Addr - CloseWithError(quic.ApplicationErrorCode, string) error - Context() context.Context - ConnectionState() quic.ConnectionState - - // ReceivedSettings returns a channel that is closed once the client's SETTINGS frame was received. - ReceivedSettings() <-chan struct{} - // Settings returns the settings received on this connection. - Settings() *Settings -} +type Conn struct { + conn *quic.Conn -type connection struct { - quic.Connection ctx context.Context - perspective protocol.Perspective - logger *slog.Logger + isServer bool + logger *slog.Logger enableDatagrams bool decoder *qpack.Decoder - streamMx sync.Mutex - streams map[protocol.StreamID]*datagrammer + streamMx sync.Mutex + streams map[quic.StreamID]*stateTrackingStream + lastStreamID quic.StreamID + maxStreamID quic.StreamID settings *Settings receivedSettings chan struct{} idleTimeout time.Duration idleTimer *time.Timer + + qlogger qlogwriter.Recorder } func newConnection( ctx context.Context, - quicConn quic.Connection, + quicConn *quic.Conn, enableDatagrams bool, - perspective protocol.Perspective, + isServer bool, logger *slog.Logger, idleTimeout time.Duration, -) *connection { - c := &connection{ +) *Conn { + var qlogger qlogwriter.Recorder + if qlogTrace := quicConn.QlogTrace(); qlogTrace != nil && qlogTrace.SupportsSchemas(qlog.EventSchema) { + qlogger = qlogTrace.AddProducer() + } + c := &Conn{ ctx: ctx, - Connection: quicConn, - perspective: perspective, + conn: quicConn, + isServer: isServer, logger: logger, idleTimeout: idleTimeout, enableDatagrams: enableDatagrams, - decoder: qpack.NewDecoder(func(hf qpack.HeaderField) {}), + decoder: qpack.NewDecoder(), receivedSettings: make(chan struct{}), - streams: make(map[protocol.StreamID]*datagrammer), + streams: make(map[quic.StreamID]*stateTrackingStream), + maxStreamID: invalidStreamID, + lastStreamID: invalidStreamID, + qlogger: qlogger, } if idleTimeout > 0 { c.idleTimer = time.AfterFunc(idleTimeout, c.onIdleTimer) @@ -85,11 +89,43 @@ func newConnection( return c } -func (c *connection) onIdleTimer() { +func (c *Conn) OpenStream() (*quic.Stream, error) { + return c.conn.OpenStream() +} + +func (c *Conn) OpenStreamSync(ctx context.Context) (*quic.Stream, error) { + return c.conn.OpenStreamSync(ctx) +} + +func (c *Conn) OpenUniStream() (*quic.SendStream, error) { + return c.conn.OpenUniStream() +} + +func (c *Conn) OpenUniStreamSync(ctx context.Context) (*quic.SendStream, error) { + return c.conn.OpenUniStreamSync(ctx) +} + +func (c *Conn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *Conn) RemoteAddr() net.Addr { + return c.conn.RemoteAddr() +} + +func (c *Conn) HandshakeComplete() <-chan struct{} { + return c.conn.HandshakeComplete() +} + +func (c *Conn) ConnectionState() quic.ConnectionState { + return c.conn.ConnectionState() +} + +func (c *Conn) onIdleTimer() { c.CloseWithError(quic.ApplicationErrorCode(ErrCodeNoError), "idle timeout") } -func (c *connection) clearStream(id quic.StreamID) { +func (c *Conn) clearStream(id quic.StreamID) { c.streamMx.Lock() defer c.streamMx.Unlock() @@ -97,82 +133,119 @@ func (c *connection) clearStream(id quic.StreamID) { if c.idleTimeout > 0 && len(c.streams) == 0 { c.idleTimer.Reset(c.idleTimeout) } + // The server is performing a graceful shutdown. + // If no more streams are remaining, close the connection. + if c.maxStreamID != invalidStreamID { + if len(c.streams) == 0 { + c.CloseWithError(quic.ApplicationErrorCode(ErrCodeNoError), "") + } + } } -func (c *connection) openRequestStream( +func (c *Conn) openRequestStream( ctx context.Context, requestWriter *requestWriter, reqDone chan<- struct{}, disableCompression bool, - maxHeaderBytes uint64, -) (*requestStream, error) { - str, err := c.Connection.OpenStreamSync(ctx) + maxHeaderBytes int, +) (*RequestStream, error) { + c.streamMx.Lock() + maxStreamID := c.maxStreamID + var nextStreamID quic.StreamID + if c.lastStreamID == invalidStreamID { + nextStreamID = 0 + } else { + nextStreamID = c.lastStreamID + 4 + } + c.streamMx.Unlock() + // Streams with stream ID equal to or greater than the stream ID carried in the GOAWAY frame + // will be rejected, see section 5.2 of RFC 9114. + if maxStreamID != invalidStreamID && nextStreamID >= maxStreamID { + return nil, errGoAway + } + + str, err := c.OpenStreamSync(ctx) if err != nil { return nil, err } - datagrams := newDatagrammer(func(b []byte) error { return c.sendDatagram(str.StreamID(), b) }) + hstr := newStateTrackingStream(str, c, func(b []byte) error { return c.sendDatagram(str.StreamID(), b) }) c.streamMx.Lock() - c.streams[str.StreamID()] = datagrams + c.streams[str.StreamID()] = hstr + c.lastStreamID = str.StreamID() c.streamMx.Unlock() - qstr := newStateTrackingStream(str, c, datagrams) rsp := &http.Response{} - hstr := newStream(qstr, c, datagrams, func(r io.Reader, l uint64) error { - hdr, err := c.decodeTrailers(r, l, maxHeaderBytes) - if err != nil { - return err - } - rsp.Trailer = hdr - return nil - }) trace := httptrace.ContextClientTrace(ctx) - return newRequestStream(hstr, requestWriter, reqDone, c.decoder, disableCompression, maxHeaderBytes, rsp, trace), nil + return newRequestStream( + newStream(hstr, c, trace, func(r io.Reader, hf *headersFrame) error { + hdr, err := c.decodeTrailers(r, str.StreamID(), hf, maxHeaderBytes) + if err != nil { + return err + } + rsp.Trailer = hdr + return nil + }, c.qlogger), + requestWriter, + reqDone, + c.decoder, + disableCompression, + maxHeaderBytes, + rsp, + ), nil } -func (c *connection) decodeTrailers(r io.Reader, l, maxHeaderBytes uint64) (http.Header, error) { - if l > maxHeaderBytes { - return nil, fmt.Errorf("HEADERS frame too large: %d bytes (max: %d)", l, maxHeaderBytes) +func (c *Conn) decodeTrailers(r io.Reader, streamID quic.StreamID, hf *headersFrame, maxHeaderBytes int) (http.Header, error) { + if hf.Length > uint64(maxHeaderBytes) { + maybeQlogInvalidHeadersFrame(c.qlogger, streamID, hf.Length) + return nil, fmt.Errorf("http3: HEADERS frame too large: %d bytes (max: %d)", hf.Length, maxHeaderBytes) } - b := make([]byte, l) + b := make([]byte, hf.Length) if _, err := io.ReadFull(r, b); err != nil { return nil, err } - fields, err := c.decoder.DecodeFull(b) + decodeFn := c.decoder.Decode(b) + var fields []qpack.HeaderField + if c.qlogger != nil { + fields = make([]qpack.HeaderField, 0, 16) + } + trailers, err := parseTrailers(decodeFn, &fields) if err != nil { + maybeQlogInvalidHeadersFrame(c.qlogger, streamID, hf.Length) return nil, err } - return parseTrailers(fields) + if c.qlogger != nil { + qlogParsedHeadersFrame(c.qlogger, streamID, hf, fields) + } + return trailers, nil } -func (c *connection) acceptStream(ctx context.Context) (quic.Stream, *datagrammer, error) { - str, err := c.AcceptStream(ctx) +// only used by the server +func (c *Conn) acceptStream(ctx context.Context) (*stateTrackingStream, error) { + str, err := c.conn.AcceptStream(ctx) if err != nil { - return nil, nil, err + return nil, err } - datagrams := newDatagrammer(func(b []byte) error { return c.sendDatagram(str.StreamID(), b) }) - if c.perspective == protocol.PerspectiveServer { - strID := str.StreamID() - c.streamMx.Lock() - c.streams[strID] = datagrams - if c.idleTimeout > 0 { - if len(c.streams) == 1 { - c.idleTimer.Stop() - } + strID := str.StreamID() + hstr := newStateTrackingStream(str, c, func(b []byte) error { return c.sendDatagram(strID, b) }) + c.streamMx.Lock() + c.streams[strID] = hstr + if c.idleTimeout > 0 { + if len(c.streams) == 1 { + c.idleTimer.Stop() } - c.streamMx.Unlock() - str = newStateTrackingStream(str, c, datagrams) } - return str, datagrams, nil + c.streamMx.Unlock() + return hstr, nil } -func (c *connection) CloseWithError(code quic.ApplicationErrorCode, msg string) error { +func (c *Conn) CloseWithError(code quic.ApplicationErrorCode, msg string) error { if c.idleTimer != nil { c.idleTimer.Stop() } - return c.Connection.CloseWithError(code, msg) + return c.conn.CloseWithError(code, msg) } -func (c *connection) handleUnidirectionalStreams(hijack func(StreamType, quic.ConnectionTracingID, quic.ReceiveStream, error) (hijacked bool)) { +func (c *Conn) handleUnidirectionalStreams(hijack func(StreamType, quic.ConnectionTracingID, *quic.ReceiveStream, error) (hijacked bool)) { var ( rcvdControlStr atomic.Bool rcvdQPACKEncoderStr atomic.Bool @@ -180,7 +253,7 @@ func (c *connection) handleUnidirectionalStreams(hijack func(StreamType, quic.Co ) for { - str, err := c.Connection.AcceptUniStream(context.Background()) + str, err := c.conn.AcceptUniStream(context.Background()) if err != nil { if c.logger != nil { c.logger.Debug("accepting unidirectional stream failed", "error", err) @@ -188,10 +261,10 @@ func (c *connection) handleUnidirectionalStreams(hijack func(StreamType, quic.Co return } - go func(str quic.ReceiveStream) { + go func(str *quic.ReceiveStream) { streamType, err := quicvarint.Read(quicvarint.NewReader(str)) if err != nil { - id := c.Connection.Context().Value(quic.ConnectionTracingKey).(quic.ConnectionTracingID) + id := c.Context().Value(quic.ConnectionTracingKey).(quic.ConnectionTracingID) if hijack != nil && hijack(StreamType(streamType), id, str, err) { return } @@ -205,31 +278,30 @@ func (c *connection) handleUnidirectionalStreams(hijack func(StreamType, quic.Co case streamTypeControlStream: case streamTypeQPACKEncoderStream: if isFirst := rcvdQPACKEncoderStr.CompareAndSwap(false, true); !isFirst { - c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeStreamCreationError), "duplicate QPACK encoder stream") + c.CloseWithError(quic.ApplicationErrorCode(ErrCodeStreamCreationError), "duplicate QPACK encoder stream") } // Our QPACK implementation doesn't use the dynamic table yet. return case streamTypeQPACKDecoderStream: if isFirst := rcvdQPACKDecoderStr.CompareAndSwap(false, true); !isFirst { - c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeStreamCreationError), "duplicate QPACK decoder stream") + c.CloseWithError(quic.ApplicationErrorCode(ErrCodeStreamCreationError), "duplicate QPACK decoder stream") } // Our QPACK implementation doesn't use the dynamic table yet. return case streamTypePushStream: - switch c.perspective { - case protocol.PerspectiveClient: - // we never increased the Push ID, so we don't expect any push streams - c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeIDError), "") - case protocol.PerspectiveServer: + if c.isServer { // only the server can push - c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeStreamCreationError), "") + c.CloseWithError(quic.ApplicationErrorCode(ErrCodeStreamCreationError), "") + } else { + // we never increased the Push ID, so we don't expect any push streams + c.CloseWithError(quic.ApplicationErrorCode(ErrCodeIDError), "") } return default: if hijack != nil { if hijack( StreamType(streamType), - c.Connection.Context().Value(quic.ConnectionTracingKey).(quic.ConnectionTracingID), + c.Context().Value(quic.ConnectionTracingKey).(quic.ConnectionTracingID), str, nil, ) { @@ -241,89 +313,160 @@ func (c *connection) handleUnidirectionalStreams(hijack func(StreamType, quic.Co } // Only a single control stream is allowed. if isFirstControlStr := rcvdControlStr.CompareAndSwap(false, true); !isFirstControlStr { - c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeStreamCreationError), "duplicate control stream") - return - } - fp := &frameParser{conn: c.Connection, r: str} - f, err := fp.ParseNext() - if err != nil { - c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameError), "") + c.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeStreamCreationError), "duplicate control stream") return } - sf, ok := f.(*settingsFrame) - if !ok { - c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeMissingSettings), "") - return - } - c.settings = &Settings{ - EnableDatagrams: sf.Datagram, - EnableExtendedConnect: sf.ExtendedConnect, - Other: sf.Other, - } - close(c.receivedSettings) - if !sf.Datagram { - return + c.handleControlStream(str) + }(str) + } +} + +func (c *Conn) handleControlStream(str *quic.ReceiveStream) { + fp := &frameParser{closeConn: c.conn.CloseWithError, r: str, streamID: str.StreamID()} + f, err := fp.ParseNext(c.qlogger) + if err != nil { + var serr *quic.StreamError + if err == io.EOF || errors.As(err, &serr) { + c.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeClosedCriticalStream), "") + return + } + c.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameError), "") + return + } + sf, ok := f.(*settingsFrame) + if !ok { + c.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeMissingSettings), "") + return + } + c.settings = &Settings{ + EnableDatagrams: sf.Datagram, + EnableExtendedConnect: sf.ExtendedConnect, + Other: sf.Other, + } + close(c.receivedSettings) + if sf.Datagram { + // If datagram support was enabled on our side as well as on the server side, + // we can expect it to have been negotiated both on the transport and on the HTTP/3 layer. + // Note: ConnectionState() will block until the handshake is complete (relevant when using 0-RTT). + if c.enableDatagrams && !c.ConnectionState().SupportsDatagrams { + c.CloseWithError(quic.ApplicationErrorCode(ErrCodeSettingsError), "missing QUIC Datagram support") + return + } + go func() { + if err := c.receiveDatagrams(); err != nil { + if c.logger != nil { + c.logger.Debug("receiving datagrams failed", "error", err) + } } - // If datagram support was enabled on our side as well as on the server side, - // we can expect it to have been negotiated both on the transport and on the HTTP/3 layer. - // Note: ConnectionState() will block until the handshake is complete (relevant when using 0-RTT). - if c.enableDatagrams && !c.Connection.ConnectionState().SupportsDatagrams { - c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeSettingsError), "missing QUIC Datagram support") + }() + } + + // we don't support server push, hence we don't expect any GOAWAY frames from the client + if c.isServer { + return + } + + for { + f, err := fp.ParseNext(c.qlogger) + if err != nil { + var serr *quic.StreamError + if err == io.EOF || errors.As(err, &serr) { + c.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeClosedCriticalStream), "") return } - go func() { - if err := c.receiveDatagrams(); err != nil { - if c.logger != nil { - c.logger.Debug("receiving datagrams failed", "error", err) - } - } - }() - }(str) + c.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameError), "") + return + } + // GOAWAY is the only frame allowed at this point: + // * unexpected frames are ignored by the frame parser + // * we don't support any extension that might add support for more frames + goaway, ok := f.(*goAwayFrame) + if !ok { + c.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "") + return + } + if goaway.StreamID%4 != 0 { // client-initiated, bidirectional streams + c.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeIDError), "") + return + } + c.streamMx.Lock() + if c.maxStreamID != invalidStreamID && goaway.StreamID > c.maxStreamID { + c.streamMx.Unlock() + c.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeIDError), "") + return + } + c.maxStreamID = goaway.StreamID + hasActiveStreams := len(c.streams) > 0 + c.streamMx.Unlock() + + // immediately close the connection if there are currently no active requests + if !hasActiveStreams { + c.CloseWithError(quic.ApplicationErrorCode(ErrCodeNoError), "") + return + } } } -func (c *connection) sendDatagram(streamID protocol.StreamID, b []byte) error { +func (c *Conn) sendDatagram(streamID quic.StreamID, b []byte) error { // TODO: this creates a lot of garbage and an additional copy data := make([]byte, 0, len(b)+8) + quarterStreamID := uint64(streamID / 4) data = quicvarint.Append(data, uint64(streamID/4)) data = append(data, b...) - return c.Connection.SendDatagram(data) + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.DatagramCreated{ + QuaterStreamID: quarterStreamID, + Raw: qlog.RawInfo{ + Length: len(data), + PayloadLength: len(b), + }, + }) + } + return c.conn.SendDatagram(data) } -func (c *connection) receiveDatagrams() error { +func (c *Conn) receiveDatagrams() error { for { - b, err := c.Connection.ReceiveDatagram(context.Background()) + b, err := c.conn.ReceiveDatagram(context.Background()) if err != nil { return err } quarterStreamID, n, err := quicvarint.Parse(b) if err != nil { - c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeDatagramError), "") + c.CloseWithError(quic.ApplicationErrorCode(ErrCodeDatagramError), "") return fmt.Errorf("could not read quarter stream id: %w", err) } + if c.qlogger != nil { + c.qlogger.RecordEvent(qlog.DatagramParsed{ + QuaterStreamID: quarterStreamID, + Raw: qlog.RawInfo{ + Length: len(b), + PayloadLength: len(b) - n, + }, + }) + } if quarterStreamID > maxQuarterStreamID { - c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeDatagramError), "") + c.CloseWithError(quic.ApplicationErrorCode(ErrCodeDatagramError), "") return fmt.Errorf("invalid quarter stream id: %w", err) } - streamID := protocol.StreamID(4 * quarterStreamID) + streamID := quic.StreamID(4 * quarterStreamID) c.streamMx.Lock() dg, ok := c.streams[streamID] + c.streamMx.Unlock() if !ok { - c.streamMx.Unlock() - return nil + continue } - c.streamMx.Unlock() - dg.enqueue(b[n:]) + dg.enqueueDatagram(b[n:]) } } // ReceivedSettings returns a channel that is closed once the peer's SETTINGS frame was received. // Settings can be optained from the Settings method after the channel was closed. -func (c *connection) ReceivedSettings() <-chan struct{} { return c.receivedSettings } +func (c *Conn) ReceivedSettings() <-chan struct{} { return c.receivedSettings } // Settings returns the settings received on this connection. // It is only valid to call this function after the channel returned by ReceivedSettings was closed. -func (c *connection) Settings() *Settings { return c.settings } +func (c *Conn) Settings() *Settings { return c.settings } // Context returns the context of the underlying QUIC connection. -func (c *connection) Context() context.Context { return c.ctx } +func (c *Conn) Context() context.Context { return c.ctx } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/datagram.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/datagram.go deleted file mode 100644 index 6d570e6b0..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/datagram.go +++ /dev/null @@ -1,98 +0,0 @@ -package http3 - -import ( - "context" - "sync" -) - -const maxQuarterStreamID = 1<<60 - 1 - -const streamDatagramQueueLen = 32 - -type datagrammer struct { - sendDatagram func([]byte) error - - hasData chan struct{} - queue [][]byte // TODO: use a ring buffer - - mx sync.Mutex - sendErr error - receiveErr error -} - -func newDatagrammer(sendDatagram func([]byte) error) *datagrammer { - return &datagrammer{ - sendDatagram: sendDatagram, - hasData: make(chan struct{}, 1), - } -} - -func (d *datagrammer) SetReceiveError(err error) { - d.mx.Lock() - defer d.mx.Unlock() - - d.receiveErr = err - d.signalHasData() -} - -func (d *datagrammer) SetSendError(err error) { - d.mx.Lock() - defer d.mx.Unlock() - - d.sendErr = err -} - -func (d *datagrammer) Send(b []byte) error { - d.mx.Lock() - sendErr := d.sendErr - d.mx.Unlock() - if sendErr != nil { - return sendErr - } - - return d.sendDatagram(b) -} - -func (d *datagrammer) signalHasData() { - select { - case d.hasData <- struct{}{}: - default: - } -} - -func (d *datagrammer) enqueue(data []byte) { - d.mx.Lock() - defer d.mx.Unlock() - - if d.receiveErr != nil { - return - } - if len(d.queue) >= streamDatagramQueueLen { - return - } - d.queue = append(d.queue, data) - d.signalHasData() -} - -func (d *datagrammer) Receive(ctx context.Context) ([]byte, error) { -start: - d.mx.Lock() - if len(d.queue) >= 1 { - data := d.queue[0] - d.queue = d.queue[1:] - d.mx.Unlock() - return data, nil - } - if receiveErr := d.receiveErr; receiveErr != nil { - d.mx.Unlock() - return nil, receiveErr - } - d.mx.Unlock() - - select { - case <-ctx.Done(): - return nil, context.Cause(ctx) - case <-d.hasData: - } - goto start -} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/error_codes.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/error_codes.go index ae646586a..d0b10a1f1 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/error_codes.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/error_codes.go @@ -9,24 +9,25 @@ import ( type ErrCode quic.ApplicationErrorCode const ( - ErrCodeNoError ErrCode = 0x100 - ErrCodeGeneralProtocolError ErrCode = 0x101 - ErrCodeInternalError ErrCode = 0x102 - ErrCodeStreamCreationError ErrCode = 0x103 - ErrCodeClosedCriticalStream ErrCode = 0x104 - ErrCodeFrameUnexpected ErrCode = 0x105 - ErrCodeFrameError ErrCode = 0x106 - ErrCodeExcessiveLoad ErrCode = 0x107 - ErrCodeIDError ErrCode = 0x108 - ErrCodeSettingsError ErrCode = 0x109 - ErrCodeMissingSettings ErrCode = 0x10a - ErrCodeRequestRejected ErrCode = 0x10b - ErrCodeRequestCanceled ErrCode = 0x10c - ErrCodeRequestIncomplete ErrCode = 0x10d - ErrCodeMessageError ErrCode = 0x10e - ErrCodeConnectError ErrCode = 0x10f - ErrCodeVersionFallback ErrCode = 0x110 - ErrCodeDatagramError ErrCode = 0x33 + ErrCodeNoError ErrCode = 0x100 + ErrCodeGeneralProtocolError ErrCode = 0x101 + ErrCodeInternalError ErrCode = 0x102 + ErrCodeStreamCreationError ErrCode = 0x103 + ErrCodeClosedCriticalStream ErrCode = 0x104 + ErrCodeFrameUnexpected ErrCode = 0x105 + ErrCodeFrameError ErrCode = 0x106 + ErrCodeExcessiveLoad ErrCode = 0x107 + ErrCodeIDError ErrCode = 0x108 + ErrCodeSettingsError ErrCode = 0x109 + ErrCodeMissingSettings ErrCode = 0x10a + ErrCodeRequestRejected ErrCode = 0x10b + ErrCodeRequestCanceled ErrCode = 0x10c + ErrCodeRequestIncomplete ErrCode = 0x10d + ErrCodeMessageError ErrCode = 0x10e + ErrCodeConnectError ErrCode = 0x10f + ErrCodeVersionFallback ErrCode = 0x110 + ErrCodeDatagramError ErrCode = 0x33 + ErrCodeQPACKDecompressionFailed ErrCode = 0x200 ) func (e ErrCode) String() string { @@ -75,6 +76,8 @@ func (e ErrCode) string() string { return "H3_VERSION_FALLBACK" case ErrCodeDatagramError: return "H3_DATAGRAM_ERROR" + case ErrCodeQPACKDecompressionFailed: + return "QPACK_DECOMPRESSION_FAILED" default: return "" } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/frames.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/frames.go index b54afb313..879a9f1b2 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/frames.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/frames.go @@ -5,8 +5,11 @@ import ( "errors" "fmt" "io" + "maps" "github.com/quic-go/quic-go" + "github.com/quic-go/quic-go/http3/qlog" + "github.com/quic-go/quic-go/qlogwriter" "github.com/quic-go/quic-go/quicvarint" ) @@ -15,20 +18,44 @@ type FrameType uint64 type unknownFrameHandlerFunc func(FrameType, error) (processed bool, err error) -type frame interface{} +type frame any var errHijacked = errors.New("hijacked") +type countingByteReader struct { + quicvarint.Reader + NumRead int +} + +func (r *countingByteReader) ReadByte() (byte, error) { + b, err := r.Reader.ReadByte() + if err == nil { + r.NumRead++ + } + return b, err +} + +func (r *countingByteReader) Read(b []byte) (int, error) { + n, err := r.Reader.Read(b) + r.NumRead += n + return n, err +} + +func (r *countingByteReader) Reset() { + r.NumRead = 0 +} + type frameParser struct { r io.Reader - conn quic.Connection + streamID quic.StreamID + closeConn func(quic.ApplicationErrorCode, string) error unknownFrameHandler unknownFrameHandlerFunc } -func (p *frameParser) ParseNext() (frame, error) { - qr := quicvarint.NewReader(p.r) +func (p *frameParser) ParseNext(qlogger qlogwriter.Recorder) (frame, error) { + r := &countingByteReader{Reader: quicvarint.NewReader(p.r)} for { - t, err := quicvarint.Read(qr) + t, err := quicvarint.Read(r) if err != nil { if p.unknownFrameHandler != nil { hijacked, err := p.unknownFrameHandler(0, err) @@ -52,31 +79,83 @@ func (p *frameParser) ParseNext() (frame, error) { } // If the unknownFrameHandler didn't process the frame, it is our responsibility to skip it. } - l, err := quicvarint.Read(qr) + l, err := quicvarint.Read(r) if err != nil { return nil, err } switch t { - case 0x0: + case 0x0: // DATA + if qlogger != nil { + qlogger.RecordEvent(qlog.FrameParsed{ + StreamID: p.streamID, + Raw: qlog.RawInfo{ + Length: int(l) + r.NumRead, + PayloadLength: int(l), + }, + Frame: qlog.Frame{Frame: qlog.DataFrame{}}, + }) + } return &dataFrame{Length: l}, nil - case 0x1: - return &headersFrame{Length: l}, nil - case 0x4: - return parseSettingsFrame(p.r, l) - case 0x3: // CANCEL_PUSH - case 0x5: // PUSH_PROMISE - case 0x7: - return parseGoAwayFrame(qr, l) - case 0xd: // MAX_PUSH_ID - case 0x2, 0x6, 0x8, 0x9: - p.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "") + case 0x1: // HEADERS + return &headersFrame{ + Length: l, + headerLen: r.NumRead, + }, nil + case 0x4: // SETTINGS + return parseSettingsFrame(r, l, p.streamID, qlogger) + case 0x3: // unsupported: CANCEL_PUSH + if qlogger != nil { + qlogger.RecordEvent(qlog.FrameParsed{ + StreamID: p.streamID, + Raw: qlog.RawInfo{Length: r.NumRead, PayloadLength: int(l)}, + Frame: qlog.Frame{Frame: qlog.CancelPushFrame{}}, + }) + } + case 0x5: // unsupported: PUSH_PROMISE + if qlogger != nil { + qlogger.RecordEvent(qlog.FrameParsed{ + StreamID: p.streamID, + Raw: qlog.RawInfo{Length: r.NumRead, PayloadLength: int(l)}, + Frame: qlog.Frame{Frame: qlog.PushPromiseFrame{}}, + }) + } + case 0x7: // GOAWAY + return parseGoAwayFrame(r, l, p.streamID, qlogger) + case 0xd: // unsupported: MAX_PUSH_ID + if qlogger != nil { + qlogger.RecordEvent(qlog.FrameParsed{ + StreamID: p.streamID, + Raw: qlog.RawInfo{Length: r.NumRead, PayloadLength: int(l)}, + Frame: qlog.Frame{Frame: qlog.MaxPushIDFrame{}}, + }) + } + case 0x2, 0x6, 0x8, 0x9: // reserved frame types + if qlogger != nil { + qlogger.RecordEvent(qlog.FrameParsed{ + StreamID: p.streamID, + Raw: qlog.RawInfo{Length: r.NumRead + int(l), PayloadLength: int(l)}, + Frame: qlog.Frame{Frame: qlog.ReservedFrame{Type: t}}, + }) + } + p.closeConn(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "") return nil, fmt.Errorf("http3: reserved frame type: %d", t) + default: + // unknown frame types + if qlogger != nil { + qlogger.RecordEvent(qlog.FrameParsed{ + StreamID: p.streamID, + Raw: qlog.RawInfo{Length: r.NumRead, PayloadLength: int(l)}, + Frame: qlog.Frame{Frame: qlog.UnknownFrame{Type: t}}, + }) + } } - // skip over unknown frames - if _, err := io.CopyN(io.Discard, qr, int64(l)); err != nil { + + // skip over the payload + if _, err := io.CopyN(io.Discard, r, int64(l)); err != nil { return nil, err } + r.Reset() } } @@ -90,7 +169,8 @@ func (f *dataFrame) Append(b []byte) []byte { } type headersFrame struct { - Length uint64 + Length uint64 + headerLen int // number of bytes read for type and length field } func (f *headersFrame) Append(b []byte) []byte { @@ -99,6 +179,8 @@ func (f *headersFrame) Append(b []byte) []byte { } const ( + // SETTINGS_MAX_FIELD_SECTION_SIZE + settingMaxFieldSectionSize = 0x6 // Extended CONNECT, RFC 9220 settingExtendedConnect = 0x8 // HTTP Datagrams, RFC 9297 @@ -106,13 +188,18 @@ const ( ) type settingsFrame struct { - Datagram bool // HTTP Datagrams, RFC 9297 - ExtendedConnect bool // Extended CONNECT, RFC 9220 + MaxFieldSectionSize int64 // SETTINGS_MAX_FIELD_SECTION_SIZE, -1 if not set - Other map[uint64]uint64 // all settings that we don't explicitly recognize + Datagram bool // HTTP Datagrams, RFC 9297 + ExtendedConnect bool // Extended CONNECT, RFC 9220 + Other map[uint64]uint64 // all settings that we don't explicitly recognize } -func parseSettingsFrame(r io.Reader, l uint64) (*settingsFrame, error) { +func pointer[T any](v T) *T { + return &v +} + +func parseSettingsFrame(r *countingByteReader, l uint64, streamID quic.StreamID, qlogger qlogwriter.Recorder) (*settingsFrame, error) { if l > 8*(1<<10) { return nil, fmt.Errorf("unexpected size for SETTINGS frame: %d", l) } @@ -123,9 +210,10 @@ func parseSettingsFrame(r io.Reader, l uint64) (*settingsFrame, error) { } return nil, err } - frame := &settingsFrame{} + frame := &settingsFrame{MaxFieldSectionSize: -1} b := bytes.NewReader(buf) - var readDatagram, readExtendedConnect bool + settingsFrame := qlog.SettingsFrame{MaxFieldSectionSize: -1} + var readMaxFieldSectionSize, readDatagram, readExtendedConnect bool for b.Len() > 0 { id, err := quicvarint.Read(b) if err != nil { // should not happen. We allocated the whole frame already. @@ -137,6 +225,13 @@ func parseSettingsFrame(r io.Reader, l uint64) (*settingsFrame, error) { } switch id { + case settingMaxFieldSectionSize: + if readMaxFieldSectionSize { + return nil, fmt.Errorf("duplicate setting: %d", id) + } + readMaxFieldSectionSize = true + frame.MaxFieldSectionSize = int64(val) + settingsFrame.MaxFieldSectionSize = int64(val) case settingExtendedConnect: if readExtendedConnect { return nil, fmt.Errorf("duplicate setting: %d", id) @@ -146,6 +241,9 @@ func parseSettingsFrame(r io.Reader, l uint64) (*settingsFrame, error) { return nil, fmt.Errorf("invalid value for SETTINGS_ENABLE_CONNECT_PROTOCOL: %d", val) } frame.ExtendedConnect = val == 1 + if qlogger != nil { + settingsFrame.ExtendedConnect = pointer(frame.ExtendedConnect) + } case settingDatagram: if readDatagram { return nil, fmt.Errorf("duplicate setting: %d", id) @@ -155,6 +253,9 @@ func parseSettingsFrame(r io.Reader, l uint64) (*settingsFrame, error) { return nil, fmt.Errorf("invalid value for SETTINGS_H3_DATAGRAM: %d", val) } frame.Datagram = val == 1 + if qlogger != nil { + settingsFrame.Datagram = pointer(frame.Datagram) + } default: if _, ok := frame.Other[id]; ok { return nil, fmt.Errorf("duplicate setting: %d", id) @@ -165,12 +266,27 @@ func parseSettingsFrame(r io.Reader, l uint64) (*settingsFrame, error) { frame.Other[id] = val } } + if qlogger != nil { + settingsFrame.Other = maps.Clone(frame.Other) + + qlogger.RecordEvent(qlog.FrameParsed{ + StreamID: streamID, + Raw: qlog.RawInfo{ + Length: r.NumRead, + PayloadLength: int(l), + }, + Frame: qlog.Frame{Frame: settingsFrame}, + }) + } return frame, nil } func (f *settingsFrame) Append(b []byte) []byte { b = quicvarint.Append(b, 0x4) var l int + if f.MaxFieldSectionSize >= 0 { + l += quicvarint.Len(settingMaxFieldSectionSize) + quicvarint.Len(uint64(f.MaxFieldSectionSize)) + } for id, val := range f.Other { l += quicvarint.Len(id) + quicvarint.Len(val) } @@ -181,6 +297,10 @@ func (f *settingsFrame) Append(b []byte) []byte { l += quicvarint.Len(settingExtendedConnect) + quicvarint.Len(1) } b = quicvarint.Append(b, uint64(l)) + if f.MaxFieldSectionSize >= 0 { + b = quicvarint.Append(b, settingMaxFieldSectionSize) + b = quicvarint.Append(b, uint64(f.MaxFieldSectionSize)) + } if f.Datagram { b = quicvarint.Append(b, settingDatagram) b = quicvarint.Append(b, 1) @@ -200,17 +320,24 @@ type goAwayFrame struct { StreamID quic.StreamID } -func parseGoAwayFrame(r io.ByteReader, l uint64) (*goAwayFrame, error) { +func parseGoAwayFrame(r *countingByteReader, l uint64, streamID quic.StreamID, qlogger qlogwriter.Recorder) (*goAwayFrame, error) { frame := &goAwayFrame{} - cbr := countingByteReader{ByteReader: r} - id, err := quicvarint.Read(&cbr) + startLen := r.NumRead + id, err := quicvarint.Read(r) if err != nil { return nil, err } - if cbr.Read != int(l) { + if r.NumRead-startLen != int(l) { return nil, errors.New("GOAWAY frame: inconsistent length") } frame.StreamID = quic.StreamID(id) + if qlogger != nil { + qlogger.RecordEvent(qlog.FrameParsed{ + StreamID: streamID, + Raw: qlog.RawInfo{Length: r.NumRead, PayloadLength: int(l)}, + Frame: qlog.Frame{Frame: qlog.GoAwayFrame{StreamID: frame.StreamID}}, + }) + } return frame, nil } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/headers.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/headers.go index 05d13ff3c..2e6d4a51c 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/headers.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/headers.go @@ -3,6 +3,7 @@ package http3 import ( "errors" "fmt" + "io" "net/http" "net/textproto" "net/url" @@ -14,6 +15,13 @@ import ( "github.com/quic-go/qpack" ) +type qpackError struct{ err error } + +func (e *qpackError) Error() string { return fmt.Sprintf("qpack: %v", e.err) } +func (e *qpackError) Unwrap() error { return e.err } + +var errHeaderTooLarge = errors.New("http3: headers too large") + type header struct { // Pseudo header fields defined in RFC 9114 Path string @@ -38,11 +46,28 @@ var invalidHeaderFields = [...]string{ "upgrade", } -func parseHeaders(headers []qpack.HeaderField, isRequest bool) (header, error) { - hdr := header{Headers: make(http.Header, len(headers))} +func parseHeaders(decodeFn qpack.DecodeFunc, isRequest bool, sizeLimit int, headerFields *[]qpack.HeaderField) (header, error) { + hdr := header{Headers: make(http.Header)} var readFirstRegularHeader, readContentLength bool var contentLengthStr string - for _, h := range headers { + for { + h, err := decodeFn() + if err != nil { + if err == io.EOF { + break + } + return header{}, &qpackError{err} + } + if headerFields != nil { + *headerFields = append(*headerFields, h) + } + // RFC 9114, section 4.2.2: + // The size of a field list is calculated based on the uncompressed size of fields, + // including the length of the name and value in bytes plus an overhead of 32 bytes for each field. + sizeLimit -= len(h.Name) + len(h.Value) + 32 + if sizeLimit < 0 { + return header{}, errHeaderTooLarge + } // field names need to be lowercase, see section 4.2 of RFC 9114 if strings.ToLower(h.Name) != h.Name { return header{}, fmt.Errorf("header field is not lower-case: %s", h.Name) @@ -55,24 +80,34 @@ func parseHeaders(headers []qpack.HeaderField, isRequest bool) (header, error) { // all pseudo headers must appear before regular header fields, see section 4.3 of RFC 9114 return header{}, fmt.Errorf("received pseudo header %s after a regular header field", h.Name) } - var isResponsePseudoHeader bool // pseudo headers are either valid for requests or for responses + var isResponsePseudoHeader bool // pseudo headers are either valid for requests or for responses + var isDuplicatePseudoHeader bool // pseudo headers are allowed to appear exactly once switch h.Name { case ":path": + isDuplicatePseudoHeader = hdr.Path != "" hdr.Path = h.Value case ":method": + isDuplicatePseudoHeader = hdr.Method != "" hdr.Method = h.Value case ":authority": + isDuplicatePseudoHeader = hdr.Authority != "" hdr.Authority = h.Value case ":protocol": + isDuplicatePseudoHeader = hdr.Protocol != "" hdr.Protocol = h.Value case ":scheme": + isDuplicatePseudoHeader = hdr.Scheme != "" hdr.Scheme = h.Value case ":status": + isDuplicatePseudoHeader = hdr.Status != "" hdr.Status = h.Value isResponsePseudoHeader = true default: return header{}, fmt.Errorf("unknown pseudo header: %s", h.Name) } + if isDuplicatePseudoHeader { + return header{}, fmt.Errorf("duplicate pseudo header: %s", h.Name) + } if isRequest && isResponsePseudoHeader { return header{}, fmt.Errorf("invalid request pseudo header: %s", h.Name) } @@ -120,19 +155,29 @@ func parseHeaders(headers []qpack.HeaderField, isRequest bool) (header, error) { return hdr, nil } -func parseTrailers(headers []qpack.HeaderField) (http.Header, error) { - h := make(http.Header, len(headers)) - for _, field := range headers { - if field.IsPseudo() { - return nil, fmt.Errorf("http3: received pseudo header in trailer: %s", field.Name) +func parseTrailers(decodeFn qpack.DecodeFunc, headerFields *[]qpack.HeaderField) (http.Header, error) { + h := make(http.Header) + for { + hf, err := decodeFn() + if err != nil { + if err == io.EOF { + break + } + return nil, &qpackError{err} + } + if headerFields != nil { + *headerFields = append(*headerFields, hf) } - h.Add(field.Name, field.Value) + if hf.IsPseudo() { + return nil, fmt.Errorf("http3: received pseudo header in trailer: %s", hf.Name) + } + h.Add(hf.Name, hf.Value) } return h, nil } -func requestFromHeaders(headerFields []qpack.HeaderField) (*http.Request, error) { - hdr, err := parseHeaders(headerFields, true) +func requestFromHeaders(decodeFn qpack.DecodeFunc, sizeLimit int, headerFields *[]qpack.HeaderField) (*http.Request, error) { + hdr, err := parseHeaders(decodeFn, true, sizeLimit, headerFields) if err != nil { return nil, err } @@ -201,24 +246,17 @@ func requestFromHeaders(headerFields []qpack.HeaderField) (*http.Request, error) }, nil } -func hostnameFromURL(url *url.URL) string { - if url != nil { - return url.Host - } - return "" -} - // updateResponseFromHeaders sets up http.Response as an HTTP/3 response, // using the decoded qpack header filed. // It is only called for the HTTP header (and not the HTTP trailer). // It takes an http.Response as an argument to allow the caller to set the trailer later on. -func updateResponseFromHeaders(rsp *http.Response, headerFields []qpack.HeaderField) error { - hdr, err := parseHeaders(headerFields, false) +func updateResponseFromHeaders(rsp *http.Response, decodeFn qpack.DecodeFunc, sizeLimit int, headerFields *[]qpack.HeaderField) error { + hdr, err := parseHeaders(decodeFn, false, sizeLimit, headerFields) if err != nil { return err } if hdr.Status == "" { - return errors.New("missing status field") + return errors.New("missing :status field") } rsp.Proto = "HTTP/3.0" rsp.ProtoMajor = 3 diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/http_stream.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/http_stream.go deleted file mode 100644 index 4593d4730..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/http_stream.go +++ /dev/null @@ -1,294 +0,0 @@ -package http3 - -import ( - "context" - "errors" - "fmt" - "io" - "net/http" - "net/http/httptrace" - - "github.com/quic-go/quic-go" - "github.com/quic-go/quic-go/internal/protocol" - - "github.com/quic-go/qpack" -) - -// A Stream is an HTTP/3 request stream. -// When writing to and reading from the stream, data is framed in HTTP/3 DATA frames. -type Stream interface { - quic.Stream - - SendDatagram([]byte) error - ReceiveDatagram(context.Context) ([]byte, error) -} - -// A RequestStream is an HTTP/3 request stream. -// When writing to and reading from the stream, data is framed in HTTP/3 DATA frames. -type RequestStream interface { - Stream - - // SendRequestHeader sends the HTTP request. - // It is invalid to call it more than once. - // It is invalid to call it after Write has been called. - SendRequestHeader(req *http.Request) error - - // ReadResponse reads the HTTP response from the stream. - // It is invalid to call it more than once. - // It doesn't set Response.Request and Response.TLS. - // It is invalid to call it after Read has been called. - ReadResponse() (*http.Response, error) -} - -type stream struct { - quic.Stream - conn *connection - - buf []byte // used as a temporary buffer when writing the HTTP/3 frame headers - - bytesRemainingInFrame uint64 - - datagrams *datagrammer - - parseTrailer func(io.Reader, uint64) error - parsedTrailer bool -} - -var _ Stream = &stream{} - -func newStream(str quic.Stream, conn *connection, datagrams *datagrammer, parseTrailer func(io.Reader, uint64) error) *stream { - return &stream{ - Stream: str, - conn: conn, - buf: make([]byte, 16), - datagrams: datagrams, - parseTrailer: parseTrailer, - } -} - -func (s *stream) Read(b []byte) (int, error) { - fp := &frameParser{ - r: s.Stream, - conn: s.conn, - } - if s.bytesRemainingInFrame == 0 { - parseLoop: - for { - frame, err := fp.ParseNext() - if err != nil { - return 0, err - } - switch f := frame.(type) { - case *dataFrame: - if s.parsedTrailer { - return 0, errors.New("DATA frame received after trailers") - } - s.bytesRemainingInFrame = f.Length - break parseLoop - case *headersFrame: - if s.conn.perspective == protocol.PerspectiveServer { - continue - } - if s.parsedTrailer { - return 0, errors.New("additional HEADERS frame received after trailers") - } - s.parsedTrailer = true - return 0, s.parseTrailer(s.Stream, f.Length) - default: - s.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "") - // parseNextFrame skips over unknown frame types - // Therefore, this condition is only entered when we parsed another known frame type. - return 0, fmt.Errorf("peer sent an unexpected frame: %T", f) - } - } - } - - var n int - var err error - if s.bytesRemainingInFrame < uint64(len(b)) { - n, err = s.Stream.Read(b[:s.bytesRemainingInFrame]) - } else { - n, err = s.Stream.Read(b) - } - s.bytesRemainingInFrame -= uint64(n) - return n, err -} - -func (s *stream) hasMoreData() bool { - return s.bytesRemainingInFrame > 0 -} - -func (s *stream) Write(b []byte) (int, error) { - s.buf = s.buf[:0] - s.buf = (&dataFrame{Length: uint64(len(b))}).Append(s.buf) - if _, err := s.Stream.Write(s.buf); err != nil { - return 0, err - } - return s.Stream.Write(b) -} - -func (s *stream) writeUnframed(b []byte) (int, error) { - return s.Stream.Write(b) -} - -func (s *stream) StreamID() protocol.StreamID { - return s.Stream.StreamID() -} - -// The stream conforms to the quic.Stream interface, but instead of writing to and reading directly -// from the QUIC stream, it writes to and reads from the HTTP stream. -type requestStream struct { - *stream - - responseBody io.ReadCloser // set by ReadResponse - - decoder *qpack.Decoder - requestWriter *requestWriter - maxHeaderBytes uint64 - reqDone chan<- struct{} - disableCompression bool - response *http.Response - trace *httptrace.ClientTrace - - sentRequest bool - requestedGzip bool - isConnect bool - firstByte bool -} - -var _ RequestStream = &requestStream{} - -func newRequestStream( - str *stream, - requestWriter *requestWriter, - reqDone chan<- struct{}, - decoder *qpack.Decoder, - disableCompression bool, - maxHeaderBytes uint64, - rsp *http.Response, - trace *httptrace.ClientTrace, -) *requestStream { - return &requestStream{ - stream: str, - requestWriter: requestWriter, - reqDone: reqDone, - decoder: decoder, - disableCompression: disableCompression, - maxHeaderBytes: maxHeaderBytes, - response: rsp, - trace: trace, - } -} - -func (s *requestStream) Read(b []byte) (int, error) { - if s.responseBody == nil { - return 0, errors.New("http3: invalid use of RequestStream.Read: need to call ReadResponse first") - } - return s.responseBody.Read(b) -} - -func (s *requestStream) SendRequestHeader(req *http.Request) error { - if s.sentRequest { - return errors.New("http3: invalid duplicate use of SendRequestHeader") - } - if !s.disableCompression && req.Method != http.MethodHead && - req.Header.Get("Accept-Encoding") == "" && req.Header.Get("Range") == "" { - s.requestedGzip = true - } - s.isConnect = req.Method == http.MethodConnect - s.sentRequest = true - return s.requestWriter.WriteRequestHeader(s.Stream, req, s.requestedGzip) -} - -func (s *requestStream) ReadResponse() (*http.Response, error) { - fp := &frameParser{ - conn: s.conn, - r: &tracingReader{ - Reader: s.Stream, - first: &s.firstByte, - trace: s.trace, - }, - } - frame, err := fp.ParseNext() - if err != nil { - s.Stream.CancelRead(quic.StreamErrorCode(ErrCodeFrameError)) - s.Stream.CancelWrite(quic.StreamErrorCode(ErrCodeFrameError)) - return nil, fmt.Errorf("http3: parsing frame failed: %w", err) - } - hf, ok := frame.(*headersFrame) - if !ok { - s.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "expected first frame to be a HEADERS frame") - return nil, errors.New("http3: expected first frame to be a HEADERS frame") - } - if hf.Length > s.maxHeaderBytes { - s.Stream.CancelRead(quic.StreamErrorCode(ErrCodeFrameError)) - s.Stream.CancelWrite(quic.StreamErrorCode(ErrCodeFrameError)) - return nil, fmt.Errorf("http3: HEADERS frame too large: %d bytes (max: %d)", hf.Length, s.maxHeaderBytes) - } - headerBlock := make([]byte, hf.Length) - if _, err := io.ReadFull(s.Stream, headerBlock); err != nil { - s.Stream.CancelRead(quic.StreamErrorCode(ErrCodeRequestIncomplete)) - s.Stream.CancelWrite(quic.StreamErrorCode(ErrCodeRequestIncomplete)) - return nil, fmt.Errorf("http3: failed to read response headers: %w", err) - } - hfs, err := s.decoder.DecodeFull(headerBlock) - if err != nil { - // TODO: use the right error code - s.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeGeneralProtocolError), "") - return nil, fmt.Errorf("http3: failed to decode response headers: %w", err) - } - res := s.response - if err := updateResponseFromHeaders(res, hfs); err != nil { - s.Stream.CancelRead(quic.StreamErrorCode(ErrCodeMessageError)) - s.Stream.CancelWrite(quic.StreamErrorCode(ErrCodeMessageError)) - return nil, fmt.Errorf("http3: invalid response: %w", err) - } - - // Check that the server doesn't send more data in DATA frames than indicated by the Content-Length header (if set). - // See section 4.1.2 of RFC 9114. - respBody := newResponseBody(s.stream, res.ContentLength, s.reqDone) - - // Rules for when to set Content-Length are defined in https://tools.ietf.org/html/rfc7230#section-3.3.2. - isInformational := res.StatusCode >= 100 && res.StatusCode < 200 - isNoContent := res.StatusCode == http.StatusNoContent - isSuccessfulConnect := s.isConnect && res.StatusCode >= 200 && res.StatusCode < 300 - if (isInformational || isNoContent || isSuccessfulConnect) && res.ContentLength == -1 { - res.ContentLength = 0 - } - if s.requestedGzip && res.Header.Get("Content-Encoding") == "gzip" { - res.Header.Del("Content-Encoding") - res.Header.Del("Content-Length") - res.ContentLength = -1 - s.responseBody = newGzipReader(respBody) - res.Uncompressed = true - } else { - s.responseBody = respBody - } - res.Body = s.responseBody - return res, nil -} - -func (s *stream) SendDatagram(b []byte) error { - // TODO: reject if datagrams are not negotiated (yet) - return s.datagrams.Send(b) -} - -func (s *stream) ReceiveDatagram(ctx context.Context) ([]byte, error) { - // TODO: reject if datagrams are not negotiated (yet) - return s.datagrams.Receive(ctx) -} - -type tracingReader struct { - io.Reader - first *bool - trace *httptrace.ClientTrace -} - -func (r *tracingReader) Read(b []byte) (int, error) { - n, err := r.Reader.Read(b) - if n > 0 && r.first != nil && !*r.first { - traceGotFirstResponseByte(r.trace) - *r.first = true - } - return n, err -} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/mockgen.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/mockgen.go index 1d790e11d..38a6f0214 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/mockgen.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/mockgen.go @@ -2,7 +2,10 @@ package http3 -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -mock_names=TestClientConnInterface=MockClientConn -package http3 -destination mock_clientconn_test.go github.com/quic-go/quic-go/http3 TestClientConnInterface" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -mock_names=TestClientConnInterface=MockClientConn -package http3 -destination mock_clientconn_test.go github.com/quic-go/quic-go/http3 TestClientConnInterface" type TestClientConnInterface = clientConn -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -package http3 -destination mock_quic_early_listener_test.go github.com/quic-go/quic-go/http3 QUICEarlyListener" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -mock_names=DatagramStream=MockDatagramStream -package http3 -destination mock_datagram_stream_test.go github.com/quic-go/quic-go/http3 DatagramStream" +type DatagramStream = datagramStream + +//go:generate sh -c "go tool mockgen -typed -package http3 -destination mock_quic_listener_test.go github.com/quic-go/quic-go/http3 QUICListener" diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/qlog.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/qlog.go new file mode 100644 index 000000000..54a2d2eb2 --- /dev/null +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/qlog.go @@ -0,0 +1,56 @@ +package http3 + +import ( + "github.com/quic-go/quic-go" + "github.com/quic-go/quic-go/http3/qlog" + "github.com/quic-go/quic-go/qlogwriter" + + "github.com/quic-go/qpack" +) + +func maybeQlogInvalidHeadersFrame(qlogger qlogwriter.Recorder, streamID quic.StreamID, l uint64) { + if qlogger != nil { + qlogger.RecordEvent(qlog.FrameParsed{ + StreamID: streamID, + Raw: qlog.RawInfo{PayloadLength: int(l)}, + Frame: qlog.Frame{Frame: qlog.HeadersFrame{}}, + }) + } +} + +func qlogParsedHeadersFrame(qlogger qlogwriter.Recorder, streamID quic.StreamID, hf *headersFrame, hfs []qpack.HeaderField) { + headerFields := make([]qlog.HeaderField, len(hfs)) + for i, hf := range hfs { + headerFields[i] = qlog.HeaderField{ + Name: hf.Name, + Value: hf.Value, + } + } + qlogger.RecordEvent(qlog.FrameParsed{ + StreamID: streamID, + Raw: qlog.RawInfo{ + Length: int(hf.Length) + hf.headerLen, + PayloadLength: int(hf.Length), + }, + Frame: qlog.Frame{Frame: qlog.HeadersFrame{ + HeaderFields: headerFields, + }}, + }) +} + +func qlogCreatedHeadersFrame(qlogger qlogwriter.Recorder, streamID quic.StreamID, length, payloadLength int, hfs []qlog.HeaderField) { + headerFields := make([]qlog.HeaderField, len(hfs)) + for i, hf := range hfs { + headerFields[i] = qlog.HeaderField{ + Name: hf.Name, + Value: hf.Value, + } + } + qlogger.RecordEvent(qlog.FrameCreated{ + StreamID: streamID, + Raw: qlog.RawInfo{Length: length, PayloadLength: payloadLength}, + Frame: qlog.Frame{Frame: qlog.HeadersFrame{ + HeaderFields: headerFields, + }}, + }) +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/qlog/event.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/qlog/event.go new file mode 100644 index 000000000..5e5b7278a --- /dev/null +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/qlog/event.go @@ -0,0 +1,138 @@ +package qlog + +import ( + "time" + + "github.com/quic-go/quic-go" + "github.com/quic-go/quic-go/qlogwriter/jsontext" +) + +type encoderHelper struct { + enc *jsontext.Encoder + err error +} + +func (h *encoderHelper) WriteToken(t jsontext.Token) { + if h.err != nil { + return + } + h.err = h.enc.WriteToken(t) +} + +type RawInfo struct { + Length int // full packet length, including header and AEAD authentication tag + PayloadLength int // length of the packet payload, excluding AEAD tag +} + +func (i RawInfo) HasValues() bool { + return i.Length != 0 || i.PayloadLength != 0 +} + +func (i RawInfo) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + if i.Length != 0 { + h.WriteToken(jsontext.String("length")) + h.WriteToken(jsontext.Uint(uint64(i.Length))) + } + if i.PayloadLength != 0 { + h.WriteToken(jsontext.String("payload_length")) + h.WriteToken(jsontext.Uint(uint64(i.PayloadLength))) + } + h.WriteToken(jsontext.EndObject) + return h.err +} + +type FrameParsed struct { + StreamID quic.StreamID + Raw RawInfo + Frame Frame +} + +func (e FrameParsed) Name() string { return "http3:frame_parsed" } + +func (e FrameParsed) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("stream_id")) + h.WriteToken(jsontext.Uint(uint64(e.StreamID))) + if e.Raw.HasValues() { + h.WriteToken(jsontext.String("raw")) + if err := e.Raw.encode(enc); err != nil { + return err + } + } + h.WriteToken(jsontext.String("frame")) + if err := e.Frame.encode(enc); err != nil { + return err + } + h.WriteToken(jsontext.EndObject) + return h.err +} + +type FrameCreated struct { + StreamID quic.StreamID + Raw RawInfo + Frame Frame +} + +func (e FrameCreated) Name() string { return "http3:frame_created" } + +func (e FrameCreated) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("stream_id")) + h.WriteToken(jsontext.Uint(uint64(e.StreamID))) + if e.Raw.HasValues() { + h.WriteToken(jsontext.String("raw")) + if err := e.Raw.encode(enc); err != nil { + return err + } + } + h.WriteToken(jsontext.String("frame")) + if err := e.Frame.encode(enc); err != nil { + return err + } + h.WriteToken(jsontext.EndObject) + return h.err +} + +type DatagramCreated struct { + QuaterStreamID uint64 + Raw RawInfo +} + +func (e DatagramCreated) Name() string { return "http3:datagram_created" } + +func (e DatagramCreated) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("quater_stream_id")) + h.WriteToken(jsontext.Uint(e.QuaterStreamID)) + h.WriteToken(jsontext.String("raw")) + if err := e.Raw.encode(enc); err != nil { + return err + } + h.WriteToken(jsontext.EndObject) + return h.err +} + +type DatagramParsed struct { + QuaterStreamID uint64 + Raw RawInfo +} + +func (e DatagramParsed) Name() string { return "http3:datagram_parsed" } + +func (e DatagramParsed) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("quater_stream_id")) + h.WriteToken(jsontext.Uint(e.QuaterStreamID)) + h.WriteToken(jsontext.String("raw")) + if err := e.Raw.encode(enc); err != nil { + return err + } + h.WriteToken(jsontext.EndObject) + return h.err +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/qlog/frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/qlog/frame.go new file mode 100644 index 000000000..b404453da --- /dev/null +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/qlog/frame.go @@ -0,0 +1,220 @@ +package qlog + +import ( + "github.com/quic-go/quic-go" + "github.com/quic-go/quic-go/qlogwriter/jsontext" +) + +// Frame represents an HTTP/3 frame. +type Frame struct { + Frame any +} + +func (f Frame) encode(enc *jsontext.Encoder) error { + switch frame := f.Frame.(type) { + case DataFrame: + return frame.encode(enc) + case HeadersFrame: + return frame.encode(enc) + case GoAwayFrame: + return frame.encode(enc) + case SettingsFrame: + return frame.encode(enc) + case PushPromiseFrame: + return frame.encode(enc) + case CancelPushFrame: + return frame.encode(enc) + case MaxPushIDFrame: + return frame.encode(enc) + case ReservedFrame: + return frame.encode(enc) + case UnknownFrame: + return frame.encode(enc) + } + // This shouldn't happen if the code is correctly logging frames. + // Write a null token to produce valid JSON. + return enc.WriteToken(jsontext.Null) +} + +// A DataFrame is a DATA frame +type DataFrame struct{} + +func (f *DataFrame) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("data")) + h.WriteToken(jsontext.EndObject) + return h.err +} + +type HeaderField struct { + Name string + Value string +} + +// A HeadersFrame is a HEADERS frame +type HeadersFrame struct { + HeaderFields []HeaderField +} + +func (f *HeadersFrame) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("headers")) + if len(f.HeaderFields) > 0 { + h.WriteToken(jsontext.String("header_fields")) + h.WriteToken(jsontext.BeginArray) + for _, f := range f.HeaderFields { + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("name")) + h.WriteToken(jsontext.String(f.Name)) + h.WriteToken(jsontext.String("value")) + h.WriteToken(jsontext.String(f.Value)) + h.WriteToken(jsontext.EndObject) + } + h.WriteToken(jsontext.EndArray) + } + h.WriteToken(jsontext.EndObject) + return h.err +} + +// A GoAwayFrame is a GOAWAY frame +type GoAwayFrame struct { + StreamID quic.StreamID +} + +func (f *GoAwayFrame) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("goaway")) + h.WriteToken(jsontext.String("id")) + h.WriteToken(jsontext.Uint(uint64(f.StreamID))) + h.WriteToken(jsontext.EndObject) + return h.err +} + +type SettingsFrame struct { + MaxFieldSectionSize int64 + Datagram *bool + ExtendedConnect *bool + Other map[uint64]uint64 +} + +func (f *SettingsFrame) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("settings")) + h.WriteToken(jsontext.String("settings")) + h.WriteToken(jsontext.BeginArray) + if f.MaxFieldSectionSize >= 0 { + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("name")) + h.WriteToken(jsontext.String("settings_max_field_section_size")) + h.WriteToken(jsontext.String("value")) + h.WriteToken(jsontext.Uint(uint64(f.MaxFieldSectionSize))) + h.WriteToken(jsontext.EndObject) + } + if f.Datagram != nil { + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("name")) + h.WriteToken(jsontext.String("settings_h3_datagram")) + h.WriteToken(jsontext.String("value")) + h.WriteToken(jsontext.Bool(*f.Datagram)) + h.WriteToken(jsontext.EndObject) + } + if f.ExtendedConnect != nil { + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("name")) + h.WriteToken(jsontext.String("settings_enable_connect_protocol")) + h.WriteToken(jsontext.String("value")) + h.WriteToken(jsontext.Bool(*f.ExtendedConnect)) + h.WriteToken(jsontext.EndObject) + } + if len(f.Other) > 0 { + for k, v := range f.Other { + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("name")) + h.WriteToken(jsontext.String("unknown")) + h.WriteToken(jsontext.String("name_bytes")) + h.WriteToken(jsontext.Uint(k)) + h.WriteToken(jsontext.String("value")) + h.WriteToken(jsontext.Uint(v)) + h.WriteToken(jsontext.EndObject) + } + } + h.WriteToken(jsontext.EndArray) + h.WriteToken(jsontext.EndObject) + return h.err +} + +// A PushPromiseFrame is a PUSH_PROMISE frame +type PushPromiseFrame struct{} + +func (f *PushPromiseFrame) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("push_promise")) + h.WriteToken(jsontext.EndObject) + return h.err +} + +// A CancelPushFrame is a CANCEL_PUSH frame +type CancelPushFrame struct{} + +func (f *CancelPushFrame) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("cancel_push")) + h.WriteToken(jsontext.EndObject) + return h.err +} + +// A MaxPushIDFrame is a MAX_PUSH_ID frame +type MaxPushIDFrame struct{} + +func (f *MaxPushIDFrame) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("max_push_id")) + h.WriteToken(jsontext.EndObject) + return h.err +} + +// A ReservedFrame is one of the reserved frame types +type ReservedFrame struct { + Type uint64 +} + +func (f *ReservedFrame) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("reserved")) + h.WriteToken(jsontext.String("frame_type_bytes")) + h.WriteToken(jsontext.Uint(f.Type)) + h.WriteToken(jsontext.EndObject) + return h.err +} + +// An UnknownFrame is an unknown frame type +type UnknownFrame struct { + Type uint64 +} + +func (f *UnknownFrame) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("unknown")) + h.WriteToken(jsontext.String("frame_type_bytes")) + h.WriteToken(jsontext.Uint(f.Type)) + h.WriteToken(jsontext.EndObject) + return h.err +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/qlog/qlog_dir.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/qlog/qlog_dir.go new file mode 100644 index 000000000..898d17c6b --- /dev/null +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/qlog/qlog_dir.go @@ -0,0 +1,15 @@ +package qlog + +import ( + "context" + + "github.com/quic-go/quic-go" + "github.com/quic-go/quic-go/qlog" + "github.com/quic-go/quic-go/qlogwriter" +) + +const EventSchema = "urn:ietf:params:qlog:events:http3-12" + +func DefaultConnectionTracer(ctx context.Context, isClient bool, connID quic.ConnectionID) qlogwriter.Trace { + return qlog.DefaultConnectionTracerWithSchemas(ctx, isClient, connID, []string{qlog.EventSchema, EventSchema}) +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/request_writer.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/request_writer.go index 2dbacb150..9737fd2ae 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/request_writer.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/request_writer.go @@ -18,6 +18,8 @@ import ( "github.com/quic-go/qpack" "github.com/quic-go/quic-go" + "github.com/quic-go/quic-go/http3/qlog" + "github.com/quic-go/quic-go/qlogwriter" ) const bodyCopyBufferSize = 8 * 1024 @@ -37,13 +39,13 @@ func newRequestWriter() *requestWriter { } } -func (w *requestWriter) WriteRequestHeader(str quic.Stream, req *http.Request, gzip bool) error { +func (w *requestWriter) WriteRequestHeader(wr io.Writer, req *http.Request, gzip bool, streamID quic.StreamID, qlogger qlogwriter.Recorder) error { // TODO: figure out how to add support for trailers buf := &bytes.Buffer{} - if err := w.writeHeaders(buf, req, gzip); err != nil { + if err := w.writeHeaders(buf, req, gzip, streamID, qlogger); err != nil { return err } - if _, err := str.Write(buf.Bytes()); err != nil { + if _, err := wr.Write(buf.Bytes()); err != nil { return err } trace := httptrace.ContextClientTrace(req.Context()) @@ -51,22 +53,26 @@ func (w *requestWriter) WriteRequestHeader(str quic.Stream, req *http.Request, g return nil } -func (w *requestWriter) writeHeaders(wr io.Writer, req *http.Request, gzip bool) error { +func (w *requestWriter) writeHeaders(wr io.Writer, req *http.Request, gzip bool, streamID quic.StreamID, qlogger qlogwriter.Recorder) error { w.mutex.Lock() defer w.mutex.Unlock() defer w.encoder.Close() defer w.headerBuf.Reset() - if err := w.encodeHeaders(req, gzip, "", actualContentLength(req)); err != nil { + headerFields, err := w.encodeHeaders(req, gzip, "", actualContentLength(req), qlogger != nil) + if err != nil { return err } b := make([]byte, 0, 128) b = (&headersFrame{Length: uint64(w.headerBuf.Len())}).Append(b) + if qlogger != nil { + qlogCreatedHeadersFrame(qlogger, streamID, len(b)+w.headerBuf.Len(), w.headerBuf.Len(), headerFields) + } if _, err := wr.Write(b); err != nil { return err } - _, err := wr.Write(w.headerBuf.Bytes()) + _, err = wr.Write(w.headerBuf.Bytes()) return err } @@ -78,17 +84,19 @@ func isExtendedConnectRequest(req *http.Request) bool { // Modified to support Extended CONNECT: // Contrary to what the godoc for the http.Request says, // we do respect the Proto field if the method is CONNECT. -func (w *requestWriter) encodeHeaders(req *http.Request, addGzipHeader bool, trailers string, contentLength int64) error { +// +// The returned header fields are only set if doQlog is true. +func (w *requestWriter) encodeHeaders(req *http.Request, addGzipHeader bool, trailers string, contentLength int64, doQlog bool) ([]qlog.HeaderField, error) { host := req.Host if host == "" { host = req.URL.Host } host, err := httpguts.PunycodeHostPort(host) if err != nil { - return err + return nil, err } if !httpguts.ValidHostHeader(host) { - return errors.New("http3: invalid Host header") + return nil, errors.New("http3: invalid Host header") } // http.NewRequest sets this field to HTTP/1.1 @@ -102,9 +110,9 @@ func (w *requestWriter) encodeHeaders(req *http.Request, addGzipHeader bool, tra path = strings.TrimPrefix(path, req.URL.Scheme+"://"+host) if !validPseudoPath(path) { if req.URL.Opaque != "" { - return fmt.Errorf("invalid request :path %q from URL.Opaque = %q", orig, req.URL.Opaque) + return nil, fmt.Errorf("invalid request :path %q from URL.Opaque = %q", orig, req.URL.Opaque) } else { - return fmt.Errorf("invalid request :path %q", orig) + return nil, fmt.Errorf("invalid request :path %q", orig) } } } @@ -115,11 +123,11 @@ func (w *requestWriter) encodeHeaders(req *http.Request, addGzipHeader bool, tra // continue to reuse the hpack encoder for future requests) for k, vv := range req.Header { if !httpguts.ValidHeaderFieldName(k) { - return fmt.Errorf("invalid HTTP header name %q", k) + return nil, fmt.Errorf("invalid HTTP header name %q", k) } for _, v := range vv { if !httpguts.ValidHeaderFieldValue(v) { - return fmt.Errorf("invalid HTTP header value %q for header %q", v, k) + return nil, fmt.Errorf("invalid HTTP header value %q for header %q", v, k) } } } @@ -207,15 +215,22 @@ func (w *requestWriter) encodeHeaders(req *http.Request, addGzipHeader bool, tra traceHeaders := traceHasWroteHeaderField(trace) // Header list size is ok. Write the headers. + var headerFields []qlog.HeaderField + if doQlog { + headerFields = make([]qlog.HeaderField, 0, len(req.Header)) + } enumerateHeaders(func(name, value string) { name = strings.ToLower(name) w.encoder.WriteField(qpack.HeaderField{Name: name, Value: value}) if traceHeaders { traceWroteHeaderField(trace, name, value) } + if doQlog { + headerFields = append(headerFields, qlog.HeaderField{Name: name, Value: value}) + } }) - return nil + return headerFields, nil } // authorityAddr returns a given authority (a host/IP, or host:port / ip:port) diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/response_writer.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/response_writer.go index b8b68120c..ed22ca246 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/response_writer.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/response_writer.go @@ -11,14 +11,15 @@ import ( "time" "github.com/quic-go/qpack" + "github.com/quic-go/quic-go/http3/qlog" + "golang.org/x/net/http/httpguts" ) -// The HTTPStreamer allows taking over a HTTP/3 stream. The interface is implemented the http.Response.Body. -// On the client side, the stream will be closed for writing, unless the DontCloseRequestStream RoundTripOpt was set. +// The HTTPStreamer allows taking over a HTTP/3 stream. The interface is implemented by the http.ResponseWriter. // When a stream is taken over, it's the caller's responsibility to close the stream. type HTTPStreamer interface { - HTTPStream() Stream + HTTPStream() *Stream } // The maximum length of an encoded HTTP/3 frame header is 16: @@ -28,9 +29,9 @@ const frameHeaderLen = 16 const maxSmallResponseSize = 4096 type responseWriter struct { - str *stream + str *Stream - conn Connection + conn *Conn header http.Header trailers map[string]struct{} buf []byte @@ -57,9 +58,16 @@ var ( _ http.Flusher = &responseWriter{} _ Hijacker = &responseWriter{} _ HTTPStreamer = &responseWriter{} + // make sure that we implement (some of the) methods used by the http.ResponseController + _ interface { + SetReadDeadline(time.Time) error + SetWriteDeadline(time.Time) error + Flush() + FlushError() error + } = &responseWriter{} ) -func newResponseWriter(str *stream, conn Connection, isHead bool, logger *slog.Logger) *responseWriter { +func newResponseWriter(str *Stream, conn *Conn, isHead bool, logger *slog.Logger) *responseWriter { return &responseWriter{ str: str, conn: conn, @@ -175,6 +183,13 @@ func (w *responseWriter) doWrite(p []byte) (int, error) { df := &dataFrame{Length: l} w.buf = w.buf[:0] w.buf = df.Append(w.buf) + if w.str.qlogger != nil { + w.str.qlogger.RecordEvent(qlog.FrameCreated{ + StreamID: w.str.StreamID(), + Raw: qlog.RawInfo{Length: len(w.buf) + int(l), PayloadLength: int(l)}, + Frame: qlog.Frame{Frame: qlog.DataFrame{}}, + }) + } if _, err := w.str.writeUnframed(w.buf); err != nil { return 0, maybeReplaceError(err) } @@ -196,11 +211,15 @@ func (w *responseWriter) doWrite(p []byte) (int, error) { } func (w *responseWriter) writeHeader(status int) error { + var headerFields []qlog.HeaderField // only used for qlog var headers bytes.Buffer enc := qpack.NewEncoder(&headers) if err := enc.WriteField(qpack.HeaderField{Name: ":status", Value: strconv.Itoa(status)}); err != nil { return err } + if w.str.qlogger != nil { + headerFields = append(headerFields, qlog.HeaderField{Name: ":status", Value: strconv.Itoa(status)}) + } // Handle trailer fields if vals, ok := w.header["Trailer"]; ok { @@ -223,9 +242,14 @@ func (w *responseWriter) writeHeader(status int) error { continue } for index := range v { - if err := enc.WriteField(qpack.HeaderField{Name: strings.ToLower(k), Value: v[index]}); err != nil { + name := strings.ToLower(k) + value := v[index] + if err := enc.WriteField(qpack.HeaderField{Name: name, Value: value}); err != nil { return err } + if w.str.qlogger != nil { + headerFields = append(headerFields, qlog.HeaderField{Name: name, Value: value}) + } } } @@ -233,6 +257,10 @@ func (w *responseWriter) writeHeader(status int) error { buf = (&headersFrame{Length: uint64(headers.Len())}).Append(buf) buf = append(buf, headers.Bytes()...) + if w.str.qlogger != nil { + qlogCreatedHeadersFrame(w.str.qlogger, w.str.StreamID(), len(buf), headers.Len(), headerFields) + } + _, err := w.str.writeUnframed(buf) return err } @@ -305,6 +333,7 @@ func (w *responseWriter) writeTrailers() error { } var b bytes.Buffer + var headerFields []qlog.HeaderField enc := qpack.NewEncoder(&b) for trailer := range w.trailers { trailerName := strings.ToLower(strings.TrimPrefix(trailer, http.TrailerPrefix)) @@ -313,6 +342,9 @@ func (w *responseWriter) writeTrailers() error { if err := enc.WriteField(qpack.HeaderField{Name: trailerName, Value: val}); err != nil { return err } + if w.str.qlogger != nil { + headerFields = append(headerFields, qlog.HeaderField{Name: trailerName, Value: val}) + } } } } @@ -320,12 +352,15 @@ func (w *responseWriter) writeTrailers() error { buf := make([]byte, 0, frameHeaderLen+b.Len()) buf = (&headersFrame{Length: uint64(b.Len())}).Append(buf) buf = append(buf, b.Bytes()...) + if w.str.qlogger != nil { + qlogCreatedHeadersFrame(w.str.qlogger, w.str.StreamID(), len(buf), b.Len(), headerFields) + } _, err := w.str.writeUnframed(buf) w.trailerWritten = true return err } -func (w *responseWriter) HTTPStream() Stream { +func (w *responseWriter) HTTPStream() *Stream { w.hijacked = true w.Flush() return w.str @@ -333,7 +368,7 @@ func (w *responseWriter) HTTPStream() Stream { func (w *responseWriter) wasStreamHijacked() bool { return w.hijacked } -func (w *responseWriter) Connection() Connection { +func (w *responseWriter) Connection() *Conn { return w.conn } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/server.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/server.go index 1479609cf..87720e43a 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/server.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/server.go @@ -7,9 +7,11 @@ import ( "fmt" "io" "log/slog" + "maps" "net" "net/http" "runtime" + "slices" "strconv" "strings" "sync" @@ -17,22 +19,13 @@ import ( "time" "github.com/quic-go/quic-go" - "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/http3/qlog" + "github.com/quic-go/quic-go/qlogwriter" "github.com/quic-go/quic-go/quicvarint" "github.com/quic-go/qpack" ) -// allows mocking of quic.Listen and quic.ListenAddr -var ( - quicListen = func(conn net.PacketConn, tlsConf *tls.Config, config *quic.Config) (QUICEarlyListener, error) { - return quic.ListenEarly(conn, tlsConf, config) - } - quicListenAddr = func(addr string, tlsConf *tls.Config, config *quic.Config) (QUICEarlyListener, error) { - return quic.ListenAddrEarly(addr, tlsConf, config) - } -) - // NextProtoH3 is the ALPN protocol negotiated during the TLS handshake, for QUIC v1 and v2. const NextProtoH3 = "h3" @@ -46,66 +39,33 @@ const ( streamTypeQPACKDecoderStream = 3 ) -const goawayTimeout = 5 * time.Second - -// A QUICEarlyListener listens for incoming QUIC connections. -type QUICEarlyListener interface { - Accept(context.Context) (quic.EarlyConnection, error) +// A QUICListener listens for incoming QUIC connections. +type QUICListener interface { + Accept(context.Context) (*quic.Conn, error) Addr() net.Addr io.Closer } -var _ QUICEarlyListener = &quic.EarlyListener{} - -func versionToALPN(v protocol.Version) string { - //nolint:exhaustive // These are all the versions we care about. - switch v { - case protocol.Version1, protocol.Version2: - return NextProtoH3 - default: - return "" - } -} +var _ QUICListener = &quic.EarlyListener{} // ConfigureTLSConfig creates a new tls.Config which can be used -// to create a quic.Listener meant for serving http3. The created -// tls.Config adds the functionality of detecting the used QUIC version -// in order to set the correct ALPN value for the http3 connection. +// to create a quic.Listener meant for serving HTTP/3. func ConfigureTLSConfig(tlsConf *tls.Config) *tls.Config { - // The tls.Config used to setup the quic.Listener needs to have the GetConfigForClient callback set. - // That way, we can get the QUIC version and set the correct ALPN value. - return &tls.Config{ - GetConfigForClient: func(ch *tls.ClientHelloInfo) (*tls.Config, error) { - // determine the ALPN from the QUIC version used - proto := NextProtoH3 - val := ch.Context().Value(quic.QUICVersionContextKey) - if v, ok := val.(quic.Version); ok { - proto = versionToALPN(v) + // Workaround for https://github.com/golang/go/issues/60506. + // This initializes the session tickets _before_ cloning the config. + _, _ = tlsConf.DecryptTicket(nil, tls.ConnectionState{}) + config := tlsConf.Clone() + config.NextProtos = []string{NextProtoH3} + if gfc := config.GetConfigForClient; gfc != nil { + config.GetConfigForClient = func(ch *tls.ClientHelloInfo) (*tls.Config, error) { + conf, err := gfc(ch) + if conf == nil || err != nil { + return conf, err } - config := tlsConf - if tlsConf.GetConfigForClient != nil { - getConfigForClient := tlsConf.GetConfigForClient - var err error - conf, err := getConfigForClient(ch) - if err != nil { - return nil, err - } - if conf != nil { - config = conf - } - } - if config == nil { - return nil, nil - } - // Workaround for https://github.com/golang/go/issues/60506. - // This initializes the session tickets _before_ cloning the config. - _, _ = config.DecryptTicket(nil, tls.ConnectionState{}) - - config = config.Clone() - config.NextProtos = []string{proto} - return config, nil - }, + return ConfigureTLSConfig(conf), nil + } } + return config } // contextKey is a value for use with context.WithValue. It's used as @@ -132,9 +92,13 @@ var ServerContextKey = &contextKey{"http3-server"} // than its string representation. var RemoteAddrContextKey = &contextKey{"remote-addr"} -// listenerInfo contains info about specific listener added with addListener -type listenerInfo struct { +// listener contains info about specific listener added with addListener +type listener struct { + ln *QUICListener port int // 0 means that no info about port is available + + // if this listener was constructed by the application, it won't be closed when the server is closed + createdLocally bool } // Server is a HTTP/3 server. @@ -194,12 +158,12 @@ type Server struct { // Callers can either ignore the frame and return control of the stream back to HTTP/3 // (by returning hijacked false). // Alternatively, callers can take over the QUIC stream (by returning hijacked true). - StreamHijacker func(FrameType, quic.ConnectionTracingID, quic.Stream, error) (hijacked bool, err error) + StreamHijacker func(FrameType, quic.ConnectionTracingID, *quic.Stream, error) (hijacked bool, err error) // UniStreamHijacker, when set, is called for unknown unidirectional stream of unknown stream type. // If parsing the stream type fails, the error is passed to the callback. // In that case, the stream type will not be set. - UniStreamHijacker func(StreamType, quic.ConnectionTracingID, quic.ReceiveStream, error) (hijacked bool) + UniStreamHijacker func(StreamType, quic.ConnectionTracingID, *quic.ReceiveStream, error) (hijacked bool) // IdleTimeout specifies how long until idle clients connection should be // closed. Idle refers only to the HTTP/3 layer, activity at the QUIC layer @@ -209,12 +173,12 @@ type Server struct { // ConnContext optionally specifies a function that modifies the context used for a new connection c. // The provided ctx has a ServerContextKey value. - ConnContext func(ctx context.Context, c quic.Connection) context.Context + ConnContext func(ctx context.Context, c *quic.Conn) context.Context Logger *slog.Logger mutex sync.RWMutex - listeners map[*QUICEarlyListener]listenerInfo + listeners []listener closed bool closeCtx context.Context // canceled when the server is closed @@ -235,9 +199,9 @@ func (s *Server) ListenAndServe() error { if err != nil { return err } - defer s.removeListener(&ln) + defer s.removeListener(ln) - return s.serveListener(ln) + return s.serveListener(*ln) } // ListenAndServeTLS listens on the UDP address s.Addr and calls s.Handler to handle HTTP/3 requests on incoming connections. @@ -256,9 +220,9 @@ func (s *Server) ListenAndServeTLS(certFile, keyFile string) error { if err != nil { return err } - defer s.removeListener(&ln) + defer s.removeListener(ln) - return s.serveListener(ln) + return s.serveListener(*ln) } // Serve an existing UDP connection. @@ -269,9 +233,9 @@ func (s *Server) Serve(conn net.PacketConn) error { if err != nil { return err } - defer s.removeListener(&ln) + defer s.removeListener(ln) - return s.serveListener(ln) + return s.serveListener(*ln) } // init initializes the contexts used for shutting down the server. @@ -291,8 +255,13 @@ func (s *Server) decreaseConnCount() { } // ServeQUICConn serves a single QUIC connection. -func (s *Server) ServeQUICConn(conn quic.Connection) error { +func (s *Server) ServeQUICConn(conn *quic.Conn) error { s.mutex.Lock() + if s.closed { + s.mutex.Unlock() + return http.ErrServerClosed + } + s.init() s.mutex.Unlock() @@ -305,11 +274,11 @@ func (s *Server) ServeQUICConn(conn quic.Connection) error { // ServeListener serves an existing QUIC listener. // Make sure you use http3.ConfigureTLSConfig to configure a tls.Config // and use it to construct a http3-friendly QUIC listener. -// Closing the server does close the listener. +// Closing the server does not close the listener. It is the application's responsibility to close them. // ServeListener always returns a non-nil error. After Shutdown or Close, the returned error is http.ErrServerClosed. -func (s *Server) ServeListener(ln QUICEarlyListener) error { +func (s *Server) ServeListener(ln QUICListener) error { s.mutex.Lock() - if err := s.addListener(&ln); err != nil { + if err := s.addListener(&ln, false); err != nil { s.mutex.Unlock() return err } @@ -319,7 +288,7 @@ func (s *Server) ServeListener(ln QUICEarlyListener) error { return s.serveListener(ln) } -func (s *Server) serveListener(ln QUICEarlyListener) error { +func (s *Server) serveListener(ln QUICListener) error { for { conn, err := ln.Accept(s.graceCtx) // server closed @@ -343,7 +312,7 @@ func (s *Server) serveListener(ln QUICEarlyListener) error { var errServerWithoutTLSConfig = errors.New("use of http3.Server without TLSConfig") -func (s *Server) setupListenerForConn(tlsConf *tls.Config, conn net.PacketConn) (QUICEarlyListener, error) { +func (s *Server) setupListenerForConn(tlsConf *tls.Config, conn net.PacketConn) (*QUICListener, error) { if tlsConf == nil { return nil, errServerWithoutTLSConfig } @@ -366,24 +335,24 @@ func (s *Server) setupListenerForConn(tlsConf *tls.Config, conn net.PacketConn) return nil, http.ErrServerClosed } - var ln QUICEarlyListener + var ln QUICListener var err error if conn == nil { addr := s.Addr if addr == "" { addr = ":https" } - ln, err = quicListenAddr(addr, baseConf, quicConf) + ln, err = quic.ListenAddrEarly(addr, baseConf, quicConf) } else { - ln, err = quicListen(conn, baseConf, quicConf) + ln, err = quic.ListenEarly(conn, baseConf, quicConf) } if err != nil { return nil, err } - if err := s.addListener(&ln); err != nil { + if err := s.addListener(&ln, true); err != nil { return nil, err } - return ln, nil + return &ln, nil } func extractPort(addr string) (int, error) { @@ -407,28 +376,10 @@ func (s *Server) generateAltSvcHeader() { } // This code assumes that we will use protocol.SupportedVersions if no quic.Config is passed. - supportedVersions := protocol.SupportedVersions - if s.QUICConfig != nil && len(s.QUICConfig.Versions) > 0 { - supportedVersions = s.QUICConfig.Versions - } - - // keep track of which have been seen so we don't yield duplicate values - seen := make(map[string]struct{}, len(supportedVersions)) - var versionStrings []string - for _, version := range supportedVersions { - if v := versionToALPN(version); len(v) > 0 { - if _, ok := seen[v]; !ok { - versionStrings = append(versionStrings, v) - seen[v] = struct{}{} - } - } - } var altSvc []string addPort := func(port int) { - for _, v := range versionStrings { - altSvc = append(altSvc, fmt.Sprintf(`%s=":%d"; ma=2592000`, v, port)) - } + altSvc = append(altSvc, fmt.Sprintf(`%s=":%d"; ma=2592000`, NextProtoH3, port)) } if s.Port != 0 { @@ -455,43 +406,45 @@ func (s *Server) generateAltSvcHeader() { s.altSvcHeader = strings.Join(altSvc, ",") } -// We store a pointer to interface in the map set. This is safe because we only -// call trackListener via Serve and can track+defer untrack the same pointer to -// local variable there. We never need to compare a Listener from another caller. -func (s *Server) addListener(l *QUICEarlyListener) error { +func (s *Server) addListener(l *QUICListener, createdLocally bool) error { if s.closed { return http.ErrServerClosed } - if s.listeners == nil { - s.listeners = make(map[*QUICEarlyListener]listenerInfo) - } s.init() laddr := (*l).Addr() if port, err := extractPort(laddr.String()); err == nil { - s.listeners[l] = listenerInfo{port} + s.listeners = append(s.listeners, listener{ln: l, port: port, createdLocally: createdLocally}) } else { logger := s.Logger if logger == nil { logger = slog.Default() } logger.Error("Unable to extract port from listener, will not be announced using SetQUICHeaders", "local addr", laddr, "error", err) - s.listeners[l] = listenerInfo{} + s.listeners = append(s.listeners, listener{ln: l, port: 0, createdLocally: createdLocally}) } s.generateAltSvcHeader() return nil } -func (s *Server) removeListener(l *QUICEarlyListener) { +func (s *Server) removeListener(l *QUICListener) { s.mutex.Lock() defer s.mutex.Unlock() - delete(s.listeners, l) + + s.listeners = slices.DeleteFunc(s.listeners, func(info listener) bool { + return info.ln == l + }) s.generateAltSvcHeader() } // handleConn handles the HTTP/3 exchange on a QUIC connection. // It blocks until all HTTP handlers for all streams have returned. -func (s *Server) handleConn(conn quic.Connection) error { +func (s *Server) handleConn(conn *quic.Conn) error { + var qlogger qlogwriter.Recorder + if qlogTrace := conn.QlogTrace(); qlogTrace != nil && qlogTrace.SupportsSchemas(qlog.EventSchema) { + qlogger = qlogTrace.AddProducer() + } + // open the control stream and send a SETTINGS frame, it's also used to send a GOAWAY frame later // when the server is gracefully closed ctrlStr, err := conn.OpenUniStream() @@ -501,28 +454,44 @@ func (s *Server) handleConn(conn quic.Connection) error { b := make([]byte, 0, 64) b = quicvarint.Append(b, streamTypeControlStream) // stream type b = (&settingsFrame{ - Datagram: s.EnableDatagrams, - ExtendedConnect: true, - Other: s.AdditionalSettings, + MaxFieldSectionSize: int64(s.maxHeaderBytes()), + Datagram: s.EnableDatagrams, + ExtendedConnect: true, + Other: s.AdditionalSettings, }).Append(b) + if qlogger != nil { + sf := qlog.SettingsFrame{ + MaxFieldSectionSize: int64(s.maxHeaderBytes()), + ExtendedConnect: pointer(true), + Other: maps.Clone(s.AdditionalSettings), + } + if s.EnableDatagrams { + sf.Datagram = pointer(true) + } + qlogger.RecordEvent(qlog.FrameCreated{ + StreamID: ctrlStr.StreamID(), + Raw: qlog.RawInfo{Length: len(b)}, + Frame: qlog.Frame{Frame: sf}, + }) + } ctrlStr.Write(b) - ctx := conn.Context() - ctx = context.WithValue(ctx, ServerContextKey, s) - ctx = context.WithValue(ctx, http.LocalAddrContextKey, conn.LocalAddr()) - ctx = context.WithValue(ctx, RemoteAddrContextKey, conn.RemoteAddr()) + connCtx := conn.Context() + connCtx = context.WithValue(connCtx, ServerContextKey, s) + connCtx = context.WithValue(connCtx, http.LocalAddrContextKey, conn.LocalAddr()) + connCtx = context.WithValue(connCtx, RemoteAddrContextKey, conn.RemoteAddr()) if s.ConnContext != nil { - ctx = s.ConnContext(ctx, conn) - if ctx == nil { + connCtx = s.ConnContext(connCtx, conn) + if connCtx == nil { panic("http3: ConnContext returned nil") } } hconn := newConnection( - ctx, + connCtx, conn, s.EnableDatagrams, - protocol.PerspectiveServer, + true, // server s.Logger, s.IdleTimeout, ) @@ -531,42 +500,63 @@ func (s *Server) handleConn(conn quic.Connection) error { var nextStreamID quic.StreamID var wg sync.WaitGroup var handleErr error + var inGracefulShutdown bool // Process all requests immediately. // It's the client's responsibility to decide which requests are eligible for 0-RTT. + ctx := s.graceCtx for { - str, datagrams, err := hconn.acceptStream(s.graceCtx) + // The context used here is: + // * before graceful shutdown: s.graceCtx + // * after graceful shutdown: s.closeCtx + // This allows us to keep accepting (and resetting) streams after graceful shutdown has started. + str, err := hconn.acceptStream(ctx) if err != nil { + // the underlying connection was closed (by either side) + if hconn.Context().Err() != nil { + var appErr *quic.ApplicationError + if !errors.As(err, &appErr) || appErr.ErrorCode != quic.ApplicationErrorCode(ErrCodeNoError) { + handleErr = fmt.Errorf("accepting stream failed: %w", err) + } + break + } // server (not gracefully) closed, close the connection immediately if s.closeCtx.Err() != nil { conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeNoError), "") handleErr = http.ErrServerClosed break } - - // gracefully closed, send GOAWAY frame and wait for requests to complete or grace period to end - // new requests will be rejected and shouldn't be sent - if s.graceCtx.Err() != nil { - b = (&goAwayFrame{StreamID: nextStreamID}).Append(b[:0]) - // set a deadline to send the GOAWAY frame - ctrlStr.SetWriteDeadline(time.Now().Add(goawayTimeout)) - ctrlStr.Write(b) - - select { - case <-hconn.Context().Done(): - // we expect the client to eventually close the connection after receiving the GOAWAY - case <-s.closeCtx.Done(): - // close the connection after graceful period - conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeNoError), "") + inGracefulShutdown = s.graceCtx.Err() != nil + if !inGracefulShutdown { + var appErr *quic.ApplicationError + if !errors.As(err, &appErr) || appErr.ErrorCode != quic.ApplicationErrorCode(ErrCodeNoError) { + handleErr = fmt.Errorf("accepting stream failed: %w", err) } - handleErr = http.ErrServerClosed break } - var appErr *quic.ApplicationError - if !errors.As(err, &appErr) || appErr.ErrorCode != quic.ApplicationErrorCode(ErrCodeNoError) { - handleErr = fmt.Errorf("accepting stream failed: %w", err) + // gracefully closed, send GOAWAY frame and wait for requests to complete or grace period to end + // new requests will be rejected and shouldn't be sent + if qlogger != nil { + qlogger.RecordEvent(qlog.FrameCreated{ + StreamID: ctrlStr.StreamID(), + Frame: qlog.Frame{Frame: qlog.GoAwayFrame{StreamID: nextStreamID}}, + }) } - break + wg.Add(1) + // Send the GOAWAY frame in a separate Goroutine. + // Sending might block if the peer didn't grant enough flow control credit. + // Write is guaranteed to return once the connection is closed. + go func() { + defer wg.Done() + _, _ = ctrlStr.Write((&goAwayFrame{StreamID: nextStreamID}).Append(nil)) + }() + ctx = s.closeCtx + continue + } + if inGracefulShutdown { + str.CancelRead(quic.StreamErrorCode(ErrCodeRequestRejected)) + str.CancelWrite(quic.StreamErrorCode(ErrCodeRequestRejected)) + continue } nextStreamID = str.StreamID() + 4 @@ -575,34 +565,39 @@ func (s *Server) handleConn(conn quic.Connection) error { // handleRequest will return once the request has been handled, // or the underlying connection is closed defer wg.Done() - s.handleRequest(hconn, str, datagrams, hconn.decoder) + s.handleRequest(hconn, str, hconn.decoder, qlogger) }() } wg.Wait() return handleErr } -func (s *Server) maxHeaderBytes() uint64 { +func (s *Server) maxHeaderBytes() int { if s.MaxHeaderBytes <= 0 { return http.DefaultMaxHeaderBytes } - return uint64(s.MaxHeaderBytes) + return s.MaxHeaderBytes } -func (s *Server) handleRequest(conn *connection, str quic.Stream, datagrams *datagrammer, decoder *qpack.Decoder) { +func (s *Server) handleRequest( + conn *Conn, + str *stateTrackingStream, + decoder *qpack.Decoder, + qlogger qlogwriter.Recorder, +) { var ufh unknownFrameHandlerFunc if s.StreamHijacker != nil { ufh = func(ft FrameType, e error) (processed bool, err error) { return s.StreamHijacker( ft, conn.Context().Value(quic.ConnectionTracingKey).(quic.ConnectionTracingID), - str, + str.QUICStream(), e, ) } } - fp := &frameParser{conn: conn, r: str, unknownFrameHandler: ufh} - frame, err := fp.ParseNext() + fp := &frameParser{closeConn: conn.CloseWithError, r: str, unknownFrameHandler: ufh} + frame, err := fp.ParseNext(qlogger) if err != nil { if !errors.Is(err, errHijacked) { str.CancelRead(quic.StreamErrorCode(ErrCodeRequestIncomplete)) @@ -615,27 +610,46 @@ func (s *Server) handleRequest(conn *connection, str quic.Stream, datagrams *dat conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "expected first frame to be a HEADERS frame") return } - if hf.Length > s.maxHeaderBytes() { - str.CancelRead(quic.StreamErrorCode(ErrCodeFrameError)) - str.CancelWrite(quic.StreamErrorCode(ErrCodeFrameError)) + if hf.Length > uint64(s.maxHeaderBytes()) { + maybeQlogInvalidHeadersFrame(qlogger, str.StreamID(), hf.Length) + // stop the client from sending more data + str.CancelRead(quic.StreamErrorCode(ErrCodeExcessiveLoad)) + // send a 431 Response (Request Header Fields Too Large) + s.rejectWithHeaderFieldsTooLarge(str, conn, qlogger) return } headerBlock := make([]byte, hf.Length) if _, err := io.ReadFull(str, headerBlock); err != nil { + maybeQlogInvalidHeadersFrame(qlogger, str.StreamID(), hf.Length) str.CancelRead(quic.StreamErrorCode(ErrCodeRequestIncomplete)) str.CancelWrite(quic.StreamErrorCode(ErrCodeRequestIncomplete)) return } - hfs, err := decoder.DecodeFull(headerBlock) - if err != nil { - // TODO: use the right error code - conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeGeneralProtocolError), "expected first frame to be a HEADERS frame") - return + decodeFn := decoder.Decode(headerBlock) + var hfs []qpack.HeaderField + if qlogger != nil { + hfs = make([]qpack.HeaderField, 0, 16) + } + req, err := requestFromHeaders(decodeFn, s.maxHeaderBytes(), &hfs) + if qlogger != nil { + qlogParsedHeadersFrame(qlogger, str.StreamID(), hf, hfs) } - req, err := requestFromHeaders(hfs) if err != nil { - str.CancelRead(quic.StreamErrorCode(ErrCodeMessageError)) - str.CancelWrite(quic.StreamErrorCode(ErrCodeMessageError)) + if errors.Is(err, errHeaderTooLarge) { + // stop the client from sending more data + str.CancelRead(quic.StreamErrorCode(ErrCodeExcessiveLoad)) + // send a 431 Response (Request Header Fields Too Large) + s.rejectWithHeaderFieldsTooLarge(str, conn, qlogger) + return + } + + errCode := ErrCodeMessageError + var qpackErr *qpackError + if errors.As(err, &qpackErr) { + errCode = ErrCodeQPACKDecompressionFailed + } + str.CancelRead(quic.StreamErrorCode(errCode)) + str.CancelWrite(quic.StreamErrorCode(errCode)) return } @@ -649,7 +663,7 @@ func (s *Server) handleRequest(conn *connection, str quic.Stream, datagrams *dat if _, ok := req.Header["Content-Length"]; ok && req.ContentLength >= 0 { contentLength = req.ContentLength } - hstr := newStream(str, conn, datagrams, nil) + hstr := newStream(str, conn, nil, nil, qlogger) body := newRequestBody(hstr, contentLength, conn.Context(), conn.ReceivedSettings(), conn.Settings) req.Body = body @@ -715,6 +729,14 @@ func (s *Server) handleRequest(conn *connection, str quic.Stream, datagrams *dat str.Close() } +func (s *Server) rejectWithHeaderFieldsTooLarge(str *stateTrackingStream, conn *Conn, qlogger qlogwriter.Recorder) { + hstr := newStream(str, conn, nil, nil, qlogger) + defer hstr.Close() + r := newResponseWriter(hstr, conn, false, s.Logger) + r.WriteHeader(http.StatusRequestHeaderFieldsTooLarge) + r.Flush() +} + // Close the server immediately, aborting requests and sending CONNECTION_CLOSE frames to connected clients. // Close in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established. // It is the caller's responsibility to close any connection passed to ServeQUICConn. @@ -730,9 +752,11 @@ func (s *Server) Close() error { s.closeCancel() var err error - for ln := range s.listeners { - if cerr := (*ln).Close(); cerr != nil && err == nil { - err = cerr + for _, l := range s.listeners { + if l.createdLocally { + if cerr := (*l.ln).Close(); cerr != nil && err == nil { + err = cerr + } } } if s.connCount.Load() == 0 { @@ -743,19 +767,33 @@ func (s *Server) Close() error { return err } -// Shutdown shuts down the server gracefully. +// Shutdown gracefully shuts down the server without interrupting any active connections. // The server sends a GOAWAY frame first, then or for all running requests to complete. -// Shutdown in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established. +// Shutdown in combination with ListenAndServe may race if it is called before a UDP socket is established. +// It is recommended to use Serve instead. func (s *Server) Shutdown(ctx context.Context) error { s.mutex.Lock() s.closed = true - // server is never used + // server was never used if s.closeCtx == nil { s.mutex.Unlock() return nil } s.graceCancel() + + // close all listeners + var closeErrs []error + for _, l := range s.listeners { + if l.createdLocally { + if err := (*l.ln).Close(); err != nil { + closeErrs = append(closeErrs, err) + } + } + } s.mutex.Unlock() + if len(closeErrs) > 0 { + return errors.Join(closeErrs...) + } if s.connCount.Load() == 0 { return s.Close() diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/state_tracking_stream.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/state_tracking_stream.go index 9cf17f5e6..6c7fa52d7 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/state_tracking_stream.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/state_tracking_stream.go @@ -9,7 +9,7 @@ import ( "github.com/quic-go/quic-go" ) -var _ quic.Stream = &stateTrackingStream{} +const streamDatagramQueueLen = 32 // stateTrackingStream is an implementation of quic.Stream that delegates // to an underlying stream @@ -19,30 +19,31 @@ var _ quic.Stream = &stateTrackingStream{} // parent connection, this is done through the streamClearer interface when // both the send and receive sides are closed type stateTrackingStream struct { - quic.Stream + *quic.Stream + + sendDatagram func([]byte) error + hasData chan struct{} + queue [][]byte // TODO: use a ring buffer mx sync.Mutex sendErr error recvErr error clearer streamClearer - setter errorSetter } +var _ datagramStream = &stateTrackingStream{} + type streamClearer interface { clearStream(quic.StreamID) } -type errorSetter interface { - SetSendError(error) - SetReceiveError(error) -} - -func newStateTrackingStream(s quic.Stream, clearer streamClearer, setter errorSetter) *stateTrackingStream { +func newStateTrackingStream(s *quic.Stream, clearer streamClearer, sendDatagram func([]byte) error) *stateTrackingStream { t := &stateTrackingStream{ - Stream: s, - clearer: clearer, - setter: setter, + Stream: s, + clearer: clearer, + sendDatagram: sendDatagram, + hasData: make(chan struct{}, 1), } context.AfterFunc(s.Context(), func() { @@ -62,8 +63,6 @@ func (s *stateTrackingStream) closeSend(e error) { if s.recvErr != nil { s.clearer.clearStream(s.StreamID()) } - - s.setter.SetSendError(e) s.sendErr = e } } @@ -78,9 +77,8 @@ func (s *stateTrackingStream) closeReceive(e error) { if s.sendErr != nil { s.clearer.clearStream(s.StreamID()) } - - s.setter.SetReceiveError(e) s.recvErr = e + s.signalHasDatagram() } } @@ -90,7 +88,7 @@ func (s *stateTrackingStream) Close() error { } func (s *stateTrackingStream) CancelWrite(e quic.StreamErrorCode) { - s.closeSend(&quic.StreamError{StreamID: s.Stream.StreamID(), ErrorCode: e}) + s.closeSend(&quic.StreamError{StreamID: s.StreamID(), ErrorCode: e}) s.Stream.CancelWrite(e) } @@ -103,7 +101,7 @@ func (s *stateTrackingStream) Write(b []byte) (int, error) { } func (s *stateTrackingStream) CancelRead(e quic.StreamErrorCode) { - s.closeReceive(&quic.StreamError{StreamID: s.Stream.StreamID(), ErrorCode: e}) + s.closeReceive(&quic.StreamError{StreamID: s.StreamID(), ErrorCode: e}) s.Stream.CancelRead(e) } @@ -114,3 +112,62 @@ func (s *stateTrackingStream) Read(b []byte) (int, error) { } return n, err } + +func (s *stateTrackingStream) SendDatagram(b []byte) error { + s.mx.Lock() + sendErr := s.sendErr + s.mx.Unlock() + if sendErr != nil { + return sendErr + } + + return s.sendDatagram(b) +} + +func (s *stateTrackingStream) signalHasDatagram() { + select { + case s.hasData <- struct{}{}: + default: + } +} + +func (s *stateTrackingStream) enqueueDatagram(data []byte) { + s.mx.Lock() + defer s.mx.Unlock() + + if s.recvErr != nil { + return + } + if len(s.queue) >= streamDatagramQueueLen { + return + } + s.queue = append(s.queue, data) + s.signalHasDatagram() +} + +func (s *stateTrackingStream) ReceiveDatagram(ctx context.Context) ([]byte, error) { +start: + s.mx.Lock() + if len(s.queue) > 0 { + data := s.queue[0] + s.queue = s.queue[1:] + s.mx.Unlock() + return data, nil + } + if receiveErr := s.recvErr; receiveErr != nil { + s.mx.Unlock() + return nil, receiveErr + } + s.mx.Unlock() + + select { + case <-ctx.Done(): + return nil, context.Cause(ctx) + case <-s.hasData: + } + goto start +} + +func (s *stateTrackingStream) QUICStream() *quic.Stream { + return s.Stream +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/stream.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/stream.go new file mode 100644 index 000000000..12204cac3 --- /dev/null +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/stream.go @@ -0,0 +1,403 @@ +package http3 + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + "net/http/httptrace" + "time" + + "github.com/quic-go/quic-go" + "github.com/quic-go/quic-go/http3/qlog" + "github.com/quic-go/quic-go/qlogwriter" + + "github.com/quic-go/qpack" +) + +type datagramStream interface { + io.ReadWriteCloser + CancelRead(quic.StreamErrorCode) + CancelWrite(quic.StreamErrorCode) + StreamID() quic.StreamID + Context() context.Context + SetDeadline(time.Time) error + SetReadDeadline(time.Time) error + SetWriteDeadline(time.Time) error + SendDatagram(b []byte) error + ReceiveDatagram(ctx context.Context) ([]byte, error) + + QUICStream() *quic.Stream +} + +// A Stream is an HTTP/3 stream. +// +// When writing to and reading from the stream, data is framed in HTTP/3 DATA frames. +type Stream struct { + datagramStream + conn *Conn + frameParser *frameParser + + buf []byte // used as a temporary buffer when writing the HTTP/3 frame headers + + bytesRemainingInFrame uint64 + + qlogger qlogwriter.Recorder + + parseTrailer func(io.Reader, *headersFrame) error + parsedTrailer bool +} + +func newStream( + str datagramStream, + conn *Conn, + trace *httptrace.ClientTrace, + parseTrailer func(io.Reader, *headersFrame) error, + qlogger qlogwriter.Recorder, +) *Stream { + return &Stream{ + datagramStream: str, + conn: conn, + buf: make([]byte, 16), + qlogger: qlogger, + parseTrailer: parseTrailer, + frameParser: &frameParser{ + r: &tracingReader{Reader: str, trace: trace}, + streamID: str.StreamID(), + closeConn: conn.CloseWithError, + }, + } +} + +func (s *Stream) Read(b []byte) (int, error) { + if s.bytesRemainingInFrame == 0 { + parseLoop: + for { + frame, err := s.frameParser.ParseNext(s.qlogger) + if err != nil { + return 0, err + } + switch f := frame.(type) { + case *dataFrame: + if s.parsedTrailer { + return 0, errors.New("DATA frame received after trailers") + } + s.bytesRemainingInFrame = f.Length + break parseLoop + case *headersFrame: + if s.conn.isServer { + continue + } + if s.parsedTrailer { + maybeQlogInvalidHeadersFrame(s.qlogger, s.StreamID(), f.Length) + return 0, errors.New("additional HEADERS frame received after trailers") + } + s.parsedTrailer = true + return 0, s.parseTrailer(s.datagramStream, f) + default: + s.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "") + // parseNextFrame skips over unknown frame types + // Therefore, this condition is only entered when we parsed another known frame type. + return 0, fmt.Errorf("peer sent an unexpected frame: %T", f) + } + } + } + + var n int + var err error + if s.bytesRemainingInFrame < uint64(len(b)) { + n, err = s.datagramStream.Read(b[:s.bytesRemainingInFrame]) + } else { + n, err = s.datagramStream.Read(b) + } + s.bytesRemainingInFrame -= uint64(n) + return n, err +} + +func (s *Stream) hasMoreData() bool { + return s.bytesRemainingInFrame > 0 +} + +func (s *Stream) Write(b []byte) (int, error) { + s.buf = s.buf[:0] + s.buf = (&dataFrame{Length: uint64(len(b))}).Append(s.buf) + if s.qlogger != nil { + s.qlogger.RecordEvent(qlog.FrameCreated{ + StreamID: s.StreamID(), + Raw: qlog.RawInfo{ + Length: len(s.buf) + len(b), + PayloadLength: len(b), + }, + Frame: qlog.Frame{Frame: qlog.DataFrame{}}, + }) + } + if _, err := s.datagramStream.Write(s.buf); err != nil { + return 0, err + } + return s.datagramStream.Write(b) +} + +func (s *Stream) writeUnframed(b []byte) (int, error) { + return s.datagramStream.Write(b) +} + +func (s *Stream) StreamID() quic.StreamID { + return s.datagramStream.StreamID() +} + +func (s *Stream) SendDatagram(b []byte) error { + // TODO: reject if datagrams are not negotiated (yet) + return s.datagramStream.SendDatagram(b) +} + +func (s *Stream) ReceiveDatagram(ctx context.Context) ([]byte, error) { + // TODO: reject if datagrams are not negotiated (yet) + return s.datagramStream.ReceiveDatagram(ctx) +} + +// A RequestStream is a low-level abstraction representing an HTTP/3 request stream. +// It decouples sending of the HTTP request from reading the HTTP response, allowing +// the application to optimistically use the stream (and, for example, send datagrams) +// before receiving the response. +// +// This is only needed for advanced use case, e.g. WebTransport and the various +// MASQUE proxying protocols. +type RequestStream struct { + str *Stream + + responseBody io.ReadCloser // set by ReadResponse + + decoder *qpack.Decoder + requestWriter *requestWriter + maxHeaderBytes int + reqDone chan<- struct{} + disableCompression bool + response *http.Response + + sentRequest bool + requestedGzip bool + isConnect bool +} + +func newRequestStream( + str *Stream, + requestWriter *requestWriter, + reqDone chan<- struct{}, + decoder *qpack.Decoder, + disableCompression bool, + maxHeaderBytes int, + rsp *http.Response, +) *RequestStream { + return &RequestStream{ + str: str, + requestWriter: requestWriter, + reqDone: reqDone, + decoder: decoder, + disableCompression: disableCompression, + maxHeaderBytes: maxHeaderBytes, + response: rsp, + } +} + +// Read reads data from the underlying stream. +// +// It can only be used after the request has been sent (using SendRequestHeader) +// and the response has been consumed (using ReadResponse). +func (s *RequestStream) Read(b []byte) (int, error) { + if s.responseBody == nil { + return 0, errors.New("http3: invalid use of RequestStream.Read before ReadResponse") + } + return s.responseBody.Read(b) +} + +// StreamID returns the QUIC stream ID of the underlying QUIC stream. +func (s *RequestStream) StreamID() quic.StreamID { + return s.str.StreamID() +} + +// Write writes data to the stream. +// +// It can only be used after the request has been sent (using SendRequestHeader). +func (s *RequestStream) Write(b []byte) (int, error) { + if !s.sentRequest { + return 0, errors.New("http3: invalid use of RequestStream.Write before SendRequestHeader") + } + return s.str.Write(b) +} + +// Close closes the send-direction of the stream. +// It does not close the receive-direction of the stream. +func (s *RequestStream) Close() error { + return s.str.Close() +} + +// CancelRead aborts receiving on this stream. +// See [quic.Stream.CancelRead] for more details. +func (s *RequestStream) CancelRead(errorCode quic.StreamErrorCode) { + s.str.CancelRead(errorCode) +} + +// CancelWrite aborts sending on this stream. +// See [quic.Stream.CancelWrite] for more details. +func (s *RequestStream) CancelWrite(errorCode quic.StreamErrorCode) { + s.str.CancelWrite(errorCode) +} + +// Context returns a context derived from the underlying QUIC stream's context. +// See [quic.Stream.Context] for more details. +func (s *RequestStream) Context() context.Context { + return s.str.Context() +} + +// SetReadDeadline sets the deadline for Read calls. +func (s *RequestStream) SetReadDeadline(t time.Time) error { + return s.str.SetReadDeadline(t) +} + +// SetWriteDeadline sets the deadline for Write calls. +func (s *RequestStream) SetWriteDeadline(t time.Time) error { + return s.str.SetWriteDeadline(t) +} + +// SetDeadline sets the read and write deadlines associated with the stream. +// It is equivalent to calling both SetReadDeadline and SetWriteDeadline. +func (s *RequestStream) SetDeadline(t time.Time) error { + return s.str.SetDeadline(t) +} + +// SendDatagrams send a new HTTP Datagram (RFC 9297). +// +// It is only possible to send datagrams if the server enabled support for this extension. +// It is recommended (though not required) to send the request before calling this method, +// as the server might drop datagrams which it can't associate with an existing request. +func (s *RequestStream) SendDatagram(b []byte) error { + return s.str.SendDatagram(b) +} + +// ReceiveDatagram receives HTTP Datagrams (RFC 9297). +// +// It is only possible if support for HTTP Datagrams was enabled, using the EnableDatagram +// option on the [Transport]. +func (s *RequestStream) ReceiveDatagram(ctx context.Context) ([]byte, error) { + return s.str.ReceiveDatagram(ctx) +} + +// SendRequestHeader sends the HTTP request. +// +// It can only used for requests that don't have a request body. +// It is invalid to call it more than once. +// It is invalid to call it after Write has been called. +func (s *RequestStream) SendRequestHeader(req *http.Request) error { + if req.Body != nil && req.Body != http.NoBody { + return errors.New("http3: invalid use of RequestStream.SendRequestHeader with a request that has a request body") + } + return s.sendRequestHeader(req) +} + +func (s *RequestStream) sendRequestHeader(req *http.Request) error { + if s.sentRequest { + return errors.New("http3: invalid duplicate use of RequestStream.SendRequestHeader") + } + if !s.disableCompression && req.Method != http.MethodHead && + req.Header.Get("Accept-Encoding") == "" && req.Header.Get("Range") == "" { + s.requestedGzip = true + } + s.isConnect = req.Method == http.MethodConnect + s.sentRequest = true + return s.requestWriter.WriteRequestHeader(s.str.datagramStream, req, s.requestedGzip, s.str.StreamID(), s.str.qlogger) +} + +// ReadResponse reads the HTTP response from the stream. +// +// It must be called after sending the request (using SendRequestHeader). +// It is invalid to call it more than once. +// It doesn't set Response.Request and Response.TLS. +// It is invalid to call it after Read has been called. +func (s *RequestStream) ReadResponse() (*http.Response, error) { + if !s.sentRequest { + return nil, errors.New("http3: invalid duplicate use of RequestStream.ReadResponse before SendRequestHeader") + } + frame, err := s.str.frameParser.ParseNext(s.str.qlogger) + if err != nil { + s.str.CancelRead(quic.StreamErrorCode(ErrCodeFrameError)) + s.str.CancelWrite(quic.StreamErrorCode(ErrCodeFrameError)) + return nil, fmt.Errorf("http3: parsing frame failed: %w", err) + } + hf, ok := frame.(*headersFrame) + if !ok { + s.str.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "expected first frame to be a HEADERS frame") + return nil, errors.New("http3: expected first frame to be a HEADERS frame") + } + if hf.Length > uint64(s.maxHeaderBytes) { + maybeQlogInvalidHeadersFrame(s.str.qlogger, s.str.StreamID(), hf.Length) + s.str.CancelRead(quic.StreamErrorCode(ErrCodeFrameError)) + s.str.CancelWrite(quic.StreamErrorCode(ErrCodeFrameError)) + return nil, fmt.Errorf("http3: HEADERS frame too large: %d bytes (max: %d)", hf.Length, s.maxHeaderBytes) + } + headerBlock := make([]byte, hf.Length) + if _, err := io.ReadFull(s.str.datagramStream, headerBlock); err != nil { + maybeQlogInvalidHeadersFrame(s.str.qlogger, s.str.StreamID(), hf.Length) + s.str.CancelRead(quic.StreamErrorCode(ErrCodeRequestIncomplete)) + s.str.CancelWrite(quic.StreamErrorCode(ErrCodeRequestIncomplete)) + return nil, fmt.Errorf("http3: failed to read response headers: %w", err) + } + decodeFn := s.decoder.Decode(headerBlock) + var hfs []qpack.HeaderField + if s.str.qlogger != nil { + hfs = make([]qpack.HeaderField, 0, 16) + } + res := s.response + err = updateResponseFromHeaders(res, decodeFn, s.maxHeaderBytes, &hfs) + if s.str.qlogger != nil { + qlogParsedHeadersFrame(s.str.qlogger, s.str.StreamID(), hf, hfs) + } + if err != nil { + errCode := ErrCodeMessageError + var qpackErr *qpackError + if errors.As(err, &qpackErr) { + errCode = ErrCodeQPACKDecompressionFailed + } + s.str.CancelRead(quic.StreamErrorCode(errCode)) + s.str.CancelWrite(quic.StreamErrorCode(errCode)) + return nil, fmt.Errorf("http3: invalid response: %w", err) + } + + // Check that the server doesn't send more data in DATA frames than indicated by the Content-Length header (if set). + // See section 4.1.2 of RFC 9114. + respBody := newResponseBody(s.str, res.ContentLength, s.reqDone) + + // Rules for when to set Content-Length are defined in https://tools.ietf.org/html/rfc7230#section-3.3.2. + isInformational := res.StatusCode >= 100 && res.StatusCode < 200 + isNoContent := res.StatusCode == http.StatusNoContent + isSuccessfulConnect := s.isConnect && res.StatusCode >= 200 && res.StatusCode < 300 + if (isInformational || isNoContent || isSuccessfulConnect) && res.ContentLength == -1 { + res.ContentLength = 0 + } + if s.requestedGzip && res.Header.Get("Content-Encoding") == "gzip" { + res.Header.Del("Content-Encoding") + res.Header.Del("Content-Length") + res.ContentLength = -1 + s.responseBody = newGzipReader(respBody) + res.Uncompressed = true + } else { + s.responseBody = respBody + } + res.Body = s.responseBody + return res, nil +} + +type tracingReader struct { + io.Reader + readFirst bool + trace *httptrace.ClientTrace +} + +func (r *tracingReader) Read(b []byte) (int, error) { + n, err := r.Reader.Read(b) + if n > 0 && !r.readFirst { + traceGotFirstResponseByte(r.trace) + r.readFirst = true + } + return n, err +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/trace.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/trace.go index 76de6fb1a..2bc70017a 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/trace.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/trace.go @@ -19,7 +19,7 @@ func traceGetConn(trace *httptrace.ClientTrace, hostPort string) { // fakeConn is a wrapper for quic.EarlyConnection // because the quic connection does not implement net.Conn. type fakeConn struct { - conn quic.EarlyConnection + conn *quic.Conn } func (c *fakeConn) Close() error { panic("connection operation prohibited") } @@ -31,7 +31,7 @@ func (c *fakeConn) SetWriteDeadline(t time.Time) error { panic("connection opera func (c *fakeConn) RemoteAddr() net.Addr { return c.conn.RemoteAddr() } func (c *fakeConn) LocalAddr() net.Addr { return c.conn.LocalAddr() } -func traceGotConn(trace *httptrace.ClientTrace, conn quic.EarlyConnection, reused bool) { +func traceGotConn(trace *httptrace.ClientTrace, conn *quic.Conn, reused bool) { if trace != nil && trace.GotConn != nil { trace.GotConn(httptrace.GotConnInfo{ Conn: &fakeConn{conn: conn}, diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/transport.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/transport.go index b90f11909..859977945 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/transport.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/http3/transport.go @@ -10,6 +10,7 @@ import ( "net" "net/http" "net/http/httptrace" + "net/url" "strings" "sync" "sync/atomic" @@ -17,7 +18,6 @@ import ( "golang.org/x/net/http/httpguts" "github.com/quic-go/quic-go" - "github.com/quic-go/quic-go/internal/protocol" ) // Settings are HTTP/3 settings that apply to the underlying connection. @@ -38,7 +38,7 @@ type RoundTripOpt struct { } type clientConn interface { - OpenRequestStream(context.Context) (RequestStream, error) + OpenRequestStream(context.Context) (*RequestStream, error) RoundTrip(*http.Request) (*http.Response, error) } @@ -46,7 +46,7 @@ type roundTripperWithCount struct { cancel context.CancelFunc dialing chan struct{} // closed as soon as quic.Dial(Early) returned dialErr error - conn quic.EarlyConnection + conn *quic.Conn clientConn clientConn useCount atomic.Int64 @@ -75,7 +75,7 @@ type Transport struct { // connections for requests. // If Dial is nil, a UDPConn will be created at the first request // and will be reused for subsequent connections to other servers. - Dial func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) + Dial func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (*quic.Conn, error) // Enable support for HTTP/3 datagrams (RFC 9297). // If a QUICConfig is set, datagram support also needs to be enabled on the QUIC layer by setting EnableDatagrams. @@ -88,7 +88,7 @@ type Transport struct { // MaxResponseHeaderBytes specifies a limit on how many response bytes are // allowed in the server's response header. // Zero means to use a default limit. - MaxResponseHeaderBytes int64 + MaxResponseHeaderBytes int // DisableCompression, if true, prevents the Transport from requesting compression with an // "Accept-Encoding: gzip" request header when the Request contains no existing Accept-Encoding value. @@ -97,8 +97,8 @@ type Transport struct { // However, if the user explicitly requested gzip it is not automatically uncompressed. DisableCompression bool - StreamHijacker func(FrameType, quic.ConnectionTracingID, quic.Stream, error) (hijacked bool, err error) - UniStreamHijacker func(StreamType, quic.ConnectionTracingID, quic.ReceiveStream, error) (hijacked bool) + StreamHijacker func(FrameType, quic.ConnectionTracingID, *quic.Stream, error) (hijacked bool, err error) + UniStreamHijacker func(StreamType, quic.ConnectionTracingID, *quic.ReceiveStream, error) (hijacked bool) Logger *slog.Logger @@ -107,10 +107,11 @@ type Transport struct { initOnce sync.Once initErr error - newClientConn func(quic.EarlyConnection) clientConn + newClientConn func(*quic.Conn) clientConn clients map[string]*roundTripperWithCount transport *quic.Transport + closed bool } var ( @@ -118,15 +119,16 @@ var ( _ io.Closer = &Transport{} ) -// Deprecated: RoundTripper was renamed to Transport. -type RoundTripper = Transport - -// ErrNoCachedConn is returned when Transport.OnlyCachedConn is set -var ErrNoCachedConn = errors.New("http3: no cached connection was available") +var ( + // ErrNoCachedConn is returned when Transport.OnlyCachedConn is set + ErrNoCachedConn = errors.New("http3: no cached connection was available") + // ErrTransportClosed is returned when attempting to use a closed Transport + ErrTransportClosed = errors.New("http3: transport is closed") +) func (t *Transport) init() error { if t.newClientConn == nil { - t.newClientConn = func(conn quic.EarlyConnection) clientConn { + t.newClientConn = func(conn *quic.Conn) clientConn { return newClientConn( conn, t.EnableDatagrams, @@ -148,7 +150,7 @@ func (t *Transport) init() error { } if len(t.QUICConfig.Versions) == 0 { t.QUICConfig = t.QUICConfig.Clone() - t.QUICConfig.Versions = []quic.Version{protocol.SupportedVersions[0]} + t.QUICConfig.Versions = []quic.Version{quic.SupportedVersions()[0]} } if len(t.QUICConfig.Versions) != 1 { return errors.New("can only use a single QUIC version for dialing a HTTP/3 connection") @@ -156,6 +158,13 @@ func (t *Transport) init() error { if t.QUICConfig.MaxIncomingStreams == 0 { t.QUICConfig.MaxIncomingStreams = -1 // don't allow any bidirectional streams } + if t.Dial == nil { + udpConn, err := net.ListenUDP("udp", nil) + if err != nil { + return err + } + t.transport = &quic.Transport{Conn: udpConn} + } return nil } @@ -203,8 +212,12 @@ func (t *Transport) roundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res } } - trace := httptrace.ContextClientTrace(req.Context()) + return t.doRoundTripOpt(req, opt, false) +} + +func (t *Transport) doRoundTripOpt(req *http.Request, opt RoundTripOpt, isRetried bool) (*http.Response, error) { hostname := authorityAddr(hostnameFromURL(req.URL)) + trace := httptrace.ContextClientTrace(req.Context()) traceGetConn(trace, hostname) cl, isReused, err := t.getClient(req.Context(), hostname, opt.OnlyCachedConn) if err != nil { @@ -221,8 +234,8 @@ func (t *Transport) roundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res t.removeClient(hostname) return nil, cl.dialErr } - traceGotConn(trace, cl.conn, isReused) defer cl.useCount.Add(-1) + traceGotConn(trace, cl.conn, isReused) rsp, err := cl.clientConn.RoundTrip(req) if err != nil { // request aborted due to context cancellation @@ -231,28 +244,51 @@ func (t *Transport) roundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res return nil, err default: } - - // Retry the request on a new connection if: - // 1. it was sent on a reused connection, - // 2. this connection is now closed, - // 3. and the error is a timeout error. - select { - case <-cl.conn.Context().Done(): - t.removeClient(hostname) - if isReused { - var nerr net.Error - if errors.As(err, &nerr) && nerr.Timeout() { - return t.RoundTripOpt(req, opt) - } - } + if isRetried { return nil, err - default: + } + + t.removeClient(hostname) + req, err = canRetryRequest(err, req) + if err != nil { return nil, err } + return t.doRoundTripOpt(req, opt, true) } return rsp, nil } +func canRetryRequest(err error, req *http.Request) (*http.Request, error) { + // error occurred while opening the stream, we can be sure that the request wasn't sent out + var connErr *errConnUnusable + if errors.As(err, &connErr) { + return req, nil + } + + // If the request stream is reset, we can only be sure that the request wasn't processed + // if the error code is H3_REQUEST_REJECTED. + var e *Error + if !errors.As(err, &e) || e.ErrorCode != ErrCodeRequestRejected { + return nil, err + } + // if the body is nil (or http.NoBody), it's safe to reuse this request and its body + if req.Body == nil || req.Body == http.NoBody { + return req, nil + } + // if the request body can be reset back to its original state via req.GetBody, do that + if req.GetBody != nil { + newBody, err := req.GetBody() + if err != nil { + return nil, err + } + reqCopy := *req + reqCopy.Body = newBody + req = &reqCopy + return &reqCopy, nil + } + return nil, fmt.Errorf("http3: Transport: cannot retry err [%w] after Request.Body was written; define Request.GetBody to avoid this error", err) +} + // RoundTrip does a round trip. func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { return t.RoundTripOpt(req, RoundTripOpt{}) @@ -261,6 +297,9 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { func (t *Transport) getClient(ctx context.Context, hostname string, onlyCached bool) (rtc *roundTripperWithCount, isReused bool, err error) { t.mutex.Lock() defer t.mutex.Unlock() + if t.closed { + return nil, false, ErrTransportClosed + } if t.clients == nil { t.clients = make(map[string]*roundTripperWithCount) @@ -306,7 +345,7 @@ func (t *Transport) getClient(ctx context.Context, hostname string, onlyCached b return cl, isReused, nil } -func (t *Transport) dial(ctx context.Context, hostname string) (quic.EarlyConnection, clientConn, error) { +func (t *Transport) dial(ctx context.Context, hostname string) (*quic.Conn, clientConn, error) { var tlsConf *tls.Config if t.TLSClientConfig == nil { tlsConf = &tls.Config{} @@ -322,18 +361,11 @@ func (t *Transport) dial(ctx context.Context, hostname string) (quic.EarlyConnec tlsConf.ServerName = sni } // Replace existing ALPNs by H3 - tlsConf.NextProtos = []string{versionToALPN(t.QUICConfig.Versions[0])} + tlsConf.NextProtos = []string{NextProtoH3} dial := t.Dial if dial == nil { - if t.transport == nil { - udpConn, err := net.ListenUDP("udp", nil) - if err != nil { - return nil, nil, err - } - t.transport = &quic.Transport{Conn: udpConn} - } - dial = func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { + dial = func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (*quic.Conn, error) { network := "udp" udpAddr, err := t.resolveUDPAddr(ctx, network, addr) if err != nil { @@ -393,7 +425,7 @@ func (t *Transport) removeClient(hostname string) { // // Obtaining a ClientConn is only needed for more advanced use cases, such as // using Extended CONNECT for WebTransport or the various MASQUE protocols. -func (t *Transport) NewClientConn(conn quic.Connection) *ClientConn { +func (t *Transport) NewClientConn(conn *quic.Conn) *ClientConn { return newClientConn( conn, t.EnableDatagrams, @@ -407,6 +439,7 @@ func (t *Transport) NewClientConn(conn quic.Connection) *ClientConn { } // Close closes the QUIC connections that this Transport has used. +// A Transport cannot be used after it has been closed. func (t *Transport) Close() error { t.mutex.Lock() defer t.mutex.Unlock() @@ -425,9 +458,17 @@ func (t *Transport) Close() error { } t.transport = nil } + t.closed = true return nil } +func hostnameFromURL(url *url.URL) string { + if url != nil { + return url.Host + } + return "" +} + func validMethod(method string) bool { /* Method = "OPTIONS" ; Section 9.2 diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/interface.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/interface.go index 7f3c40c28..984e3b12f 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/interface.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/interface.go @@ -4,13 +4,13 @@ import ( "context" "crypto/tls" "errors" - "io" "net" + "slices" "time" "github.com/quic-go/quic-go/internal/handshake" "github.com/quic-go/quic-go/internal/protocol" - "github.com/quic-go/quic-go/logging" + "github.com/quic-go/quic-go/qlogwriter" ) // The StreamID is the ID of a QUIC stream. @@ -26,10 +26,17 @@ const ( Version2 = protocol.Version2 ) +// SupportedVersions returns the support versions, sorted in descending order of preference. +func SupportedVersions() []Version { + // clone the slice to prevent the caller from modifying the slice + return slices.Clone(protocol.SupportedVersions) +} + // A ClientToken is a token received by the client. // It can be used to skip address validation on future connection attempts. type ClientToken struct { data []byte + rtt time.Duration } type TokenStore interface { @@ -44,185 +51,31 @@ type TokenStore interface { } // Err0RTTRejected is the returned from: -// * Open{Uni}Stream{Sync} -// * Accept{Uni}Stream -// * Stream.Read and Stream.Write +// - Open{Uni}Stream{Sync} +// - Accept{Uni}Stream +// - Stream.Read and Stream.Write +// // when the server rejects a 0-RTT connection attempt. var Err0RTTRejected = errors.New("0-RTT rejected") -// ConnectionTracingKey can be used to associate a ConnectionTracer with a Connection. -// It is set on the Connection.Context() context, +// ConnectionTracingKey can be used to associate a [logging.ConnectionTracer] with a [Conn]. +// It is set on the Conn.Context() context, // as well as on the context passed to logging.Tracer.NewConnectionTracer. +// // Deprecated: Applications can set their own tracing key using Transport.ConnContext. var ConnectionTracingKey = connTracingCtxKey{} // ConnectionTracingID is the type of the context value saved under the ConnectionTracingKey. +// // Deprecated: Applications can set their own tracing key using Transport.ConnContext. type ConnectionTracingID uint64 type connTracingCtxKey struct{} // QUICVersionContextKey can be used to find out the QUIC version of a TLS handshake from the -// context returned by tls.Config.ClientHelloInfo.Context. +// context returned by tls.Config.ClientInfo.Context. var QUICVersionContextKey = handshake.QUICVersionContextKey -// Stream is the interface implemented by QUIC streams -// In addition to the errors listed on the Connection, -// calls to stream functions can return a StreamError if the stream is canceled. -type Stream interface { - ReceiveStream - SendStream - // SetDeadline sets the read and write deadlines associated - // with the connection. It is equivalent to calling both - // SetReadDeadline and SetWriteDeadline. - SetDeadline(t time.Time) error -} - -// A ReceiveStream is a unidirectional Receive Stream. -type ReceiveStream interface { - // StreamID returns the stream ID. - StreamID() StreamID - // Read reads data from the stream. - // Read can be made to time out and return a net.Error with Timeout() == true - // after a fixed time limit; see SetDeadline and SetReadDeadline. - // If the stream was canceled by the peer, the error is a StreamError and - // Remote == true. - // If the connection was closed due to a timeout, the error satisfies - // the net.Error interface, and Timeout() will be true. - io.Reader - // CancelRead aborts receiving on this stream. - // It will ask the peer to stop transmitting stream data. - // Read will unblock immediately, and future Read calls will fail. - // When called multiple times or after reading the io.EOF it is a no-op. - CancelRead(StreamErrorCode) - // SetReadDeadline sets the deadline for future Read calls and - // any currently-blocked Read call. - // A zero value for t means Read will not time out. - SetReadDeadline(t time.Time) error -} - -// A SendStream is a unidirectional Send Stream. -type SendStream interface { - // StreamID returns the stream ID. - StreamID() StreamID - // Write writes data to the stream. - // Write can be made to time out and return a net.Error with Timeout() == true - // after a fixed time limit; see SetDeadline and SetWriteDeadline. - // If the stream was canceled by the peer, the error is a StreamError and - // Remote == true. - // If the connection was closed due to a timeout, the error satisfies - // the net.Error interface, and Timeout() will be true. - io.Writer - // Close closes the write-direction of the stream. - // Future calls to Write are not permitted after calling Close. - // It must not be called concurrently with Write. - // It must not be called after calling CancelWrite. - io.Closer - // CancelWrite aborts sending on this stream. - // Data already written, but not yet delivered to the peer is not guaranteed to be delivered reliably. - // Write will unblock immediately, and future calls to Write will fail. - // When called multiple times it is a no-op. - // When called after Close, it aborts delivery. Note that there is no guarantee if - // the peer will receive the FIN or the reset first. - CancelWrite(StreamErrorCode) - // The Context is canceled as soon as the write-side of the stream is closed. - // This happens when Close() or CancelWrite() is called, or when the peer - // cancels the read-side of their stream. - // The cancellation cause is set to the error that caused the stream to - // close, or `context.Canceled` in case the stream is closed without error. - Context() context.Context - // SetWriteDeadline sets the deadline for future Write calls - // and any currently-blocked Write call. - // Even if write times out, it may return n > 0, indicating that - // some data was successfully written. - // A zero value for t means Write will not time out. - SetWriteDeadline(t time.Time) error -} - -// A Connection is a QUIC connection between two peers. -// Calls to the connection (and to streams) can return the following types of errors: -// * ApplicationError: for errors triggered by the application running on top of QUIC -// * TransportError: for errors triggered by the QUIC transport (in many cases a misbehaving peer) -// * IdleTimeoutError: when the peer goes away unexpectedly (this is a net.Error timeout error) -// * HandshakeTimeoutError: when the cryptographic handshake takes too long (this is a net.Error timeout error) -// * StatelessResetError: when we receive a stateless reset -// * VersionNegotiationError: returned by the client, when there's no version overlap between the peers -type Connection interface { - // AcceptStream returns the next stream opened by the peer, blocking until one is available. - // If the connection was closed due to a timeout, the error satisfies - // the net.Error interface, and Timeout() will be true. - AcceptStream(context.Context) (Stream, error) - // AcceptUniStream returns the next unidirectional stream opened by the peer, blocking until one is available. - // If the connection was closed due to a timeout, the error satisfies - // the net.Error interface, and Timeout() will be true. - AcceptUniStream(context.Context) (ReceiveStream, error) - // OpenStream opens a new bidirectional QUIC stream. - // There is no signaling to the peer about new streams: - // The peer can only accept the stream after data has been sent on the stream, - // or the stream has been reset or closed. - // When reaching the peer's stream limit, it is not possible to open a new stream until the - // peer raises the stream limit. In that case, a StreamLimitReachedError is returned. - OpenStream() (Stream, error) - // OpenStreamSync opens a new bidirectional QUIC stream. - // It blocks until a new stream can be opened. - // There is no signaling to the peer about new streams: - // The peer can only accept the stream after data has been sent on the stream, - // or the stream has been reset or closed. - OpenStreamSync(context.Context) (Stream, error) - // OpenUniStream opens a new outgoing unidirectional QUIC stream. - // There is no signaling to the peer about new streams: - // The peer can only accept the stream after data has been sent on the stream, - // or the stream has been reset or closed. - // When reaching the peer's stream limit, it is not possible to open a new stream until the - // peer raises the stream limit. In that case, a StreamLimitReachedError is returned. - OpenUniStream() (SendStream, error) - // OpenUniStreamSync opens a new outgoing unidirectional QUIC stream. - // It blocks until a new stream can be opened. - // There is no signaling to the peer about new streams: - // The peer can only accept the stream after data has been sent on the stream, - // or the stream has been reset or closed. - OpenUniStreamSync(context.Context) (SendStream, error) - // LocalAddr returns the local address. - LocalAddr() net.Addr - // RemoteAddr returns the address of the peer. - RemoteAddr() net.Addr - // CloseWithError closes the connection with an error. - // The error string will be sent to the peer. - CloseWithError(ApplicationErrorCode, string) error - // Context returns a context that is cancelled when the connection is closed. - // The cancellation cause is set to the error that caused the connection to - // close, or `context.Canceled` in case the listener is closed first. - Context() context.Context - // ConnectionState returns basic details about the QUIC connection. - // Warning: This API should not be considered stable and might change soon. - ConnectionState() ConnectionState - - // SendDatagram sends a message using a QUIC datagram, as specified in RFC 9221. - // There is no delivery guarantee for DATAGRAM frames, they are not retransmitted if lost. - // The payload of the datagram needs to fit into a single QUIC packet. - // In addition, a datagram may be dropped before being sent out if the available packet size suddenly decreases. - // If the payload is too large to be sent at the current time, a DatagramTooLargeError is returned. - SendDatagram(payload []byte) error - // ReceiveDatagram gets a message received in a datagram, as specified in RFC 9221. - ReceiveDatagram(context.Context) ([]byte, error) -} - -// An EarlyConnection is a connection that is handshaking. -// Data sent during the handshake is encrypted using the forward secure keys. -// When using client certificates, the client's identity is only verified -// after completion of the handshake. -type EarlyConnection interface { - Connection - - // HandshakeComplete blocks until the handshake completes (or fails). - // For the client, data sent before completion of the handshake is encrypted with 0-RTT keys. - // For the server, data sent before completion of the handshake is encrypted with 1-RTT keys, - // however the client's identity is only verified once the handshake completes. - HandshakeComplete() <-chan struct{} - - NextConnection(context.Context) (Connection, error) -} - // StatelessResetKey is a key used to derive stateless reset tokens. type StatelessResetKey [32]byte @@ -234,27 +87,22 @@ type TokenGeneratorKey = handshake.TokenProtectorKey // as they are allowed by RFC 8999. type ConnectionID = protocol.ConnectionID -// ConnectionIDFromBytes interprets b as a Connection ID. It panics if b is +// ConnectionIDFromBytes interprets b as a [ConnectionID]. It panics if b is // longer than 20 bytes. func ConnectionIDFromBytes(b []byte) ConnectionID { return protocol.ParseConnectionID(b) } -// A ConnectionIDGenerator is an interface that allows clients to implement their own format -// for the Connection IDs that servers/clients use as SrcConnectionID in QUIC packets. -// -// Connection IDs generated by an implementation should always produce IDs of constant size. +// A ConnectionIDGenerator allows the application to take control over the generation of Connection IDs. +// Connection IDs generated by an implementation must be of constant length. type ConnectionIDGenerator interface { - // GenerateConnectionID generates a new ConnectionID. - // Generated ConnectionIDs should be unique and observers should not be able to correlate two ConnectionIDs. + // GenerateConnectionID generates a new Connection ID. + // Generated Connection IDs must be unique and observers should not be able to correlate two Connection IDs. GenerateConnectionID() (ConnectionID, error) - // ConnectionIDLen tells what is the length of the ConnectionIDs generated by the implementation of - // this interface. - // Effectively, this means that implementations of ConnectionIDGenerator must always return constant-size - // connection IDs. Valid lengths are between 0 and 20 and calls to GenerateConnectionID. - // 0-length ConnectionsIDs can be used when an endpoint (server or client) does not require multiplexing connections - // in the presence of a connection migration environment. + // ConnectionIDLen returns the length of Connection IDs generated by this implementation. + // Implementations must return constant-length Connection IDs with lengths between 0 and 20 bytes. + // A length of 0 can only be used when an endpoint doesn't need to multiplex connections during migration. ConnectionIDLen() int } @@ -262,7 +110,7 @@ type ConnectionIDGenerator interface { type Config struct { // GetConfigForClient is called for incoming connections. // If the error is not nil, the connection attempt is refused. - GetConfigForClient func(info *ClientHelloInfo) (*Config, error) + GetConfigForClient func(info *ClientInfo) (*Config, error) // The QUIC versions that can be negotiated. // If not set, it uses all versions available. Versions []Version @@ -308,7 +156,7 @@ type Config struct { // limit the memory usage. // To avoid deadlocks, it is not valid to call other functions on the connection or on streams // in this callback. - AllowConnectionWindowIncrease func(conn Connection, delta uint64) bool + AllowConnectionWindowIncrease func(conn *Conn, delta uint64) bool // MaxIncomingStreams is the maximum number of concurrent bidirectional streams that a peer is allowed to open. // If not set, it will default to 100. // If set to a negative value, it doesn't allow any bidirectional streams. @@ -323,10 +171,10 @@ type Config struct { // If set to 0, then no keep alive is sent. Otherwise, the keep alive is sent on that period (or at most // every half of MaxIdleTimeout, whichever is smaller). KeepAlivePeriod time.Duration - // InitialPacketSize is the initial size of packets sent. - // It is usually not necessary to manually set this value, - // since Path MTU discovery very quickly finds the path's MTU. - // If set too high, the path might not support packets that large, leading to a timeout of the QUIC handshake. + // InitialPacketSize is the initial size (and the lower limit) for packets sent. + // Under most circumstances, it is not necessary to manually set this value, + // since path MTU discovery quickly finds the path's MTU. + // If set too high, the path might not support packets of that size, leading to a timeout of the QUIC handshake. // Values below 1200 are invalid. InitialPacketSize uint16 // DisablePathMTUDiscovery disables Path MTU Discovery (RFC 8899). @@ -338,11 +186,20 @@ type Config struct { Allow0RTT bool // Enable QUIC datagram support (RFC 9221). EnableDatagrams bool - Tracer func(context.Context, logging.Perspective, ConnectionID) *logging.ConnectionTracer + // Enable QUIC Stream Resets with Partial Delivery. + // See https://datatracker.ietf.org/doc/html/draft-ietf-quic-reliable-stream-reset-07. + EnableStreamResetPartialDelivery bool + + Tracer func(ctx context.Context, isClient bool, connID ConnectionID) qlogwriter.Trace } // ClientHelloInfo contains information about an incoming connection attempt. -type ClientHelloInfo struct { +// +// Deprecated: Use ClientInfo instead. +type ClientHelloInfo = ClientInfo + +// ClientInfo contains information about an incoming connection attempt. +type ClientInfo struct { // RemoteAddr is the remote address on the Initial packet. // Unless AddrVerified is set, the address is not yet verified, and could be a spoofed IP address. RemoteAddr net.Addr @@ -352,19 +209,21 @@ type ClientHelloInfo struct { AddrVerified bool } -// ConnectionState records basic details about a QUIC connection +// ConnectionState records basic details about a QUIC connection. type ConnectionState struct { // TLS contains information about the TLS connection state, incl. the tls.ConnectionState. TLS tls.ConnectionState // SupportsDatagrams indicates whether the peer advertised support for QUIC datagrams (RFC 9221). - // When true, datagrams can be sent using the Connection's SendDatagram method. + // When true, datagrams can be sent using the Conn's SendDatagram method. // This is a unilateral declaration by the peer - receiving datagrams is only possible if // datagram support was enabled locally via Config.EnableDatagrams. SupportsDatagrams bool + // SupportsStreamResetPartialDelivery indicates whether the peer advertised support for QUIC Stream Resets with Partial Delivery. + SupportsStreamResetPartialDelivery bool // Used0RTT says if 0-RTT resumption was used. Used0RTT bool // Version is the QUIC version of the QUIC connection. Version Version - // GSO says if generic segmentation offload is used + // GSO says if generic segmentation offload is used. GSO bool } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/ack_eliciting.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/ack_eliciting.go index 34506b12e..8d8436123 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/ack_eliciting.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/ack_eliciting.go @@ -2,6 +2,19 @@ package ackhandler import "github.com/quic-go/quic-go/internal/wire" +// IsFrameTypeAckEliciting returns true if the frame is ack-eliciting. +func IsFrameTypeAckEliciting(t wire.FrameType) bool { + //nolint:exhaustive // The default case catches the rest. + switch t { + case wire.FrameTypeAck, wire.FrameTypeAckECN: + return false + case wire.FrameTypeConnectionClose, wire.FrameTypeApplicationClose: + return false + default: + return true + } +} + // IsFrameAckEliciting returns true if the frame is ack-eliciting. func IsFrameAckEliciting(f wire.Frame) bool { _, isAck := f.(*wire.AckFrame) diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/ackhandler.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/ackhandler.go index 6f890b4d3..c399358f2 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/ackhandler.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/ackhandler.go @@ -3,7 +3,7 @@ package ackhandler import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" - "github.com/quic-go/quic-go/logging" + "github.com/quic-go/quic-go/qlogwriter" ) // NewAckHandler creates a new SentPacketHandler and a new ReceivedPacketHandler. @@ -13,12 +13,13 @@ func NewAckHandler( initialPacketNumber protocol.PacketNumber, initialMaxDatagramSize protocol.ByteCount, rttStats *utils.RTTStats, + connStats *utils.ConnectionStats, clientAddressValidated bool, enableECN bool, pers protocol.Perspective, - tracer *logging.ConnectionTracer, + qlogger qlogwriter.Recorder, logger utils.Logger, ) (SentPacketHandler, ReceivedPacketHandler) { - sph := newSentPacketHandler(initialPacketNumber, initialMaxDatagramSize, rttStats, clientAddressValidated, enableECN, pers, tracer, logger) + sph := newSentPacketHandler(initialPacketNumber, initialMaxDatagramSize, rttStats, connStats, clientAddressValidated, enableECN, pers, qlogger, logger) return sph, newReceivedPacketHandler(sph, logger) } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/ecn.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/ecn.go index 68415ac6c..123d3a341 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/ecn.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/ecn.go @@ -5,7 +5,8 @@ import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" - "github.com/quic-go/quic-go/logging" + "github.com/quic-go/quic-go/qlog" + "github.com/quic-go/quic-go/qlogwriter" ) type ecnState uint8 @@ -18,13 +19,29 @@ const ( ecnStateFailed ) +const ( + // ecnFailedNoECNCounts is emitted when an ACK acknowledges ECN-marked packets, + // but doesn't contain any ECN counts + ecnFailedNoECNCounts = "ACK doesn't contain ECN marks" + // ecnFailedDecreasedECNCounts is emitted when an ACK frame decreases ECN counts + ecnFailedDecreasedECNCounts = "ACK decreases ECN counts" + // ecnFailedLostAllTestingPackets is emitted when all ECN testing packets are declared lost + ecnFailedLostAllTestingPackets = "all ECN testing packets declared lost" + // ecnFailedMoreECNCountsThanSent is emitted when an ACK contains more ECN counts than ECN-marked packets were sent + ecnFailedMoreECNCountsThanSent = "ACK contains more ECN counts than ECN-marked packets sent" + // ecnFailedTooFewECNCounts is emitted when an ACK contains fewer ECN counts than it acknowledges packets + ecnFailedTooFewECNCounts = "ACK contains fewer new ECN counts than acknowledged ECN-marked packets" + // ecnFailedManglingDetected is emitted when the path marks all ECN-marked packets as CE + ecnFailedManglingDetected = "ECN mangling detected" +) + // must fit into an uint8, otherwise numSentTesting and numLostTesting must have a larger type const numECNTestingPackets = 10 type ecnHandler interface { SentPacket(protocol.PacketNumber, protocol.ECN) Mode() protocol.ECN - HandleNewlyAcked(packets []*packet, ect0, ect1, ecnce int64) (congested bool) + HandleNewlyAcked(packets []packetWithPacketNumber, ect0, ect1, ecnce int64) (congested bool) LostPacket(protocol.PacketNumber) } @@ -45,20 +62,20 @@ type ecnTracker struct { numSentECT0, numSentECT1 int64 numAckedECT0, numAckedECT1, numAckedECNCE int64 - tracer *logging.ConnectionTracer - logger utils.Logger + qlogger qlogwriter.Recorder + logger utils.Logger } var _ ecnHandler = &ecnTracker{} -func newECNTracker(logger utils.Logger, tracer *logging.ConnectionTracer) *ecnTracker { +func newECNTracker(logger utils.Logger, qlogger qlogwriter.Recorder) *ecnTracker { return &ecnTracker{ firstTestingPacket: protocol.InvalidPacketNumber, lastTestingPacket: protocol.InvalidPacketNumber, firstCapablePacket: protocol.InvalidPacketNumber, state: ecnStateInitial, logger: logger, - tracer: tracer, + qlogger: qlogger, } } @@ -92,8 +109,10 @@ func (e *ecnTracker) SentPacket(pn protocol.PacketNumber, ecn protocol.ECN) { e.firstTestingPacket = pn } if e.numSentECT0+e.numSentECT1 >= numECNTestingPackets { - if e.tracer != nil && e.tracer.ECNStateUpdated != nil { - e.tracer.ECNStateUpdated(logging.ECNStateUnknown, logging.ECNTriggerNoTrigger) + if e.qlogger != nil { + e.qlogger.RecordEvent(qlog.ECNStateUpdated{ + State: qlog.ECNStateUnknown, + }) } e.state = ecnStateUnknown e.lastTestingPacket = pn @@ -103,8 +122,10 @@ func (e *ecnTracker) SentPacket(pn protocol.PacketNumber, ecn protocol.ECN) { func (e *ecnTracker) Mode() protocol.ECN { switch e.state { case ecnStateInitial: - if e.tracer != nil && e.tracer.ECNStateUpdated != nil { - e.tracer.ECNStateUpdated(logging.ECNStateTesting, logging.ECNTriggerNoTrigger) + if e.qlogger != nil { + e.qlogger.RecordEvent(qlog.ECNStateUpdated{ + State: qlog.ECNStateTesting, + }) } e.state = ecnStateTesting return e.Mode() @@ -131,8 +152,11 @@ func (e *ecnTracker) LostPacket(pn protocol.PacketNumber) { } if e.numLostTesting >= e.numSentTesting { e.logger.Debugf("Disabling ECN. All testing packets were lost.") - if e.tracer != nil && e.tracer.ECNStateUpdated != nil { - e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedLostAllTestingPackets) + if e.qlogger != nil { + e.qlogger.RecordEvent(qlog.ECNStateUpdated{ + State: qlog.ECNStateFailed, + Trigger: ecnFailedLostAllTestingPackets, + }) } e.state = ecnStateFailed return @@ -144,7 +168,7 @@ func (e *ecnTracker) LostPacket(pn protocol.PacketNumber) { // HandleNewlyAcked handles the ECN counts on an ACK frame. // It must only be called for ACK frames that increase the largest acknowledged packet number, // see section 13.4.2.1 of RFC 9000. -func (e *ecnTracker) HandleNewlyAcked(packets []*packet, ect0, ect1, ecnce int64) (congested bool) { +func (e *ecnTracker) HandleNewlyAcked(packets []packetWithPacketNumber, ect0, ect1, ecnce int64) (congested bool) { if e.state == ecnStateFailed { return false } @@ -153,8 +177,11 @@ func (e *ecnTracker) HandleNewlyAcked(packets []*packet, ect0, ect1, ecnce int64 // the total number of packets sent with each corresponding ECT codepoint. if ect0 > e.numSentECT0 || ect1 > e.numSentECT1 { e.logger.Debugf("Disabling ECN. Received more ECT(0) / ECT(1) acknowledgements than packets sent.") - if e.tracer != nil && e.tracer.ECNStateUpdated != nil { - e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedMoreECNCountsThanSent) + if e.qlogger != nil { + e.qlogger.RecordEvent(qlog.ECNStateUpdated{ + State: qlog.ECNStateFailed, + Trigger: ecnFailedMoreECNCountsThanSent, + }) } e.state = ecnStateFailed return false @@ -179,8 +206,11 @@ func (e *ecnTracker) HandleNewlyAcked(packets []*packet, ect0, ect1, ecnce int64 // * peers that don't report any ECN counts if (ackedECT0 > 0 || ackedECT1 > 0) && ect0 == 0 && ect1 == 0 && ecnce == 0 { e.logger.Debugf("Disabling ECN. ECN-marked packet acknowledged, but no ECN counts on ACK frame.") - if e.tracer != nil && e.tracer.ECNStateUpdated != nil { - e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedNoECNCounts) + if e.qlogger != nil { + e.qlogger.RecordEvent(qlog.ECNStateUpdated{ + State: qlog.ECNStateFailed, + Trigger: ecnFailedNoECNCounts, + }) } e.state = ecnStateFailed return false @@ -196,8 +226,11 @@ func (e *ecnTracker) HandleNewlyAcked(packets []*packet, ect0, ect1, ecnce int64 // Any decrease means that the peer's counting logic is broken. if newECT0 < 0 || newECT1 < 0 || newECNCE < 0 { e.logger.Debugf("Disabling ECN. ECN counts decreased unexpectedly.") - if e.tracer != nil && e.tracer.ECNStateUpdated != nil { - e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedDecreasedECNCounts) + if e.qlogger != nil { + e.qlogger.RecordEvent(qlog.ECNStateUpdated{ + State: qlog.ECNStateFailed, + Trigger: ecnFailedDecreasedECNCounts, + }) } e.state = ecnStateFailed return false @@ -208,8 +241,11 @@ func (e *ecnTracker) HandleNewlyAcked(packets []*packet, ect0, ect1, ecnce int64 // This could be the result of (partial) bleaching. if newECT0+newECNCE < ackedECT0 { e.logger.Debugf("Disabling ECN. Received less ECT(0) + ECN-CE than packets sent with ECT(0).") - if e.tracer != nil && e.tracer.ECNStateUpdated != nil { - e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedTooFewECNCounts) + if e.qlogger != nil { + e.qlogger.RecordEvent(qlog.ECNStateUpdated{ + State: qlog.ECNStateFailed, + Trigger: ecnFailedTooFewECNCounts, + }) } e.state = ecnStateFailed return false @@ -218,8 +254,11 @@ func (e *ecnTracker) HandleNewlyAcked(packets []*packet, ect0, ect1, ecnce int64 // the number of newly acknowledged packets sent with an ECT(1) marking. if newECT1+newECNCE < ackedECT1 { e.logger.Debugf("Disabling ECN. Received less ECT(1) + ECN-CE than packets sent with ECT(1).") - if e.tracer != nil && e.tracer.ECNStateUpdated != nil { - e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedTooFewECNCounts) + if e.qlogger != nil { + e.qlogger.RecordEvent(qlog.ECNStateUpdated{ + State: qlog.ECNStateFailed, + Trigger: ecnFailedTooFewECNCounts, + }) } e.state = ecnStateFailed return false @@ -249,8 +288,10 @@ func (e *ecnTracker) HandleNewlyAcked(packets []*packet, ect0, ect1, ecnce int64 // This check won't succeed if the path is mangling ECN-marks (i.e. rewrites all ECN-marked packets to CE). if ackedTestingPacket && (newECT0 > 0 || newECT1 > 0) { e.logger.Debugf("ECN capability confirmed.") - if e.tracer != nil && e.tracer.ECNStateUpdated != nil { - e.tracer.ECNStateUpdated(logging.ECNStateCapable, logging.ECNTriggerNoTrigger) + if e.qlogger != nil { + e.qlogger.RecordEvent(qlog.ECNStateUpdated{ + State: qlog.ECNStateCapable, + }) } e.state = ecnStateCapable } @@ -267,8 +308,11 @@ func (e *ecnTracker) failIfMangled() { if e.numSentECT0+e.numSentECT1 > numAckedECNCE { return } - if e.tracer != nil && e.tracer.ECNStateUpdated != nil { - e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedManglingDetected) + if e.qlogger != nil { + e.qlogger.RecordEvent(qlog.ECNStateUpdated{ + State: qlog.ECNStateFailed, + Trigger: ecnFailedManglingDetected, + }) } e.state = ecnStateFailed } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go index 5fcce44d2..9ee9da7d9 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go @@ -1,8 +1,7 @@ package ackhandler import ( - "time" - + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" ) @@ -10,19 +9,19 @@ import ( // SentPacketHandler handles ACKs received for outgoing packets type SentPacketHandler interface { // SentPacket may modify the packet - SentPacket(t time.Time, pn, largestAcked protocol.PacketNumber, streamFrames []StreamFrame, frames []Frame, encLevel protocol.EncryptionLevel, ecn protocol.ECN, size protocol.ByteCount, isPathMTUProbePacket, isPathProbePacket bool) + SentPacket(t monotime.Time, pn, largestAcked protocol.PacketNumber, streamFrames []StreamFrame, frames []Frame, encLevel protocol.EncryptionLevel, ecn protocol.ECN, size protocol.ByteCount, isPathMTUProbePacket, isPathProbePacket bool) // ReceivedAck processes an ACK frame. // It does not store a copy of the frame. - ReceivedAck(f *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) (bool /* 1-RTT packet acked */, error) - ReceivedBytes(_ protocol.ByteCount, rcvTime time.Time) - DropPackets(_ protocol.EncryptionLevel, rcvTime time.Time) - ResetForRetry(rcvTime time.Time) + ReceivedAck(f *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime monotime.Time) (bool /* 1-RTT packet acked */, error) + ReceivedBytes(_ protocol.ByteCount, rcvTime monotime.Time) + DropPackets(_ protocol.EncryptionLevel, rcvTime monotime.Time) + ResetForRetry(rcvTime monotime.Time) // The SendMode determines if and what kind of packets can be sent. - SendMode(now time.Time) SendMode + SendMode(now monotime.Time) SendMode // TimeUntilSend is the time when the next packet should be sent. // It is used for pacing packets. - TimeUntilSend() time.Time + TimeUntilSend() monotime.Time SetMaxDatagramSize(count protocol.ByteCount) // only to be called once the handshake is complete @@ -32,23 +31,23 @@ type SentPacketHandler interface { PeekPacketNumber(protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen) PopPacketNumber(protocol.EncryptionLevel) protocol.PacketNumber - GetLossDetectionTimeout() time.Time - OnLossDetectionTimeout(now time.Time) error + GetLossDetectionTimeout() monotime.Time + OnLossDetectionTimeout(now monotime.Time) error - MigratedPath(now time.Time, initialMaxPacketSize protocol.ByteCount) + MigratedPath(now monotime.Time, initialMaxPacketSize protocol.ByteCount) } type sentPacketTracker interface { GetLowestPacketNotConfirmedAcked() protocol.PacketNumber - ReceivedPacket(_ protocol.EncryptionLevel, rcvTime time.Time) + ReceivedPacket(_ protocol.EncryptionLevel, rcvTime monotime.Time) } // ReceivedPacketHandler handles ACKs needed to send for incoming packets type ReceivedPacketHandler interface { IsPotentiallyDuplicate(protocol.PacketNumber, protocol.EncryptionLevel) bool - ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, encLevel protocol.EncryptionLevel, rcvTime time.Time, ackEliciting bool) error + ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, encLevel protocol.EncryptionLevel, rcvTime monotime.Time, ackEliciting bool) error DropPackets(protocol.EncryptionLevel) - GetAlarmTimeout() time.Time - GetAckFrame(_ protocol.EncryptionLevel, now time.Time, onlyIfQueued bool) *wire.AckFrame + GetAlarmTimeout() monotime.Time + GetAckFrame(_ protocol.EncryptionLevel, now monotime.Time, onlyIfQueued bool) *wire.AckFrame } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/lost_packet_tracker.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/lost_packet_tracker.go new file mode 100644 index 000000000..40665734d --- /dev/null +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/lost_packet_tracker.go @@ -0,0 +1,73 @@ +package ackhandler + +import ( + "iter" + "slices" + + "github.com/quic-go/quic-go/internal/monotime" + "github.com/quic-go/quic-go/internal/protocol" +) + +type lostPacket struct { + PacketNumber protocol.PacketNumber + SendTime monotime.Time +} + +type lostPacketTracker struct { + maxLength int + lostPackets []lostPacket +} + +func newLostPacketTracker(maxLength int) *lostPacketTracker { + return &lostPacketTracker{ + maxLength: maxLength, + // Preallocate a small slice only. + // Hopefully we won't lose many packets. + lostPackets: make([]lostPacket, 0, 4), + } +} + +func (t *lostPacketTracker) Add(p protocol.PacketNumber, sendTime monotime.Time) { + if len(t.lostPackets) == t.maxLength { + t.lostPackets = t.lostPackets[1:] + } + t.lostPackets = append(t.lostPackets, lostPacket{ + PacketNumber: p, + SendTime: sendTime, + }) +} + +// Delete deletes a packet from the lost packet tracker. +// This function is not optimized for performance if many packets are lost, +// but it is only used when a spurious loss is detected, which is rare. +func (t *lostPacketTracker) Delete(pn protocol.PacketNumber) { + t.lostPackets = slices.DeleteFunc(t.lostPackets, func(p lostPacket) bool { + return p.PacketNumber == pn + }) +} + +func (t *lostPacketTracker) All() iter.Seq2[protocol.PacketNumber, monotime.Time] { + return func(yield func(protocol.PacketNumber, monotime.Time) bool) { + for _, p := range t.lostPackets { + if !yield(p.PacketNumber, p.SendTime) { + return + } + } + } +} + +func (t *lostPacketTracker) DeleteBefore(ti monotime.Time) { + if len(t.lostPackets) == 0 { + return + } + if !t.lostPackets[0].SendTime.Before(ti) { + return + } + var idx int + for ; idx < len(t.lostPackets); idx++ { + if !t.lostPackets[idx].SendTime.Before(ti) { + break + } + } + t.lostPackets = slices.Delete(t.lostPackets, 0, idx) +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/mockgen.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/mockgen.go index 0031e6b1c..323d9fde9 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/mockgen.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/mockgen.go @@ -2,8 +2,8 @@ package ackhandler -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package ackhandler -destination mock_sent_packet_tracker_test.go github.com/quic-go/quic-go/internal/ackhandler SentPacketTracker" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -package ackhandler -destination mock_sent_packet_tracker_test.go github.com/quic-go/quic-go/internal/ackhandler SentPacketTracker" type SentPacketTracker = sentPacketTracker -//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package ackhandler -destination mock_ecn_handler_test.go github.com/quic-go/quic-go/internal/ackhandler ECNHandler" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -package ackhandler -destination mock_ecn_handler_test.go github.com/quic-go/quic-go/internal/ackhandler ECNHandler" type ECNHandler = ecnHandler diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go index c634939a5..f0500e9f2 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go @@ -2,15 +2,19 @@ package ackhandler import ( "sync" - "time" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" ) +type packetWithPacketNumber struct { + PacketNumber protocol.PacketNumber + *packet +} + // A Packet is a packet type packet struct { - SendTime time.Time - PacketNumber protocol.PacketNumber + SendTime monotime.Time StreamFrames []StreamFrame Frames []Frame LargestAcked protocol.PacketNumber // InvalidPacketNumber if the packet doesn't contain an ACK @@ -21,29 +25,27 @@ type packet struct { includedInBytesInFlight bool declaredLost bool - skippedPacket bool isPathProbePacket bool } func (p *packet) outstanding() bool { - return !p.declaredLost && !p.skippedPacket && !p.IsPathMTUProbePacket && !p.isPathProbePacket + return !p.declaredLost && !p.IsPathMTUProbePacket && !p.isPathProbePacket } var packetPool = sync.Pool{New: func() any { return &packet{} }} func getPacket() *packet { p := packetPool.Get().(*packet) - p.PacketNumber = 0 p.StreamFrames = nil p.Frames = nil p.LargestAcked = 0 p.Length = 0 p.EncryptionLevel = protocol.EncryptionLevel(0) - p.SendTime = time.Time{} + p.SendTime = 0 p.IsPathMTUProbePacket = false p.includedInBytesInFlight = false p.declaredLost = false - p.skippedPacket = false + p.isPathProbePacket = false return p } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go index eda0826c0..7180db85f 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go @@ -2,8 +2,8 @@ package ackhandler import ( "fmt" - "time" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" @@ -35,20 +35,20 @@ func (h *receivedPacketHandler) ReceivedPacket( pn protocol.PacketNumber, ecn protocol.ECN, encLevel protocol.EncryptionLevel, - rcvTime time.Time, + rcvTime monotime.Time, ackEliciting bool, ) error { h.sentPackets.ReceivedPacket(encLevel, rcvTime) switch encLevel { case protocol.EncryptionInitial: - return h.initialPackets.ReceivedPacket(pn, ecn, rcvTime, ackEliciting) + return h.initialPackets.ReceivedPacket(pn, ecn, ackEliciting) case protocol.EncryptionHandshake: // The Handshake packet number space might already have been dropped as a result // of processing the CRYPTO frame that was contained in this packet. if h.handshakePackets == nil { return nil } - return h.handshakePackets.ReceivedPacket(pn, ecn, rcvTime, ackEliciting) + return h.handshakePackets.ReceivedPacket(pn, ecn, ackEliciting) case protocol.Encryption0RTT: if h.lowest1RTTPacket != protocol.InvalidPacketNumber && pn > h.lowest1RTTPacket { return fmt.Errorf("received packet number %d on a 0-RTT packet after receiving %d on a 1-RTT packet", pn, h.lowest1RTTPacket) @@ -83,11 +83,11 @@ func (h *receivedPacketHandler) DropPackets(encLevel protocol.EncryptionLevel) { } } -func (h *receivedPacketHandler) GetAlarmTimeout() time.Time { +func (h *receivedPacketHandler) GetAlarmTimeout() monotime.Time { return h.appDataPackets.GetAlarmTimeout() } -func (h *receivedPacketHandler) GetAckFrame(encLevel protocol.EncryptionLevel, now time.Time, onlyIfQueued bool) *wire.AckFrame { +func (h *receivedPacketHandler) GetAckFrame(encLevel protocol.EncryptionLevel, now monotime.Time, onlyIfQueued bool) *wire.AckFrame { //nolint:exhaustive // 0-RTT packets can't contain ACK frames. switch encLevel { case protocol.EncryptionInitial: diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_history.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_history.go index f9feae1db..d065b6e65 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_history.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_history.go @@ -1,10 +1,10 @@ package ackhandler import ( + "iter" "slices" "github.com/quic-go/quic-go/internal/protocol" - "github.com/quic-go/quic-go/internal/wire" ) // interval is an interval from one PacketNumber to the other @@ -23,7 +23,9 @@ type receivedPacketHistory struct { } func newReceivedPacketHistory() *receivedPacketHistory { - return &receivedPacketHistory{} + return &receivedPacketHistory{ + deletedBelow: protocol.InvalidPacketNumber, + } } // ReceivedPacket registers a packet with PacketNumber p and updates the ranges @@ -107,21 +109,37 @@ func (h *receivedPacketHistory) DeleteBelow(p protocol.PacketNumber) { } } -// AppendAckRanges appends to a slice of all AckRanges that can be used in an AckFrame -func (h *receivedPacketHistory) AppendAckRanges(ackRanges []wire.AckRange) []wire.AckRange { - for i := len(h.ranges) - 1; i >= 0; i-- { - ackRanges = append(ackRanges, wire.AckRange{Smallest: h.ranges[i].Start, Largest: h.ranges[i].End}) +// Backward returns an iterator over the ranges in reverse order +func (h *receivedPacketHistory) Backward() iter.Seq[interval] { + return func(yield func(interval) bool) { + for i := len(h.ranges) - 1; i >= 0; i-- { + if !yield(h.ranges[i]) { + return + } + } } - return ackRanges } -func (h *receivedPacketHistory) GetHighestAckRange() wire.AckRange { - ackRange := wire.AckRange{} - if len(h.ranges) > 0 { - ackRange.Smallest = h.ranges[len(h.ranges)-1].Start - ackRange.Largest = h.ranges[len(h.ranges)-1].End +func (h *receivedPacketHistory) HighestMissingUpTo(p protocol.PacketNumber) protocol.PacketNumber { + if len(h.ranges) == 0 || (h.deletedBelow != protocol.InvalidPacketNumber && p < h.deletedBelow) { + return protocol.InvalidPacketNumber + } + p = min(h.ranges[len(h.ranges)-1].End, p) + for i := len(h.ranges) - 1; i >= 0; i-- { + r := h.ranges[i] + if p >= r.Start && p <= r.End { // p is contained in this range + highest := r.Start - 1 // highest packet in the gap before this range + if h.deletedBelow != protocol.InvalidPacketNumber && highest < h.deletedBelow { + return protocol.InvalidPacketNumber + } + return highest + } + if i >= 1 && p > h.ranges[i-1].End && p <= r.Start { + // p is in the gap between the previous range and this range + return p + } } - return ackRange + return p } func (h *receivedPacketHistory) IsPotentiallyDuplicate(p protocol.PacketNumber) bool { diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go index d1d26f4ab..64092cccf 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go @@ -4,11 +4,14 @@ import ( "fmt" "time" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" ) +const reorderingThreshold = 1 + // The receivedPacketTracker tracks packets for the Initial and Handshake packet number space. // Every received packet is acknowledged immediately. type receivedPacketTracker struct { @@ -24,9 +27,9 @@ func newReceivedPacketTracker() *receivedPacketTracker { return &receivedPacketTracker{packetHistory: *newReceivedPacketHistory()} } -func (h *receivedPacketTracker) ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, rcvTime time.Time, ackEliciting bool) error { +func (h *receivedPacketTracker) ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, ackEliciting bool) error { if isNew := h.packetHistory.ReceivedPacket(pn); !isNew { - return fmt.Errorf("recevedPacketTracker BUG: ReceivedPacket called for old / duplicate packet %d", pn) + return fmt.Errorf("receivedPacketTracker BUG: ReceivedPacket called for old / duplicate packet %d", pn) } //nolint:exhaustive // Only need to count ECT(0), ECT(1) and ECN-CE. @@ -59,7 +62,9 @@ func (h *receivedPacketTracker) GetAckFrame() *wire.AckFrame { ack.ECT0 = h.ect0 ack.ECT1 = h.ect1 ack.ECNCE = h.ecnce - ack.AckRanges = h.packetHistory.AppendAckRanges(ack.AckRanges) + for r := range h.packetHistory.Backward() { + ack.AckRanges = append(ack.AckRanges, wire.AckRange{Smallest: r.Start, Largest: r.End}) + } h.lastAck = ack h.hasNewAck = false @@ -78,7 +83,7 @@ const packetsBeforeAck = 2 type appDataReceivedPacketTracker struct { receivedPacketTracker - largestObservedRcvdTime time.Time + largestObservedRcvdTime monotime.Time largestObserved protocol.PacketNumber ignoreBelow protocol.PacketNumber @@ -87,7 +92,7 @@ type appDataReceivedPacketTracker struct { ackQueued bool // true if we need send a new ACK ackElicitingPacketsReceivedSinceLastAck int - ackAlarm time.Time + ackAlarm monotime.Time logger utils.Logger } @@ -101,8 +106,8 @@ func newAppDataReceivedPacketTracker(logger utils.Logger) *appDataReceivedPacket return h } -func (h *appDataReceivedPacketTracker) ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, rcvTime time.Time, ackEliciting bool) error { - if err := h.receivedPacketTracker.ReceivedPacket(pn, ecn, rcvTime, ackEliciting); err != nil { +func (h *appDataReceivedPacketTracker) ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, rcvTime monotime.Time, ackEliciting bool) error { + if err := h.receivedPacketTracker.ReceivedPacket(pn, ecn, ackEliciting); err != nil { return err } if pn >= h.largestObserved { @@ -116,7 +121,7 @@ func (h *appDataReceivedPacketTracker) ReceivedPacket(pn protocol.PacketNumber, isMissing := h.isMissing(pn) if !h.ackQueued && h.shouldQueueACK(pn, ecn, isMissing) { h.ackQueued = true - h.ackAlarm = time.Time{} // cancel the ack alarm + h.ackAlarm = 0 // cancel the ack alarm } if !h.ackQueued { // No ACK queued, but we'll need to acknowledge the packet after max_ack_delay. @@ -153,17 +158,21 @@ func (h *appDataReceivedPacketTracker) hasNewMissingPackets() bool { if h.lastAck == nil { return false } - highestRange := h.packetHistory.GetHighestAckRange() - return highestRange.Smallest > h.lastAck.LargestAcked()+1 && highestRange.Len() == 1 + if h.largestObserved < reorderingThreshold { + return false + } + highestMissing := h.packetHistory.HighestMissingUpTo(h.largestObserved - reorderingThreshold) + if highestMissing == protocol.InvalidPacketNumber { + return false + } + if highestMissing < h.lastAck.LargestAcked() { + // the packet was already reported missing in the last ACK + return false + } + return highestMissing > h.lastAck.LargestAcked()-reorderingThreshold } func (h *appDataReceivedPacketTracker) shouldQueueACK(pn protocol.PacketNumber, ecn protocol.ECN, wasMissing bool) bool { - // always acknowledge the first packet - if h.lastAck == nil { - h.logger.Debugf("\tQueueing ACK because the first packet should be acknowledged.") - return true - } - // Send an ACK if this packet was reported missing in an ACK sent before. // Ack decimation with reordering relies on the timer to send an ACK, but if // missing packets we reported in the previous ACK, send an ACK immediately. @@ -196,7 +205,7 @@ func (h *appDataReceivedPacketTracker) shouldQueueACK(pn protocol.PacketNumber, return false } -func (h *appDataReceivedPacketTracker) GetAckFrame(now time.Time, onlyIfQueued bool) *wire.AckFrame { +func (h *appDataReceivedPacketTracker) GetAckFrame(now monotime.Time, onlyIfQueued bool) *wire.AckFrame { if onlyIfQueued && !h.ackQueued { if h.ackAlarm.IsZero() || h.ackAlarm.After(now) { return nil @@ -211,9 +220,9 @@ func (h *appDataReceivedPacketTracker) GetAckFrame(now time.Time, onlyIfQueued b } ack.DelayTime = max(0, now.Sub(h.largestObservedRcvdTime)) h.ackQueued = false - h.ackAlarm = time.Time{} + h.ackAlarm = 0 h.ackElicitingPacketsReceivedSinceLastAck = 0 return ack } -func (h *appDataReceivedPacketTracker) GetAlarmTimeout() time.Time { return h.ackAlarm } +func (h *appDataReceivedPacketTracker) GetAlarmTimeout() monotime.Time { return h.ackAlarm } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go index 83d2736ac..e134232c4 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go @@ -6,11 +6,13 @@ import ( "time" "github.com/quic-go/quic-go/internal/congestion" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" - "github.com/quic-go/quic-go/logging" + "github.com/quic-go/quic-go/qlog" + "github.com/quic-go/quic-go/qlogwriter" ) const ( @@ -34,8 +36,8 @@ type packetNumberSpace struct { history sentPacketHistory pns packetNumberGenerator - lossTime time.Time - lastAckElicitingPacketTime time.Time + lossTime monotime.Time + lastAckElicitingPacketTime monotime.Time largestAcked protocol.PacketNumber largestSent protocol.PacketNumber @@ -57,8 +59,8 @@ func newPacketNumberSpace(initialPN protocol.PacketNumber, isAppData bool) *pack } type alarmTimer struct { - Time time.Time - TimerType logging.TimerType + Time monotime.Time + TimerType qlog.TimerType EncryptionLevel protocol.EncryptionLevel } @@ -66,6 +68,7 @@ type sentPacketHandler struct { initialPackets *packetNumberSpace handshakePackets *packetNumberSpace appDataPackets *packetNumberSpace + lostPackets lostPacketTracker // only for application-data packet number space // Do we know that the peer completed address validation yet? // Always true for the server. @@ -84,12 +87,13 @@ type sentPacketHandler struct { // Only applies to the application-data packet number space. lowestNotConfirmedAcked protocol.PacketNumber - ackedPackets []*packet // to avoid allocations in detectAndRemoveAckedPackets + ackedPackets []packetWithPacketNumber // to avoid allocations in detectAndRemoveAckedPackets bytesInFlight protocol.ByteCount congestion congestion.SendAlgorithmWithDebugInfos rttStats *utils.RTTStats + connStats *utils.ConnectionStats // The number of times a PTO has been sent without receiving an ack. ptoCount uint32 @@ -106,8 +110,9 @@ type sentPacketHandler struct { perspective protocol.Perspective - tracer *logging.ConnectionTracer - logger utils.Logger + qlogger qlogwriter.Recorder + lastMetrics qlog.MetricsUpdated + logger utils.Logger } var ( @@ -121,18 +126,20 @@ func newSentPacketHandler( initialPN protocol.PacketNumber, initialMaxDatagramSize protocol.ByteCount, rttStats *utils.RTTStats, + connStats *utils.ConnectionStats, clientAddressValidated bool, enableECN bool, pers protocol.Perspective, - tracer *logging.ConnectionTracer, + qlogger qlogwriter.Recorder, logger utils.Logger, ) *sentPacketHandler { congestion := congestion.NewCubicSender( congestion.DefaultClock{}, rttStats, + connStats, initialMaxDatagramSize, true, // use Reno - tracer, + qlogger, ) h := &sentPacketHandler{ @@ -141,15 +148,17 @@ func newSentPacketHandler( initialPackets: newPacketNumberSpace(initialPN, false), handshakePackets: newPacketNumberSpace(0, false), appDataPackets: newPacketNumberSpace(0, true), + lostPackets: *newLostPacketTracker(64), rttStats: rttStats, + connStats: connStats, congestion: congestion, perspective: pers, - tracer: tracer, + qlogger: qlogger, logger: logger, } if enableECN { h.enableECN = true - h.ecnTracker = newECNTracker(logger, tracer) + h.ecnTracker = newECNTracker(logger, qlogger) } return h } @@ -164,7 +173,7 @@ func (h *sentPacketHandler) removeFromBytesInFlight(p *packet) { } } -func (h *sentPacketHandler) DropPackets(encLevel protocol.EncryptionLevel, now time.Time) { +func (h *sentPacketHandler) DropPackets(encLevel protocol.EncryptionLevel, now monotime.Time) { // The server won't await address validation after the handshake is confirmed. // This applies even if we didn't receive an ACK for a Handshake packet. if h.perspective == protocol.PerspectiveClient && encLevel == protocol.EncryptionHandshake { @@ -177,7 +186,7 @@ func (h *sentPacketHandler) DropPackets(encLevel protocol.EncryptionLevel, now t if pnSpace == nil { return } - for p := range pnSpace.history.Packets() { + for _, p := range pnSpace.history.Packets() { h.removeFromBytesInFlight(p) } } @@ -196,18 +205,18 @@ func (h *sentPacketHandler) DropPackets(encLevel protocol.EncryptionLevel, now t // and not when the client drops 0-RTT keys when the handshake completes. // When 0-RTT is rejected, all application data sent so far becomes invalid. // Delete the packets from the history and remove them from bytes_in_flight. - for p := range h.appDataPackets.history.Packets() { - if p.EncryptionLevel != protocol.Encryption0RTT && !p.skippedPacket { + for pn, p := range h.appDataPackets.history.Packets() { + if p.EncryptionLevel != protocol.Encryption0RTT { break } h.removeFromBytesInFlight(p) - h.appDataPackets.history.Remove(p.PacketNumber) + h.appDataPackets.history.Remove(pn) } default: panic(fmt.Sprintf("Cannot drop keys for encryption level %s", encLevel)) } - if h.tracer != nil && h.tracer.UpdatedPTOCount != nil && h.ptoCount != 0 { - h.tracer.UpdatedPTOCount(0) + if h.qlogger != nil && h.ptoCount != 0 { + h.qlogger.RecordEvent(qlog.PTOCountUpdated{PTOCount: 0}) } h.ptoCount = 0 h.numProbesToSend = 0 @@ -215,7 +224,8 @@ func (h *sentPacketHandler) DropPackets(encLevel protocol.EncryptionLevel, now t h.setLossDetectionTimer(now) } -func (h *sentPacketHandler) ReceivedBytes(n protocol.ByteCount, t time.Time) { +func (h *sentPacketHandler) ReceivedBytes(n protocol.ByteCount, t monotime.Time) { + h.connStats.BytesReceived.Add(uint64(n)) wasAmplificationLimit := h.isAmplificationLimited() h.bytesReceived += n if wasAmplificationLimit && !h.isAmplificationLimited() { @@ -223,7 +233,8 @@ func (h *sentPacketHandler) ReceivedBytes(n protocol.ByteCount, t time.Time) { } } -func (h *sentPacketHandler) ReceivedPacket(l protocol.EncryptionLevel, t time.Time) { +func (h *sentPacketHandler) ReceivedPacket(l protocol.EncryptionLevel, t monotime.Time) { + h.connStats.PacketsReceived.Add(1) if h.perspective == protocol.PerspectiveServer && l == protocol.EncryptionHandshake && !h.peerAddressValidated { h.peerAddressValidated = true h.setLossDetectionTimer(t) @@ -242,7 +253,7 @@ func (h *sentPacketHandler) packetsInFlight() int { } func (h *sentPacketHandler) SentPacket( - t time.Time, + t monotime.Time, pn, largestAcked protocol.PacketNumber, streamFrames []StreamFrame, frames []Frame, @@ -253,6 +264,8 @@ func (h *sentPacketHandler) SentPacket( isPathProbePacket bool, ) { h.bytesSent += size + h.connStats.BytesSent.Add(uint64(size)) + h.connStats.PacketsSent.Add(1) pnSpace := h.getPacketNumberSpace(encLevel) if h.logger.Debug() && (pnSpace.history.HasOutstandingPackets() || pnSpace.history.HasOutstandingPathProbes()) { @@ -267,12 +280,11 @@ func (h *sentPacketHandler) SentPacket( if isPathProbePacket { p := getPacket() p.SendTime = t - p.PacketNumber = pn p.EncryptionLevel = encLevel p.Length = size p.Frames = frames p.isPathProbePacket = true - pnSpace.history.SentPathProbePacket(p) + pnSpace.history.SentPathProbePacket(pn, p) h.setLossDetectionTimer(t) return } @@ -299,7 +311,6 @@ func (h *sentPacketHandler) SentPacket( p := getPacket() p.SendTime = t - p.PacketNumber = pn p.EncryptionLevel = encLevel p.Length = size p.LargestAcked = largestAcked @@ -308,13 +319,58 @@ func (h *sentPacketHandler) SentPacket( p.IsPathMTUProbePacket = isPathMTUProbePacket p.includedInBytesInFlight = true - pnSpace.history.SentAckElicitingPacket(p) - if h.tracer != nil && h.tracer.UpdatedMetrics != nil { - h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight()) + pnSpace.history.SentAckElicitingPacket(pn, p) + if h.qlogger != nil { + h.qlogMetricsUpdated() } h.setLossDetectionTimer(t) } +func (h *sentPacketHandler) qlogMetricsUpdated() { + var metricsUpdatedEvent qlog.MetricsUpdated + var updated bool + if h.rttStats.HasMeasurement() { + if h.lastMetrics.MinRTT != h.rttStats.MinRTT() { + metricsUpdatedEvent.MinRTT = h.rttStats.MinRTT() + h.lastMetrics.MinRTT = metricsUpdatedEvent.MinRTT + updated = true + } + if h.lastMetrics.SmoothedRTT != h.rttStats.SmoothedRTT() { + metricsUpdatedEvent.SmoothedRTT = h.rttStats.SmoothedRTT() + h.lastMetrics.SmoothedRTT = metricsUpdatedEvent.SmoothedRTT + updated = true + } + if h.lastMetrics.LatestRTT != h.rttStats.LatestRTT() { + metricsUpdatedEvent.LatestRTT = h.rttStats.LatestRTT() + h.lastMetrics.LatestRTT = metricsUpdatedEvent.LatestRTT + updated = true + } + if h.lastMetrics.RTTVariance != h.rttStats.MeanDeviation() { + metricsUpdatedEvent.RTTVariance = h.rttStats.MeanDeviation() + h.lastMetrics.RTTVariance = metricsUpdatedEvent.RTTVariance + updated = true + } + } + if cwnd := h.congestion.GetCongestionWindow(); h.lastMetrics.CongestionWindow != int(cwnd) { + metricsUpdatedEvent.CongestionWindow = int(cwnd) + h.lastMetrics.CongestionWindow = metricsUpdatedEvent.CongestionWindow + updated = true + } + if h.lastMetrics.BytesInFlight != int(h.bytesInFlight) { + metricsUpdatedEvent.BytesInFlight = int(h.bytesInFlight) + h.lastMetrics.BytesInFlight = metricsUpdatedEvent.BytesInFlight + updated = true + } + if h.lastMetrics.PacketsInFlight != h.packetsInFlight() { + metricsUpdatedEvent.PacketsInFlight = h.packetsInFlight() + h.lastMetrics.PacketsInFlight = metricsUpdatedEvent.PacketsInFlight + updated = true + } + if updated { + h.qlogger.RecordEvent(metricsUpdatedEvent) + } +} + func (h *sentPacketHandler) getPacketNumberSpace(encLevel protocol.EncryptionLevel) *packetNumberSpace { switch encLevel { case protocol.EncryptionInitial: @@ -328,7 +384,7 @@ func (h *sentPacketHandler) getPacketNumberSpace(encLevel protocol.EncryptionLev } } -func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) (bool /* contained 1-RTT packet */, error) { +func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime monotime.Time) (bool /* contained 1-RTT packet */, error) { pnSpace := h.getPacketNumberSpace(encLevel) largestAcked := ack.LargestAcked() @@ -391,82 +447,144 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En if p.EncryptionLevel == protocol.Encryption1RTT { acked1RTTPacket = true } - h.removeFromBytesInFlight(p) + h.removeFromBytesInFlight(p.packet) if !p.isPathProbePacket { - putPacket(p) + putPacket(p.packet) } } + + // detect spurious losses for application data packets, if the ACK was not reordered + if encLevel == protocol.Encryption1RTT && largestAcked == pnSpace.largestAcked { + h.detectSpuriousLosses( + ack, + rcvTime.Add(-min(ack.DelayTime, h.rttStats.MaxAckDelay())), + ) + // clean up lost packet history + h.lostPackets.DeleteBefore(rcvTime.Add(-3 * h.rttStats.PTO(false))) + } + // After this point, we must not use ackedPackets any longer! // We've already returned the buffers. - ackedPackets = nil //nolint:ineffassign // This is just to be on the safe side. + ackedPackets = nil //nolint:ineffassign // This is just to be on the safe side. + clear(h.ackedPackets) // make sure the memory is released + h.ackedPackets = h.ackedPackets[:0] // Reset the pto_count unless the client is unsure if the server has validated the client's address. if h.peerCompletedAddressValidation { - if h.tracer != nil && h.tracer.UpdatedPTOCount != nil && h.ptoCount != 0 { - h.tracer.UpdatedPTOCount(0) + if h.qlogger != nil && h.ptoCount != 0 { + h.qlogger.RecordEvent(qlog.PTOCountUpdated{PTOCount: 0}) } h.ptoCount = 0 } h.numProbesToSend = 0 - if h.tracer != nil && h.tracer.UpdatedMetrics != nil { - h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight()) + if h.qlogger != nil { + h.qlogMetricsUpdated() } h.setLossDetectionTimer(rcvTime) return acked1RTTPacket, nil } +func (h *sentPacketHandler) detectSpuriousLosses(ack *wire.AckFrame, ackTime monotime.Time) { + var maxPacketReordering protocol.PacketNumber + var maxTimeReordering time.Duration + ackRangeIdx := len(ack.AckRanges) - 1 + var spuriousLosses []protocol.PacketNumber + for pn, sendTime := range h.lostPackets.All() { + ackRange := ack.AckRanges[ackRangeIdx] + for pn > ackRange.Largest { + // this should never happen, since detectSpuriousLosses is only called for ACKs that increase the largest acked + if ackRangeIdx == 0 { + break + } + ackRangeIdx-- + ackRange = ack.AckRanges[ackRangeIdx] + } + if pn < ackRange.Smallest { + continue + } + if pn <= ackRange.Largest { + packetReordering := h.appDataPackets.history.Difference(ack.LargestAcked(), pn) + timeReordering := ackTime.Sub(sendTime) + maxPacketReordering = max(maxPacketReordering, packetReordering) + maxTimeReordering = max(maxTimeReordering, timeReordering) + + if h.qlogger != nil { + h.qlogger.RecordEvent(qlog.SpuriousLoss{ + EncryptionLevel: protocol.Encryption1RTT, + PacketNumber: pn, + PacketReordering: uint64(packetReordering), + TimeReordering: timeReordering, + }) + } + spuriousLosses = append(spuriousLosses, pn) + } + } + for _, pn := range spuriousLosses { + h.lostPackets.Delete(pn) + } +} + func (h *sentPacketHandler) GetLowestPacketNotConfirmedAcked() protocol.PacketNumber { return h.lowestNotConfirmedAcked } // Packets are returned in ascending packet number order. -func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encLevel protocol.EncryptionLevel) ([]*packet, error) { +func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encLevel protocol.EncryptionLevel) ([]packetWithPacketNumber, error) { + if len(h.ackedPackets) > 0 { + return nil, errors.New("ackhandler BUG: ackedPackets slice not empty") + } + pnSpace := h.getPacketNumberSpace(encLevel) - h.ackedPackets = h.ackedPackets[:0] - ackRangeIndex := 0 + + if encLevel == protocol.Encryption1RTT { + for p := range pnSpace.history.SkippedPackets() { + if ack.AcksPacket(p) { + return nil, &qerr.TransportError{ + ErrorCode: qerr.ProtocolViolation, + ErrorMessage: fmt.Sprintf("received an ACK for skipped packet number: %d (%s)", p, encLevel), + } + } + } + } + + var ackRangeIndex int lowestAcked := ack.LowestAcked() largestAcked := ack.LargestAcked() - for p := range pnSpace.history.Packets() { + for pn, p := range pnSpace.history.Packets() { // ignore packets below the lowest acked - if p.PacketNumber < lowestAcked { + if pn < lowestAcked { continue } - if p.PacketNumber > largestAcked { + if pn > largestAcked { break } if ack.HasMissingRanges() { ackRange := ack.AckRanges[len(ack.AckRanges)-1-ackRangeIndex] - for p.PacketNumber > ackRange.Largest && ackRangeIndex < len(ack.AckRanges)-1 { + for pn > ackRange.Largest && ackRangeIndex < len(ack.AckRanges)-1 { ackRangeIndex++ ackRange = ack.AckRanges[len(ack.AckRanges)-1-ackRangeIndex] } - if p.PacketNumber < ackRange.Smallest { // packet not contained in ACK range + if pn < ackRange.Smallest { // packet not contained in ACK range continue } - if p.PacketNumber > ackRange.Largest { - return nil, fmt.Errorf("BUG: ackhandler would have acked wrong packet %d, while evaluating range %d -> %d", p.PacketNumber, ackRange.Smallest, ackRange.Largest) - } - } - if p.skippedPacket { - return nil, &qerr.TransportError{ - ErrorCode: qerr.ProtocolViolation, - ErrorMessage: fmt.Sprintf("received an ACK for skipped packet number: %d (%s)", p.PacketNumber, encLevel), + if pn > ackRange.Largest { + return nil, fmt.Errorf("BUG: ackhandler would have acked wrong packet %d, while evaluating range %d -> %d", pn, ackRange.Smallest, ackRange.Largest) } } if p.isPathProbePacket { - probePacket := pnSpace.history.RemovePathProbe(p.PacketNumber) + probePacket := pnSpace.history.RemovePathProbe(pn) // the probe packet might already have been declared lost if probePacket != nil { - h.ackedPackets = append(h.ackedPackets, probePacket) + h.ackedPackets = append(h.ackedPackets, packetWithPacketNumber{PacketNumber: pn, packet: probePacket}) } continue } - h.ackedPackets = append(h.ackedPackets, p) + h.ackedPackets = append(h.ackedPackets, packetWithPacketNumber{PacketNumber: pn, packet: p}) } if h.logger.Debug() && len(h.ackedPackets) > 0 { pns := make([]protocol.PacketNumber, len(h.ackedPackets)) @@ -494,16 +612,14 @@ func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encL if err := pnSpace.history.Remove(p.PacketNumber); err != nil { return nil, err } - if h.tracer != nil && h.tracer.AcknowledgedPacket != nil { - h.tracer.AcknowledgedPacket(encLevel, p.PacketNumber) - } } + // TODO: add support for the transport:packets_acked qlog event return h.ackedPackets, nil } -func (h *sentPacketHandler) getLossTimeAndSpace() (time.Time, protocol.EncryptionLevel) { +func (h *sentPacketHandler) getLossTimeAndSpace() (monotime.Time, protocol.EncryptionLevel) { var encLevel protocol.EncryptionLevel - var lossTime time.Time + var lossTime monotime.Time if h.initialPackets != nil { lossTime = h.initialPackets.lossTime @@ -529,7 +645,7 @@ func (h *sentPacketHandler) getScaledPTO(includeMaxAckDelay bool) time.Duration } // same logic as getLossTimeAndSpace, but for lastAckElicitingPacketTime instead of lossTime -func (h *sentPacketHandler) getPTOTimeAndSpace(now time.Time) (pto time.Time, encLevel protocol.EncryptionLevel) { +func (h *sentPacketHandler) getPTOTimeAndSpace(now monotime.Time) (pto monotime.Time, encLevel protocol.EncryptionLevel) { // We only send application data probe packets once the handshake is confirmed, // because before that, we don't have the keys to decrypt ACKs sent in 1-RTT packets. if !h.handshakeConfirmed && !h.hasOutstandingCryptoPackets() { @@ -579,24 +695,32 @@ func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool { return false } -func (h *sentPacketHandler) setLossDetectionTimer(now time.Time) { +func (h *sentPacketHandler) setLossDetectionTimer(now monotime.Time) { oldAlarm := h.alarm // only needed in case tracing is enabled newAlarm := h.lossDetectionTime(now) h.alarm = newAlarm - if newAlarm.Time.IsZero() && !oldAlarm.Time.IsZero() { + hasAlarm := !newAlarm.Time.IsZero() + if !hasAlarm && !oldAlarm.Time.IsZero() { h.logger.Debugf("Canceling loss detection timer.") - if h.tracer != nil && h.tracer.LossTimerCanceled != nil { - h.tracer.LossTimerCanceled() + if h.qlogger != nil { + h.qlogger.RecordEvent(qlog.LossTimerUpdated{ + Type: qlog.LossTimerUpdateTypeCancelled, + }) } } - if h.tracer != nil && h.tracer.SetLossTimer != nil && newAlarm != oldAlarm { - h.tracer.SetLossTimer(newAlarm.TimerType, newAlarm.EncryptionLevel, newAlarm.Time) + if h.qlogger != nil && hasAlarm && newAlarm != oldAlarm { + h.qlogger.RecordEvent(qlog.LossTimerUpdated{ + Type: qlog.LossTimerUpdateTypeSet, + TimerType: newAlarm.TimerType, + EncLevel: newAlarm.EncryptionLevel, + Time: newAlarm.Time.ToTime(), + }) } } -func (h *sentPacketHandler) lossDetectionTime(now time.Time) alarmTimer { +func (h *sentPacketHandler) lossDetectionTime(now monotime.Time) alarmTimer { // cancel the alarm if no packets are outstanding if h.peerCompletedAddressValidation && !h.hasOutstandingCryptoPackets() && !h.appDataPackets.history.HasOutstandingPackets() && !h.appDataPackets.history.HasOutstandingPathProbes() { @@ -608,9 +732,9 @@ func (h *sentPacketHandler) lossDetectionTime(now time.Time) alarmTimer { return alarmTimer{} } - var pathProbeLossTime time.Time + var pathProbeLossTime monotime.Time if h.appDataPackets.history.HasOutstandingPathProbes() { - if p := h.appDataPackets.history.FirstOutstandingPathProbe(); p != nil { + if _, p := h.appDataPackets.history.FirstOutstandingPathProbe(); p != nil { pathProbeLossTime = p.SendTime.Add(pathProbePacketLossTimeout) } } @@ -620,7 +744,7 @@ func (h *sentPacketHandler) lossDetectionTime(now time.Time) alarmTimer { if !lossTime.IsZero() && (pathProbeLossTime.IsZero() || lossTime.Before(pathProbeLossTime)) { return alarmTimer{ Time: lossTime, - TimerType: logging.TimerTypeACK, + TimerType: qlog.TimerTypeACK, EncryptionLevel: encLevel, } } @@ -628,30 +752,30 @@ func (h *sentPacketHandler) lossDetectionTime(now time.Time) alarmTimer { if !ptoTime.IsZero() && (pathProbeLossTime.IsZero() || ptoTime.Before(pathProbeLossTime)) { return alarmTimer{ Time: ptoTime, - TimerType: logging.TimerTypePTO, + TimerType: qlog.TimerTypePTO, EncryptionLevel: encLevel, } } if !pathProbeLossTime.IsZero() { return alarmTimer{ Time: pathProbeLossTime, - TimerType: logging.TimerTypePathProbe, - EncryptionLevel: encLevel, + TimerType: qlog.TimerTypePathProbe, + EncryptionLevel: protocol.Encryption1RTT, } } return alarmTimer{} } -func (h *sentPacketHandler) detectLostPathProbes(now time.Time) { +func (h *sentPacketHandler) detectLostPathProbes(now monotime.Time) { if !h.appDataPackets.history.HasOutstandingPathProbes() { return } lossTime := now.Add(-pathProbePacketLossTimeout) // RemovePathProbe cannot be called while iterating. - var lostPathProbes []*packet - for p := range h.appDataPackets.history.PathProbes() { + var lostPathProbes []packetWithPacketNumber + for pn, p := range h.appDataPackets.history.PathProbes() { if !p.SendTime.After(lossTime) { - lostPathProbes = append(lostPathProbes, p) + lostPathProbes = append(lostPathProbes, packetWithPacketNumber{PacketNumber: pn, packet: p}) } } for _, p := range lostPathProbes { @@ -662,9 +786,9 @@ func (h *sentPacketHandler) detectLostPathProbes(now time.Time) { } } -func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.EncryptionLevel) { +func (h *sentPacketHandler) detectLostPackets(now monotime.Time, encLevel protocol.EncryptionLevel) { pnSpace := h.getPacketNumberSpace(encLevel) - pnSpace.lossTime = time.Time{} + pnSpace.lossTime = 0 maxRTT := float64(max(h.rttStats.LatestRTT(), h.rttStats.SmoothedRTT())) lossDelay := time.Duration(timeThreshold * maxRTT) @@ -676,59 +800,73 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E lostSendTime := now.Add(-lossDelay) priorInFlight := h.bytesInFlight - for p := range pnSpace.history.Packets() { - if p.PacketNumber > pnSpace.largestAcked { + for pn, p := range pnSpace.history.Packets() { + if pn > pnSpace.largestAcked { break } - isRegularPacket := !p.skippedPacket && !p.isPathProbePacket var packetLost bool if !p.SendTime.After(lostSendTime) { packetLost = true - if isRegularPacket { + if !p.isPathProbePacket { if h.logger.Debug() { - h.logger.Debugf("\tlost packet %d (time threshold)", p.PacketNumber) + h.logger.Debugf("\tlost packet %d (time threshold)", pn) } - if h.tracer != nil && h.tracer.LostPacket != nil { - h.tracer.LostPacket(p.EncryptionLevel, p.PacketNumber, logging.PacketLossTimeThreshold) + if h.qlogger != nil { + h.qlogger.RecordEvent(qlog.PacketLost{ + Header: qlog.PacketHeader{ + PacketType: qlog.EncryptionLevelToPacketType(p.EncryptionLevel), + PacketNumber: pn, + }, + Trigger: qlog.PacketLossTimeThreshold, + }) } } - } else if pnSpace.largestAcked >= p.PacketNumber+packetThreshold { + } else if pnSpace.history.Difference(pnSpace.largestAcked, pn) >= packetThreshold { packetLost = true - if isRegularPacket { + if !p.isPathProbePacket { if h.logger.Debug() { - h.logger.Debugf("\tlost packet %d (reordering threshold)", p.PacketNumber) + h.logger.Debugf("\tlost packet %d (reordering threshold)", pn) } - if h.tracer != nil && h.tracer.LostPacket != nil { - h.tracer.LostPacket(p.EncryptionLevel, p.PacketNumber, logging.PacketLossReorderingThreshold) + if h.qlogger != nil { + h.qlogger.RecordEvent(qlog.PacketLost{ + Header: qlog.PacketHeader{ + PacketType: qlog.EncryptionLevelToPacketType(p.EncryptionLevel), + PacketNumber: pn, + }, + Trigger: qlog.PacketLossReorderingThreshold, + }) } } } else if pnSpace.lossTime.IsZero() { // Note: This conditional is only entered once per call lossTime := p.SendTime.Add(lossDelay) if h.logger.Debug() { - h.logger.Debugf("\tsetting loss timer for packet %d (%s) to %s (in %s)", p.PacketNumber, encLevel, lossDelay, lossTime) + h.logger.Debugf("\tsetting loss timer for packet %d (%s) to %s (in %s)", pn, encLevel, lossDelay, lossTime) } pnSpace.lossTime = lossTime } if packetLost { - pnSpace.history.DeclareLost(p.PacketNumber) - if isRegularPacket { + if encLevel == protocol.Encryption0RTT || encLevel == protocol.Encryption1RTT { + h.lostPackets.Add(pn, p.SendTime) + } + pnSpace.history.DeclareLost(pn) + if !p.isPathProbePacket { // the bytes in flight need to be reduced no matter if the frames in this packet will be retransmitted h.removeFromBytesInFlight(p) h.queueFramesForRetransmission(p) if !p.IsPathMTUProbePacket { - h.congestion.OnCongestionEvent(p.PacketNumber, p.Length, priorInFlight) + h.congestion.OnCongestionEvent(pn, p.Length, priorInFlight) } if encLevel == protocol.Encryption1RTT && h.ecnTracker != nil { - h.ecnTracker.LostPacket(p.PacketNumber) + h.ecnTracker.LostPacket(pn) } } } } } -func (h *sentPacketHandler) OnLossDetectionTimeout(now time.Time) error { +func (h *sentPacketHandler) OnLossDetectionTimeout(now monotime.Time) error { defer h.setLossDetectionTimer(now) if h.handshakeConfirmed { @@ -740,8 +878,12 @@ func (h *sentPacketHandler) OnLossDetectionTimeout(now time.Time) error { if h.logger.Debug() { h.logger.Debugf("Loss detection alarm fired in loss timer mode. Loss time: %s", earliestLossTime) } - if h.tracer != nil && h.tracer.LossTimerExpired != nil { - h.tracer.LossTimerExpired(logging.TimerTypeACK, encLevel) + if h.qlogger != nil { + h.qlogger.RecordEvent(qlog.LossTimerUpdated{ + Type: qlog.LossTimerUpdateTypeExpired, + TimerType: qlog.TimerTypeACK, + EncLevel: encLevel, + }) } // Early retransmit or time loss detection h.detectLostPackets(now, encLevel) @@ -778,13 +920,13 @@ func (h *sentPacketHandler) OnLossDetectionTimeout(now time.Time) error { if h.logger.Debug() { h.logger.Debugf("Loss detection alarm for %s fired in PTO mode. PTO count: %d", encLevel, h.ptoCount) } - if h.tracer != nil { - if h.tracer.LossTimerExpired != nil { - h.tracer.LossTimerExpired(logging.TimerTypePTO, encLevel) - } - if h.tracer.UpdatedPTOCount != nil { - h.tracer.UpdatedPTOCount(h.ptoCount) - } + if h.qlogger != nil { + h.qlogger.RecordEvent(qlog.LossTimerUpdated{ + Type: qlog.LossTimerUpdateTypeExpired, + TimerType: qlog.TimerTypePTO, + EncLevel: encLevel, + }) + h.qlogger.RecordEvent(qlog.PTOCountUpdated{PTOCount: h.ptoCount}) } h.numProbesToSend += 2 //nolint:exhaustive // We never arm a PTO timer for 0-RTT packets. @@ -804,7 +946,7 @@ func (h *sentPacketHandler) OnLossDetectionTimeout(now time.Time) error { return nil } -func (h *sentPacketHandler) GetLossDetectionTimeout() time.Time { +func (h *sentPacketHandler) GetLossDetectionTimeout() monotime.Time { return h.alarm.Time } @@ -838,7 +980,7 @@ func (h *sentPacketHandler) PopPacketNumber(encLevel protocol.EncryptionLevel) p return pn } -func (h *sentPacketHandler) SendMode(now time.Time) SendMode { +func (h *sentPacketHandler) SendMode(now monotime.Time) SendMode { numTrackedPackets := h.appDataPackets.history.Len() if h.initialPackets != nil { numTrackedPackets += h.initialPackets.history.Len() @@ -883,7 +1025,7 @@ func (h *sentPacketHandler) SendMode(now time.Time) SendMode { return SendAny } -func (h *sentPacketHandler) TimeUntilSend() time.Time { +func (h *sentPacketHandler) TimeUntilSend() monotime.Time { return h.congestion.TimeUntilSend(h.bytesInFlight) } @@ -900,7 +1042,7 @@ func (h *sentPacketHandler) isAmplificationLimited() bool { func (h *sentPacketHandler) QueueProbePacket(encLevel protocol.EncryptionLevel) bool { pnSpace := h.getPacketNumberSpace(encLevel) - p := pnSpace.history.FirstOutstanding() + pn, p := pnSpace.history.FirstOutstanding() if p == nil { return false } @@ -908,7 +1050,7 @@ func (h *sentPacketHandler) QueueProbePacket(encLevel protocol.EncryptionLevel) // TODO: don't declare the packet lost here. // Keep track of acknowledged frames instead. h.removeFromBytesInFlight(p) - pnSpace.history.DeclareLost(p.PacketNumber) + pnSpace.history.DeclareLost(pn) return true } @@ -930,21 +1072,21 @@ func (h *sentPacketHandler) queueFramesForRetransmission(p *packet) { p.Frames = nil } -func (h *sentPacketHandler) ResetForRetry(now time.Time) { +func (h *sentPacketHandler) ResetForRetry(now monotime.Time) { h.bytesInFlight = 0 - var firstPacketSendTime time.Time - for p := range h.initialPackets.history.Packets() { + var firstPacketSendTime monotime.Time + for _, p := range h.initialPackets.history.Packets() { if firstPacketSendTime.IsZero() { firstPacketSendTime = p.SendTime } - if !p.declaredLost && !p.skippedPacket { + if !p.declaredLost { h.queueFramesForRetransmission(p) } } // All application data packets sent at this point are 0-RTT packets. // In the case of a Retry, we can assume that the server dropped all of them. - for p := range h.appDataPackets.history.Packets() { - if !p.declaredLost && !p.skippedPacket { + for _, p := range h.appDataPackets.history.Packets() { + if !p.declaredLost { h.queueFramesForRetransmission(p) } } @@ -957,43 +1099,44 @@ func (h *sentPacketHandler) ResetForRetry(now time.Time) { if h.logger.Debug() { h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation()) } - if h.tracer != nil && h.tracer.UpdatedMetrics != nil { - h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight()) + if h.qlogger != nil { + h.qlogMetricsUpdated() } } h.initialPackets = newPacketNumberSpace(h.initialPackets.pns.Peek(), false) h.appDataPackets = newPacketNumberSpace(h.appDataPackets.pns.Peek(), true) oldAlarm := h.alarm h.alarm = alarmTimer{} - if h.tracer != nil { - if h.tracer.UpdatedPTOCount != nil { - h.tracer.UpdatedPTOCount(0) - } - if !oldAlarm.Time.IsZero() && h.tracer.LossTimerCanceled != nil { - h.tracer.LossTimerCanceled() + if h.qlogger != nil { + h.qlogger.RecordEvent(qlog.PTOCountUpdated{PTOCount: 0}) + if !oldAlarm.Time.IsZero() { + h.qlogger.RecordEvent(qlog.LossTimerUpdated{ + Type: qlog.LossTimerUpdateTypeCancelled, + }) } } h.ptoCount = 0 } -func (h *sentPacketHandler) MigratedPath(now time.Time, initialMaxDatagramSize protocol.ByteCount) { +func (h *sentPacketHandler) MigratedPath(now monotime.Time, initialMaxDatagramSize protocol.ByteCount) { h.rttStats.ResetForPathMigration() - for p := range h.appDataPackets.history.Packets() { - h.appDataPackets.history.DeclareLost(p.PacketNumber) - if !p.skippedPacket && !p.isPathProbePacket { + for pn, p := range h.appDataPackets.history.Packets() { + h.appDataPackets.history.DeclareLost(pn) + if !p.isPathProbePacket { h.removeFromBytesInFlight(p) h.queueFramesForRetransmission(p) } } - for p := range h.appDataPackets.history.PathProbes() { - h.appDataPackets.history.RemovePathProbe(p.PacketNumber) + for pn := range h.appDataPackets.history.PathProbes() { + h.appDataPackets.history.RemovePathProbe(pn) } h.congestion = congestion.NewCubicSender( congestion.DefaultClock{}, h.rttStats, + h.connStats, initialMaxDatagramSize, true, // use Reno - h.tracer, + h.qlogger, ) h.setLossDetectionTimer(now) } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_history.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_history.go index 0aabc6d93..54b74cf74 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_history.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_history.go @@ -3,25 +3,32 @@ package ackhandler import ( "fmt" "iter" + "slices" "github.com/quic-go/quic-go/internal/protocol" ) +const maxSkippedPackets = 4 + type sentPacketHistory struct { packets []*packet - pathProbePackets []*packet + pathProbePackets []packetWithPacketNumber + skippedPackets []protocol.PacketNumber numOutstanding int + firstPacketNumber protocol.PacketNumber highestPacketNumber protocol.PacketNumber } func newSentPacketHistory(isAppData bool) *sentPacketHistory { h := &sentPacketHistory{ highestPacketNumber: protocol.InvalidPacketNumber, + firstPacketNumber: protocol.InvalidPacketNumber, } if isAppData { h.packets = make([]*packet, 0, 32) + h.skippedPackets = make([]protocol.PacketNumber, 0, maxSkippedPackets) } else { h.packets = make([]*packet, 0, 6) } @@ -35,14 +42,20 @@ func (h *sentPacketHistory) checkSequentialPacketNumberUse(pn protocol.PacketNum } } h.highestPacketNumber = pn + if len(h.packets) == 0 { + h.firstPacketNumber = pn + } } func (h *sentPacketHistory) SkippedPacket(pn protocol.PacketNumber) { h.checkSequentialPacketNumberUse(pn) - h.packets = append(h.packets, &packet{ - PacketNumber: pn, - skippedPacket: true, - }) + if len(h.packets) > 0 { + h.packets = append(h.packets, nil) + } + if len(h.skippedPackets) == maxSkippedPackets { + h.skippedPackets = slices.Delete(h.skippedPackets, 0, 1) + } + h.skippedPackets = append(h.skippedPackets, pn) } func (h *sentPacketHistory) SentNonAckElicitingPacket(pn protocol.PacketNumber) { @@ -52,40 +65,40 @@ func (h *sentPacketHistory) SentNonAckElicitingPacket(pn protocol.PacketNumber) } } -func (h *sentPacketHistory) SentAckElicitingPacket(p *packet) { - h.checkSequentialPacketNumberUse(p.PacketNumber) +func (h *sentPacketHistory) SentAckElicitingPacket(pn protocol.PacketNumber, p *packet) { + h.checkSequentialPacketNumberUse(pn) h.packets = append(h.packets, p) if p.outstanding() { h.numOutstanding++ } } -func (h *sentPacketHistory) SentPathProbePacket(p *packet) { - h.checkSequentialPacketNumberUse(p.PacketNumber) - h.packets = append(h.packets, &packet{ - PacketNumber: p.PacketNumber, - isPathProbePacket: true, - }) - h.pathProbePackets = append(h.pathProbePackets, p) +func (h *sentPacketHistory) SentPathProbePacket(pn protocol.PacketNumber, p *packet) { + h.checkSequentialPacketNumberUse(pn) + h.packets = append(h.packets, &packet{isPathProbePacket: true}) + h.pathProbePackets = append(h.pathProbePackets, packetWithPacketNumber{PacketNumber: pn, packet: p}) } -func (h *sentPacketHistory) Packets() iter.Seq[*packet] { - return func(yield func(*packet) bool) { - for _, p := range h.packets { +func (h *sentPacketHistory) Packets() iter.Seq2[protocol.PacketNumber, *packet] { + return func(yield func(protocol.PacketNumber, *packet) bool) { + // h.firstPacketNumber might be updated in the yield function, + // so we need to save it here. + firstPacketNumber := h.firstPacketNumber + for i, p := range h.packets { if p == nil { continue } - if !yield(p) { + if !yield(firstPacketNumber+protocol.PacketNumber(i), p) { return } } } } -func (h *sentPacketHistory) PathProbes() iter.Seq[*packet] { - return func(yield func(*packet) bool) { +func (h *sentPacketHistory) PathProbes() iter.Seq2[protocol.PacketNumber, *packet] { + return func(yield func(protocol.PacketNumber, *packet) bool) { for _, p := range h.pathProbePackets { - if !yield(p) { + if !yield(p.PacketNumber, p.packet) { return } } @@ -93,30 +106,42 @@ func (h *sentPacketHistory) PathProbes() iter.Seq[*packet] { } // FirstOutstanding returns the first outstanding packet. -func (h *sentPacketHistory) FirstOutstanding() *packet { +func (h *sentPacketHistory) FirstOutstanding() (protocol.PacketNumber, *packet) { if !h.HasOutstandingPackets() { - return nil + return protocol.InvalidPacketNumber, nil } - for _, p := range h.packets { + for i, p := range h.packets { if p != nil && p.outstanding() { - return p + return h.firstPacketNumber + protocol.PacketNumber(i), p } } - return nil + return protocol.InvalidPacketNumber, nil } // FirstOutstandingPathProbe returns the first outstanding path probe packet -func (h *sentPacketHistory) FirstOutstandingPathProbe() *packet { +func (h *sentPacketHistory) FirstOutstandingPathProbe() (protocol.PacketNumber, *packet) { if len(h.pathProbePackets) == 0 { - return nil + return protocol.InvalidPacketNumber, nil + } + return h.pathProbePackets[0].PacketNumber, h.pathProbePackets[0].packet +} + +func (h *sentPacketHistory) SkippedPackets() iter.Seq[protocol.PacketNumber] { + return func(yield func(protocol.PacketNumber) bool) { + for _, p := range h.skippedPackets { + if !yield(p) { + return + } + } } - return h.pathProbePackets[0] } func (h *sentPacketHistory) Len() int { return len(h.packets) } +// Remove removes a packet from the sent packet history. +// It must not be used for skipped packet numbers. func (h *sentPacketHistory) Remove(pn protocol.PacketNumber) error { idx, ok := h.getIndex(pn) if !ok { @@ -131,19 +156,19 @@ func (h *sentPacketHistory) Remove(pn protocol.PacketNumber) error { } h.packets[idx] = nil // clean up all skipped packets directly before this packet number + var hasPacketBefore bool for idx > 0 { idx-- - p := h.packets[idx] - if p == nil || !p.skippedPacket { + if h.packets[idx] != nil { + hasPacketBefore = true break } - h.packets[idx] = nil } - if idx == 0 { + if !hasPacketBefore { h.cleanupStart() } if len(h.packets) > 0 && h.packets[0] == nil { - panic("remove failed") + panic("cleanup failed") } return nil } @@ -156,7 +181,7 @@ func (h *sentPacketHistory) RemovePathProbe(pn protocol.PacketNumber) *packet { idx := -1 for i, p := range h.pathProbePackets { if p.PacketNumber == pn { - packetToDelete = p + packetToDelete = p.packet idx = i break } @@ -174,11 +199,10 @@ func (h *sentPacketHistory) getIndex(p protocol.PacketNumber) (int, bool) { if len(h.packets) == 0 { return 0, false } - first := h.packets[0].PacketNumber - if p < first { + if p < h.firstPacketNumber { return 0, false } - index := int(p - first) + index := int(p - h.firstPacketNumber) if index > len(h.packets)-1 { return 0, false } @@ -198,17 +222,19 @@ func (h *sentPacketHistory) cleanupStart() { for i, p := range h.packets { if p != nil { h.packets = h.packets[i:] + h.firstPacketNumber += protocol.PacketNumber(i) return } } h.packets = h.packets[:0] + h.firstPacketNumber = protocol.InvalidPacketNumber } func (h *sentPacketHistory) LowestPacketNumber() protocol.PacketNumber { if len(h.packets) == 0 { return protocol.InvalidPacketNumber } - return h.packets[0].PacketNumber + return h.firstPacketNumber } func (h *sentPacketHistory) DeclareLost(pn protocol.PacketNumber) { @@ -228,3 +254,24 @@ func (h *sentPacketHistory) DeclareLost(pn protocol.PacketNumber) { h.cleanupStart() } } + +// Difference returns the difference between two packet numbers a and b (a - b), +// taking into account any skipped packet numbers between them. +// +// Note that old skipped packets are garbage collected at some point, +// so this function is not guaranteed to return the correct result after a while. +func (h *sentPacketHistory) Difference(a, b protocol.PacketNumber) protocol.PacketNumber { + diff := a - b + if len(h.skippedPackets) == 0 { + return diff + } + if a < h.skippedPackets[0] || b > h.skippedPackets[len(h.skippedPackets)-1] { + return diff + } + for _, p := range h.skippedPackets { + if p > b && p < a { + diff-- + } + } + return diff +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/bandwidth.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/bandwidth.go index 1d03abbb8..3ad827d2b 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/bandwidth.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/bandwidth.go @@ -1,7 +1,6 @@ package congestion import ( - "math" "time" "github.com/quic-go/quic-go/internal/protocol" @@ -10,8 +9,6 @@ import ( // Bandwidth of a connection type Bandwidth uint64 -const infBandwidth Bandwidth = math.MaxUint64 - const ( // BitsPerSecond is 1 bit per second BitsPerSecond Bandwidth = 1 diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/clock.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/clock.go index 405fae70f..8315026f6 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/clock.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/clock.go @@ -1,10 +1,12 @@ package congestion -import "time" +import ( + "github.com/quic-go/quic-go/internal/monotime" +) // A Clock returns the current time type Clock interface { - Now() time.Time + Now() monotime.Time } // DefaultClock implements the Clock interface using the Go stdlib clock. @@ -13,6 +15,6 @@ type DefaultClock struct{} var _ Clock = DefaultClock{} // Now gets the current time -func (DefaultClock) Now() time.Time { - return time.Now() +func (DefaultClock) Now() monotime.Time { + return monotime.Now() } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/cubic.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/cubic.go index b35d40d41..40655de61 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/cubic.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/cubic.go @@ -4,6 +4,7 @@ import ( "math" "time" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" ) @@ -42,7 +43,7 @@ type Cubic struct { numConnections int // Time when this cycle started, after last loss event. - epoch time.Time + epoch monotime.Time // Max congestion window used just before last loss event. // Note: to improve fairness to other streams an additional back off is @@ -77,7 +78,7 @@ func NewCubic(clock Clock) *Cubic { // Reset is called after a timeout to reset the cubic state func (c *Cubic) Reset() { - c.epoch = time.Time{} + c.epoch = 0 c.lastMaxCongestionWindow = 0 c.ackedBytesCount = 0 c.estimatedTCPcongestionWindow = 0 @@ -121,7 +122,7 @@ func (c *Cubic) OnApplicationLimited() { // in such a period. This reset effectively freezes congestion window growth // through application-limited periods and allows Cubic growth to continue // when the entire window is being used. - c.epoch = time.Time{} + c.epoch = 0 } // CongestionWindowAfterPacketLoss computes a new congestion window to use after @@ -135,7 +136,7 @@ func (c *Cubic) CongestionWindowAfterPacketLoss(currentCongestionWindow protocol } else { c.lastMaxCongestionWindow = currentCongestionWindow } - c.epoch = time.Time{} // Reset time. + c.epoch = 0 // Reset time. return protocol.ByteCount(float32(currentCongestionWindow) * c.beta()) } @@ -147,7 +148,7 @@ func (c *Cubic) CongestionWindowAfterAck( ackedBytes protocol.ByteCount, currentCongestionWindow protocol.ByteCount, delayMin time.Duration, - eventTime time.Time, + eventTime monotime.Time, ) protocol.ByteCount { c.ackedBytesCount += ackedBytes diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/cubic_sender.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/cubic_sender.go index 075b08e00..e5457ddb8 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/cubic_sender.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/cubic_sender.go @@ -2,11 +2,12 @@ package congestion import ( "fmt" - "time" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" - "github.com/quic-go/quic-go/logging" + "github.com/quic-go/quic-go/qlog" + "github.com/quic-go/quic-go/qlogwriter" ) const ( @@ -22,6 +23,7 @@ const ( type cubicSender struct { hybridSlowStart HybridSlowStart rttStats *utils.RTTStats + connStats *utils.ConnectionStats cubic *Cubic pacer *pacer clock Clock @@ -55,8 +57,8 @@ type cubicSender struct { maxDatagramSize protocol.ByteCount - lastState logging.CongestionState - tracer *logging.ConnectionTracer + lastState qlog.CongestionState + qlogger qlogwriter.Recorder } var ( @@ -68,32 +70,36 @@ var ( func NewCubicSender( clock Clock, rttStats *utils.RTTStats, + connStats *utils.ConnectionStats, initialMaxDatagramSize protocol.ByteCount, reno bool, - tracer *logging.ConnectionTracer, + qlogger qlogwriter.Recorder, ) *cubicSender { return newCubicSender( clock, rttStats, + connStats, reno, initialMaxDatagramSize, initialCongestionWindow*initialMaxDatagramSize, protocol.MaxCongestionWindowPackets*initialMaxDatagramSize, - tracer, + qlogger, ) } func newCubicSender( clock Clock, rttStats *utils.RTTStats, + connStats *utils.ConnectionStats, reno bool, initialMaxDatagramSize, initialCongestionWindow, initialMaxCongestionWindow protocol.ByteCount, - tracer *logging.ConnectionTracer, + qlogger qlogwriter.Recorder, ) *cubicSender { c := &cubicSender{ rttStats: rttStats, + connStats: connStats, largestSentPacketNumber: protocol.InvalidPacketNumber, largestAckedPacketNumber: protocol.InvalidPacketNumber, largestSentAtLastCutback: protocol.InvalidPacketNumber, @@ -104,23 +110,25 @@ func newCubicSender( cubic: NewCubic(clock), clock: clock, reno: reno, - tracer: tracer, + qlogger: qlogger, maxDatagramSize: initialMaxDatagramSize, } c.pacer = newPacer(c.BandwidthEstimate) - if c.tracer != nil && c.tracer.UpdatedCongestionState != nil { - c.lastState = logging.CongestionStateSlowStart - c.tracer.UpdatedCongestionState(logging.CongestionStateSlowStart) + if c.qlogger != nil { + c.lastState = qlog.CongestionStateSlowStart + c.qlogger.RecordEvent(qlog.CongestionStateUpdated{ + State: qlog.CongestionStateSlowStart, + }) } return c } // TimeUntilSend returns when the next packet should be sent. -func (c *cubicSender) TimeUntilSend(_ protocol.ByteCount) time.Time { +func (c *cubicSender) TimeUntilSend(_ protocol.ByteCount) monotime.Time { return c.pacer.TimeUntilSend() } -func (c *cubicSender) HasPacingBudget(now time.Time) bool { +func (c *cubicSender) HasPacingBudget(now monotime.Time) bool { return c.pacer.Budget(now) >= c.maxDatagramSize } @@ -133,7 +141,7 @@ func (c *cubicSender) minCongestionWindow() protocol.ByteCount { } func (c *cubicSender) OnPacketSent( - sentTime time.Time, + sentTime monotime.Time, _ protocol.ByteCount, packetNumber protocol.PacketNumber, bytes protocol.ByteCount, @@ -168,7 +176,7 @@ func (c *cubicSender) MaybeExitSlowStart() { c.hybridSlowStart.ShouldExitSlowStart(c.rttStats.LatestRTT(), c.rttStats.MinRTT(), c.GetCongestionWindow()/c.maxDatagramSize) { // exit slow start c.slowStartThreshold = c.congestionWindow - c.maybeTraceStateChange(logging.CongestionStateCongestionAvoidance) + c.maybeQlogStateChange(qlog.CongestionStateCongestionAvoidance) } } @@ -176,7 +184,7 @@ func (c *cubicSender) OnPacketAcked( ackedPacketNumber protocol.PacketNumber, ackedBytes protocol.ByteCount, priorInFlight protocol.ByteCount, - eventTime time.Time, + eventTime monotime.Time, ) { c.largestAckedPacketNumber = max(ackedPacketNumber, c.largestAckedPacketNumber) if c.InRecovery() { @@ -189,13 +197,16 @@ func (c *cubicSender) OnPacketAcked( } func (c *cubicSender) OnCongestionEvent(packetNumber protocol.PacketNumber, lostBytes, priorInFlight protocol.ByteCount) { + c.connStats.PacketsLost.Add(1) + c.connStats.BytesLost.Add(uint64(lostBytes)) + // TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets // already sent should be treated as a single loss event, since it's expected. if packetNumber <= c.largestSentAtLastCutback { return } c.lastCutbackExitedSlowstart = c.InSlowStart() - c.maybeTraceStateChange(logging.CongestionStateRecovery) + c.maybeQlogStateChange(qlog.CongestionStateRecovery) if c.reno { c.congestionWindow = protocol.ByteCount(float64(c.congestionWindow) * renoBeta) @@ -218,13 +229,13 @@ func (c *cubicSender) maybeIncreaseCwnd( _ protocol.PacketNumber, ackedBytes protocol.ByteCount, priorInFlight protocol.ByteCount, - eventTime time.Time, + eventTime monotime.Time, ) { // Do not increase the congestion window unless the sender is close to using // the current window. if !c.isCwndLimited(priorInFlight) { c.cubic.OnApplicationLimited() - c.maybeTraceStateChange(logging.CongestionStateApplicationLimited) + c.maybeQlogStateChange(qlog.CongestionStateApplicationLimited) return } if c.congestionWindow >= c.maxCongestionWindow() { @@ -233,11 +244,11 @@ func (c *cubicSender) maybeIncreaseCwnd( if c.InSlowStart() { // TCP slow start, exponential growth, increase by one for each ACK. c.congestionWindow += c.maxDatagramSize - c.maybeTraceStateChange(logging.CongestionStateSlowStart) + c.maybeQlogStateChange(qlog.CongestionStateSlowStart) return } // Congestion avoidance - c.maybeTraceStateChange(logging.CongestionStateCongestionAvoidance) + c.maybeQlogStateChange(qlog.CongestionStateCongestionAvoidance) if c.reno { // Classic Reno congestion avoidance. c.numAckedPackets++ @@ -246,7 +257,10 @@ func (c *cubicSender) maybeIncreaseCwnd( c.numAckedPackets = 0 } } else { - c.congestionWindow = min(c.maxCongestionWindow(), c.cubic.CongestionWindowAfterAck(ackedBytes, c.congestionWindow, c.rttStats.MinRTT(), eventTime)) + c.congestionWindow = min( + c.maxCongestionWindow(), + c.cubic.CongestionWindowAfterAck(ackedBytes, c.congestionWindow, c.rttStats.MinRTT(), eventTime), + ) } } @@ -264,8 +278,8 @@ func (c *cubicSender) isCwndLimited(bytesInFlight protocol.ByteCount) bool { func (c *cubicSender) BandwidthEstimate() Bandwidth { srtt := c.rttStats.SmoothedRTT() if srtt == 0 { - // If we haven't measured an rtt, the bandwidth estimate is unknown. - return infBandwidth + // This should never happen, but if it does, avoid division by zero. + srtt = protocol.TimerGranularity } return BandwidthFromDelta(c.GetCongestionWindow(), srtt) } @@ -295,11 +309,11 @@ func (c *cubicSender) OnConnectionMigration() { c.slowStartThreshold = c.initialMaxCongestionWindow } -func (c *cubicSender) maybeTraceStateChange(new logging.CongestionState) { - if c.tracer == nil || c.tracer.UpdatedCongestionState == nil || new == c.lastState { +func (c *cubicSender) maybeQlogStateChange(new qlog.CongestionState) { + if c.qlogger == nil || new == c.lastState { return } - c.tracer.UpdatedCongestionState(new) + c.qlogger.RecordEvent(qlog.CongestionStateUpdated{State: new}) c.lastState = new } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/interface.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/interface.go index 881f453b6..996907c61 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/interface.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/interface.go @@ -1,19 +1,18 @@ package congestion import ( - "time" - + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" ) // A SendAlgorithm performs congestion control type SendAlgorithm interface { - TimeUntilSend(bytesInFlight protocol.ByteCount) time.Time - HasPacingBudget(now time.Time) bool - OnPacketSent(sentTime time.Time, bytesInFlight protocol.ByteCount, packetNumber protocol.PacketNumber, bytes protocol.ByteCount, isRetransmittable bool) + TimeUntilSend(bytesInFlight protocol.ByteCount) monotime.Time + HasPacingBudget(now monotime.Time) bool + OnPacketSent(sentTime monotime.Time, bytesInFlight protocol.ByteCount, packetNumber protocol.PacketNumber, bytes protocol.ByteCount, isRetransmittable bool) CanSend(bytesInFlight protocol.ByteCount) bool MaybeExitSlowStart() - OnPacketAcked(number protocol.PacketNumber, ackedBytes protocol.ByteCount, priorInFlight protocol.ByteCount, eventTime time.Time) + OnPacketAcked(number protocol.PacketNumber, ackedBytes protocol.ByteCount, priorInFlight protocol.ByteCount, eventTime monotime.Time) OnCongestionEvent(number protocol.PacketNumber, lostBytes protocol.ByteCount, priorInFlight protocol.ByteCount) OnRetransmissionTimeout(packetsRetransmitted bool) SetMaxDatagramSize(protocol.ByteCount) diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/pacer.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/pacer.go index 34d3d1d09..7656f5296 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/pacer.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/congestion/pacer.go @@ -1,8 +1,10 @@ package congestion import ( + "math" "time" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" ) @@ -12,7 +14,7 @@ const maxBurstSizePackets = 10 type pacer struct { budgetAtLastSent protocol.ByteCount maxDatagramSize protocol.ByteCount - lastSentTime time.Time + lastSentTime monotime.Time adjustedBandwidth func() uint64 // in bytes/s } @@ -33,7 +35,7 @@ func newPacer(getBandwidth func() Bandwidth) *pacer { return p } -func (p *pacer) SentPacket(sendTime time.Time, size protocol.ByteCount) { +func (p *pacer) SentPacket(sendTime monotime.Time, size protocol.ByteCount) { budget := p.Budget(sendTime) if size >= budget { p.budgetAtLastSent = 0 @@ -43,12 +45,17 @@ func (p *pacer) SentPacket(sendTime time.Time, size protocol.ByteCount) { p.lastSentTime = sendTime } -func (p *pacer) Budget(now time.Time) protocol.ByteCount { +func (p *pacer) Budget(now monotime.Time) protocol.ByteCount { if p.lastSentTime.IsZero() { return p.maxBurstSize() } - budget := p.budgetAtLastSent + (protocol.ByteCount(p.adjustedBandwidth())*protocol.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9 - if budget < 0 { // protect against overflows + delta := now.Sub(p.lastSentTime) + var added protocol.ByteCount + if delta > 0 { + added = p.timeScaledBandwidth(uint64(delta.Nanoseconds())) + } + budget := p.budgetAtLastSent + added + if added > 0 && budget < p.budgetAtLastSent { budget = protocol.MaxByteCount } return min(p.maxBurstSize(), budget) @@ -56,16 +63,35 @@ func (p *pacer) Budget(now time.Time) protocol.ByteCount { func (p *pacer) maxBurstSize() protocol.ByteCount { return max( - protocol.ByteCount(uint64((protocol.MinPacingDelay+protocol.TimerGranularity).Nanoseconds())*p.adjustedBandwidth())/1e9, + p.timeScaledBandwidth(uint64((protocol.MinPacingDelay + protocol.TimerGranularity).Nanoseconds())), maxBurstSizePackets*p.maxDatagramSize, ) } +// timeScaledBandwidth calculates the number of bytes that may be sent within +// a given time interval (ns nanoseconds), based on the current bandwidth estimate. +// It caps the scaled value to the maximum allowed burst and handles overflows. +func (p *pacer) timeScaledBandwidth(ns uint64) protocol.ByteCount { + bw := p.adjustedBandwidth() + if bw == 0 { + return 0 + } + const nsPerSecond = 1e9 + maxBurst := maxBurstSizePackets * p.maxDatagramSize + var scaled protocol.ByteCount + if ns > math.MaxUint64/bw { + scaled = maxBurst + } else { + scaled = protocol.ByteCount(bw * ns / nsPerSecond) + } + return scaled +} + // TimeUntilSend returns when the next packet should be sent. -// It returns the zero value of time.Time if a packet can be sent immediately. -func (p *pacer) TimeUntilSend() time.Time { +// It returns zero if a packet can be sent immediately. +func (p *pacer) TimeUntilSend() monotime.Time { if p.budgetAtLastSent >= p.maxDatagramSize { - return time.Time{} + return 0 } diff := 1e9 * uint64(p.maxDatagramSize-p.budgetAtLastSent) bw := p.adjustedBandwidth() diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/flowcontrol/base_flow_controller.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/flowcontrol/base_flow_controller.go index 950e5f722..04a9f98ee 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/flowcontrol/base_flow_controller.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/flowcontrol/base_flow_controller.go @@ -4,6 +4,7 @@ import ( "sync" "time" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" ) @@ -25,7 +26,7 @@ type baseFlowController struct { allowWindowIncrease func(size protocol.ByteCount) bool - epochStartTime time.Time + epochStartTime monotime.Time epochStartOffset protocol.ByteCount rttStats *utils.RTTStats @@ -77,7 +78,7 @@ func (c *baseFlowController) hasWindowUpdate() bool { // getWindowUpdate updates the receive window, if necessary // it returns the new offset -func (c *baseFlowController) getWindowUpdate(now time.Time) protocol.ByteCount { +func (c *baseFlowController) getWindowUpdate(now monotime.Time) protocol.ByteCount { if !c.hasWindowUpdate() { return 0 } @@ -89,7 +90,7 @@ func (c *baseFlowController) getWindowUpdate(now time.Time) protocol.ByteCount { // maybeAdjustWindowSize increases the receiveWindowSize if we're sending updates too often. // For details about auto-tuning, see https://docs.google.com/document/d/1SExkMmGiz8VYzV3s9E35JQlJ73vhzCekKkDi85F1qCE/edit?usp=sharing. -func (c *baseFlowController) maybeAdjustWindowSize(now time.Time) { +func (c *baseFlowController) maybeAdjustWindowSize(now monotime.Time) { bytesReadInEpoch := c.bytesRead - c.epochStartOffset // don't do anything if less than half the window has been consumed if bytesReadInEpoch <= c.receiveWindowSize/2 { @@ -111,7 +112,7 @@ func (c *baseFlowController) maybeAdjustWindowSize(now time.Time) { c.startNewAutoTuningEpoch(now) } -func (c *baseFlowController) startNewAutoTuningEpoch(now time.Time) { +func (c *baseFlowController) startNewAutoTuningEpoch(now monotime.Time) { c.epochStartTime = now c.epochStartOffset = c.bytesRead } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/flowcontrol/connection_flow_controller.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/flowcontrol/connection_flow_controller.go index bbeb78892..0c74dcc90 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/flowcontrol/connection_flow_controller.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/flowcontrol/connection_flow_controller.go @@ -3,8 +3,8 @@ package flowcontrol import ( "errors" "fmt" - "time" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" @@ -38,7 +38,7 @@ func NewConnectionFlowController( } // IncrementHighestReceived adds an increment to the highestReceived value -func (c *connectionFlowController) IncrementHighestReceived(increment protocol.ByteCount, now time.Time) error { +func (c *connectionFlowController) IncrementHighestReceived(increment protocol.ByteCount, now monotime.Time) error { c.mutex.Lock() defer c.mutex.Unlock() @@ -61,16 +61,16 @@ func (c *connectionFlowController) AddBytesRead(n protocol.ByteCount) (hasWindow c.mutex.Lock() defer c.mutex.Unlock() - c.baseFlowController.addBytesRead(n) - return c.baseFlowController.hasWindowUpdate() + c.addBytesRead(n) + return c.hasWindowUpdate() } -func (c *connectionFlowController) GetWindowUpdate(now time.Time) protocol.ByteCount { +func (c *connectionFlowController) GetWindowUpdate(now monotime.Time) protocol.ByteCount { c.mutex.Lock() defer c.mutex.Unlock() oldWindowSize := c.receiveWindowSize - offset := c.baseFlowController.getWindowUpdate(now) + offset := c.getWindowUpdate(now) if c.logger.Debug() && oldWindowSize < c.receiveWindowSize { c.logger.Debugf("Increasing receive flow control window for the connection to %d kB", c.receiveWindowSize/(1<<10)) } @@ -79,7 +79,7 @@ func (c *connectionFlowController) GetWindowUpdate(now time.Time) protocol.ByteC // EnsureMinimumWindowSize sets a minimum window size // it should make sure that the connection-level window is increased when a stream-level window grows -func (c *connectionFlowController) EnsureMinimumWindowSize(inc protocol.ByteCount, now time.Time) { +func (c *connectionFlowController) EnsureMinimumWindowSize(inc protocol.ByteCount, now monotime.Time) { c.mutex.Lock() defer c.mutex.Unlock() diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/flowcontrol/interface.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/flowcontrol/interface.go index 23cf30c58..e95c65d99 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/flowcontrol/interface.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/flowcontrol/interface.go @@ -1,8 +1,7 @@ package flowcontrol import ( - "time" - + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" ) @@ -12,7 +11,7 @@ type flowController interface { UpdateSendWindow(protocol.ByteCount) (updated bool) AddBytesSent(protocol.ByteCount) // for receiving - GetWindowUpdate(time.Time) protocol.ByteCount // returns 0 if no update is necessary + GetWindowUpdate(monotime.Time) protocol.ByteCount // returns 0 if no update is necessary } // A StreamFlowController is a flow controller for a QUIC stream. @@ -22,7 +21,7 @@ type StreamFlowController interface { // UpdateHighestReceived is called when a new highest offset is received // final has to be to true if this is the final offset of the stream, // as contained in a STREAM frame with FIN bit, and the RESET_STREAM frame - UpdateHighestReceived(offset protocol.ByteCount, final bool, now time.Time) error + UpdateHighestReceived(offset protocol.ByteCount, final bool, now monotime.Time) error // Abandon is called when reading from the stream is aborted early, // and there won't be any further calls to AddBytesRead. Abandon() @@ -41,7 +40,7 @@ type connectionFlowControllerI interface { ConnectionFlowController // The following two methods are not supposed to be called from outside this packet, but are needed internally // for sending - EnsureMinimumWindowSize(protocol.ByteCount, time.Time) + EnsureMinimumWindowSize(protocol.ByteCount, monotime.Time) // for receiving - IncrementHighestReceived(protocol.ByteCount, time.Time) error + IncrementHighestReceived(protocol.ByteCount, monotime.Time) error } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/flowcontrol/stream_flow_controller.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/flowcontrol/stream_flow_controller.go index ba0051224..ccbfe9cd4 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/flowcontrol/stream_flow_controller.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/flowcontrol/stream_flow_controller.go @@ -2,8 +2,8 @@ package flowcontrol import ( "fmt" - "time" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" @@ -46,7 +46,7 @@ func NewStreamFlowController( } // UpdateHighestReceived updates the highestReceived value, if the offset is higher. -func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount, final bool, now time.Time) error { +func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount, final bool, now monotime.Time) error { // If the final offset for this stream is already known, check for consistency. if c.receivedFinalOffset { // If we receive another final offset, check that it's the same. @@ -100,7 +100,7 @@ func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount, func (c *streamFlowController) AddBytesRead(n protocol.ByteCount) (hasStreamWindowUpdate, hasConnWindowUpdate bool) { c.mutex.Lock() - c.baseFlowController.addBytesRead(n) + c.addBytesRead(n) hasStreamWindowUpdate = c.shouldQueueWindowUpdate() c.mutex.Unlock() hasConnWindowUpdate = c.connection.AddBytesRead(n) @@ -135,7 +135,7 @@ func (c *streamFlowController) shouldQueueWindowUpdate() bool { return !c.receivedFinalOffset && c.hasWindowUpdate() } -func (c *streamFlowController) GetWindowUpdate(now time.Time) protocol.ByteCount { +func (c *streamFlowController) GetWindowUpdate(now monotime.Time) protocol.ByteCount { // If we already received the final offset for this stream, the peer won't need any additional flow control credit. if c.receivedFinalOffset { return 0 @@ -145,7 +145,7 @@ func (c *streamFlowController) GetWindowUpdate(now time.Time) protocol.ByteCount defer c.mutex.Unlock() oldWindowSize := c.receiveWindowSize - offset := c.baseFlowController.getWindowUpdate(now) + offset := c.getWindowUpdate(now) if c.receiveWindowSize > oldWindowSize { // auto-tuning enlarged the window size c.logger.Debugf("Increasing receive flow control window for stream %d to %d", c.streamID, c.receiveWindowSize) c.connection.EnsureMinimumWindowSize(protocol.ByteCount(float64(c.receiveWindowSize)*protocol.ConnectionFlowControlMultiplier), now) diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go index 1a86c6754..534934515 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go @@ -14,7 +14,8 @@ import ( "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" - "github.com/quic-go/quic-go/logging" + "github.com/quic-go/quic-go/qlog" + "github.com/quic-go/quic-go/qlogwriter" "github.com/quic-go/quic-go/quicvarint" ) @@ -22,7 +23,7 @@ type quicVersionContextKey struct{} var QUICVersionContextKey = &quicVersionContextKey{} -const clientSessionStateRevision = 4 +const clientSessionStateRevision = 5 type cryptoSetup struct { tlsConf *tls.Config @@ -40,8 +41,8 @@ type cryptoSetup struct { rttStats *utils.RTTStats - tracer *logging.ConnectionTracer - logger utils.Logger + qlogger qlogwriter.Recorder + logger utils.Logger perspective protocol.Perspective @@ -72,7 +73,7 @@ func NewCryptoSetupClient( tlsConf *tls.Config, enable0RTT bool, rttStats *utils.RTTStats, - tracer *logging.ConnectionTracer, + qlogger qlogwriter.Recorder, logger utils.Logger, version protocol.Version, ) CryptoSetup { @@ -80,7 +81,7 @@ func NewCryptoSetupClient( connID, tp, rttStats, - tracer, + qlogger, logger, protocol.PerspectiveClient, version, @@ -108,7 +109,7 @@ func NewCryptoSetupServer( tlsConf *tls.Config, allow0RTT bool, rttStats *utils.RTTStats, - tracer *logging.ConnectionTracer, + qlogger qlogwriter.Recorder, logger utils.Logger, version protocol.Version, ) CryptoSetup { @@ -116,7 +117,7 @@ func NewCryptoSetupServer( connID, tp, rttStats, - tracer, + qlogger, logger, protocol.PerspectiveServer, version, @@ -137,24 +138,30 @@ func newCryptoSetup( connID protocol.ConnectionID, tp *wire.TransportParameters, rttStats *utils.RTTStats, - tracer *logging.ConnectionTracer, + qlogger qlogwriter.Recorder, logger utils.Logger, perspective protocol.Perspective, version protocol.Version, ) *cryptoSetup { initialSealer, initialOpener := NewInitialAEAD(connID, perspective, version) - if tracer != nil && tracer.UpdatedKeyFromTLS != nil { - tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveClient) - tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer) + if qlogger != nil { + qlogger.RecordEvent(qlog.KeyUpdated{ + Trigger: qlog.KeyUpdateTLS, + KeyType: encLevelToKeyType(protocol.EncryptionInitial, protocol.PerspectiveClient), + }) + qlogger.RecordEvent(qlog.KeyUpdated{ + Trigger: qlog.KeyUpdateTLS, + KeyType: encLevelToKeyType(protocol.EncryptionInitial, protocol.PerspectiveServer), + }) } return &cryptoSetup{ initialSealer: initialSealer, initialOpener: initialOpener, - aead: newUpdatableAEAD(rttStats, tracer, logger, version), + aead: newUpdatableAEAD(rttStats, qlogger, logger, version), events: make([]Event, 0, 16), ourParams: tp, rttStats: rttStats, - tracer: tracer, + qlogger: qlogger, logger: logger, perspective: perspective, version: version, @@ -165,9 +172,15 @@ func (h *cryptoSetup) ChangeConnectionID(id protocol.ConnectionID) { initialSealer, initialOpener := NewInitialAEAD(id, h.perspective, h.version) h.initialSealer = initialSealer h.initialOpener = initialOpener - if h.tracer != nil && h.tracer.UpdatedKeyFromTLS != nil { - h.tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveClient) - h.tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer) + if h.qlogger != nil { + h.qlogger.RecordEvent(qlog.KeyUpdated{ + Trigger: qlog.KeyUpdateTLS, + KeyType: encLevelToKeyType(protocol.EncryptionInitial, protocol.PerspectiveClient), + }) + h.qlogger.RecordEvent(qlog.KeyUpdated{ + Trigger: qlog.KeyUpdateTLS, + KeyType: encLevelToKeyType(protocol.EncryptionInitial, protocol.PerspectiveServer), + }) } } @@ -314,7 +327,6 @@ func (h *cryptoSetup) handleTransportParameters(data []byte) error { func (h *cryptoSetup) marshalDataForSessionState(earlyData bool) []byte { b := make([]byte, 0, 256) b = quicvarint.Append(b, clientSessionStateRevision) - b = quicvarint.Append(b, uint64(h.rttStats.SmoothedRTT().Microseconds())) if earlyData { // only save the transport parameters for 0-RTT enabled session tickets return h.peerParams.MarshalForSessionTicket(b) @@ -323,12 +335,11 @@ func (h *cryptoSetup) marshalDataForSessionState(earlyData bool) []byte { } func (h *cryptoSetup) handleDataFromSessionState(data []byte, earlyData bool) (allowEarlyData bool) { - rtt, tp, err := decodeDataFromSessionState(data, earlyData) + tp, err := decodeDataFromSessionState(data, earlyData) if err != nil { h.logger.Debugf("Restoring of transport parameters from session ticket failed: %s", err.Error()) return } - h.rttStats.SetInitialRTT(rtt) // The session ticket might have been saved from a connection that allowed 0-RTT, // and therefore contain transport parameters. // Only use them if 0-RTT is actually used on the new connection. @@ -339,39 +350,29 @@ func (h *cryptoSetup) handleDataFromSessionState(data []byte, earlyData bool) (a return false } -func decodeDataFromSessionState(b []byte, earlyData bool) (time.Duration, *wire.TransportParameters, error) { +func decodeDataFromSessionState(b []byte, earlyData bool) (*wire.TransportParameters, error) { ver, l, err := quicvarint.Parse(b) if err != nil { - return 0, nil, err + return nil, err } b = b[l:] if ver != clientSessionStateRevision { - return 0, nil, fmt.Errorf("mismatching version. Got %d, expected %d", ver, clientSessionStateRevision) - } - rttEncoded, l, err := quicvarint.Parse(b) - if err != nil { - return 0, nil, err + return nil, fmt.Errorf("mismatching version. Got %d, expected %d", ver, clientSessionStateRevision) } - b = b[l:] - rtt := time.Duration(rttEncoded) * time.Microsecond if !earlyData { - return rtt, nil, nil + return nil, nil } var tp wire.TransportParameters if err := tp.UnmarshalFromSessionTicket(b); err != nil { - return 0, nil, err + return nil, err } - return rtt, &tp, nil + return &tp, nil } func (h *cryptoSetup) getDataForSessionTicket() []byte { - ticket := &sessionTicket{ - RTT: h.rttStats.SmoothedRTT(), - } - if h.allow0RTT { - ticket.Parameters = h.ourParams - } - return ticket.Marshal() + return (&sessionTicket{ + Parameters: h.ourParams, + }).Marshal() } // GetSessionTicket generates a new session ticket. @@ -409,11 +410,10 @@ func (h *cryptoSetup) GetSessionTicket() ([]byte, error) { // A client may use a 0-RTT enabled session to resume a TLS session without using 0-RTT. func (h *cryptoSetup) handleSessionTicket(data []byte, using0RTT bool) (allowEarlyData bool) { var t sessionTicket - if err := t.Unmarshal(data, using0RTT); err != nil { + if err := t.Unmarshal(data); err != nil { h.logger.Debugf("Unmarshalling session ticket failed: %s", err.Error()) return false } - h.rttStats.SetInitialRTT(t.RTT) if !using0RTT { return false } @@ -426,7 +426,6 @@ func (h *cryptoSetup) handleSessionTicket(data []byte, using0RTT bool) (allowEar h.logger.Debugf("0-RTT not allowed. Rejecting 0-RTT.") return false } - h.logger.Debugf("Accepting 0-RTT. Restoring RTT from session ticket: %s", t.RTT) return true } @@ -476,8 +475,11 @@ func (h *cryptoSetup) setReadKey(el tls.QUICEncryptionLevel, suiteID uint16, tra panic("unexpected read encryption level") } h.events = append(h.events, Event{Kind: EventReceivedReadKeys}) - if h.tracer != nil && h.tracer.UpdatedKeyFromTLS != nil { - h.tracer.UpdatedKeyFromTLS(protocol.FromTLSEncryptionLevel(el), h.perspective.Opposite()) + if h.qlogger != nil { + h.qlogger.RecordEvent(qlog.KeyUpdated{ + Trigger: qlog.KeyUpdateTLS, + KeyType: encLevelToKeyType(protocol.FromTLSEncryptionLevel(el), h.perspective.Opposite()), + }) } } @@ -496,8 +498,11 @@ func (h *cryptoSetup) setWriteKey(el tls.QUICEncryptionLevel, suiteID uint16, tr if h.logger.Debug() { h.logger.Debugf("Installed 0-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID)) } - if h.tracer != nil && h.tracer.UpdatedKeyFromTLS != nil { - h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective) + if h.qlogger != nil { + h.qlogger.RecordEvent(qlog.KeyUpdated{ + Trigger: qlog.KeyUpdateTLS, + KeyType: encLevelToKeyType(protocol.Encryption0RTT, h.perspective), + }) } // don't set used0RTT here. 0-RTT might still get rejected. return @@ -520,15 +525,18 @@ func (h *cryptoSetup) setWriteKey(el tls.QUICEncryptionLevel, suiteID uint16, tr h.used0RTT.Store(true) h.zeroRTTSealer = nil h.logger.Debugf("Dropping 0-RTT keys.") - if h.tracer != nil && h.tracer.DroppedEncryptionLevel != nil { - h.tracer.DroppedEncryptionLevel(protocol.Encryption0RTT) + if h.qlogger != nil { + h.qlogger.RecordEvent(qlog.KeyDiscarded{KeyType: qlog.KeyTypeClient0RTT}) } } default: panic("unexpected write encryption level") } - if h.tracer != nil && h.tracer.UpdatedKeyFromTLS != nil { - h.tracer.UpdatedKeyFromTLS(protocol.FromTLSEncryptionLevel(el), h.perspective) + if h.qlogger != nil { + h.qlogger.RecordEvent(qlog.KeyUpdated{ + Trigger: qlog.KeyUpdateTLS, + KeyType: encLevelToKeyType(protocol.FromTLSEncryptionLevel(el), h.perspective), + }) } } @@ -553,6 +561,10 @@ func (h *cryptoSetup) DiscardInitialKeys() { h.initialSealer = nil if dropped { h.logger.Debugf("Dropping Initial keys.") + if h.qlogger != nil { + h.qlogger.RecordEvent(qlog.KeyDiscarded{KeyType: qlog.KeyTypeClientInitial}) + h.qlogger.RecordEvent(qlog.KeyDiscarded{KeyType: qlog.KeyTypeServerInitial}) + } } } @@ -572,6 +584,10 @@ func (h *cryptoSetup) SetHandshakeConfirmed() { } if dropped { h.logger.Debugf("Dropping Handshake keys.") + if h.qlogger != nil { + h.qlogger.RecordEvent(qlog.KeyDiscarded{KeyType: qlog.KeyTypeClientHandshake}) + h.qlogger.RecordEvent(qlog.KeyDiscarded{KeyType: qlog.KeyTypeServerHandshake}) + } } } @@ -639,8 +655,8 @@ func (h *cryptoSetup) Get1RTTOpener() (ShortHeaderOpener, error) { if h.zeroRTTOpener != nil && time.Since(h.handshakeCompleteTime) > 3*h.rttStats.PTO(true) { h.zeroRTTOpener = nil h.logger.Debugf("Dropping 0-RTT keys.") - if h.tracer != nil && h.tracer.DroppedEncryptionLevel != nil { - h.tracer.DroppedEncryptionLevel(protocol.Encryption0RTT) + if h.qlogger != nil { + h.qlogger.RecordEvent(qlog.KeyDiscarded{KeyType: qlog.KeyTypeClient0RTT}) } } @@ -663,3 +679,32 @@ func wrapError(err error) error { } return &qerr.TransportError{ErrorCode: qerr.InternalError, ErrorMessage: err.Error()} } + +func encLevelToKeyType(encLevel protocol.EncryptionLevel, pers protocol.Perspective) qlog.KeyType { + if pers == protocol.PerspectiveServer { + switch encLevel { + case protocol.EncryptionInitial: + return qlog.KeyTypeServerInitial + case protocol.EncryptionHandshake: + return qlog.KeyTypeServerHandshake + case protocol.Encryption0RTT: + return qlog.KeyTypeServer0RTT + case protocol.Encryption1RTT: + return qlog.KeyTypeServer1RTT + default: + return "" + } + } + switch encLevel { + case protocol.EncryptionInitial: + return qlog.KeyTypeClientInitial + case protocol.EncryptionHandshake: + return qlog.KeyTypeClientHandshake + case protocol.Encryption0RTT: + return qlog.KeyTypeClient0RTT + case protocol.Encryption1RTT: + return qlog.KeyTypeClient1RTT + default: + return "" + } +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/interface.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/interface.go index c3a59fcd0..d9227339f 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/interface.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/interface.go @@ -5,8 +5,8 @@ import ( "crypto/tls" "errors" "io" - "time" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" ) @@ -38,7 +38,7 @@ type LongHeaderOpener interface { type ShortHeaderOpener interface { headerDecryptor DecodePacketNumber(wirePN protocol.PacketNumber, wirePNLen protocol.PacketNumberLen) protocol.PacketNumber - Open(dst, src []byte, rcvTime time.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, associatedData []byte) ([]byte, error) + Open(dst, src []byte, rcvTime monotime.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, associatedData []byte) ([]byte, error) } // LongHeaderSealer seals a long header packet diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/session_ticket.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/session_ticket.go index 4da517fc8..875b0d841 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/session_ticket.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/session_ticket.go @@ -4,30 +4,24 @@ import ( "bytes" "errors" "fmt" - "time" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/quicvarint" ) -const sessionTicketRevision = 4 +const sessionTicketRevision = 5 type sessionTicket struct { Parameters *wire.TransportParameters - RTT time.Duration // to be encoded in mus } func (t *sessionTicket) Marshal() []byte { b := make([]byte, 0, 256) b = quicvarint.Append(b, sessionTicketRevision) - b = quicvarint.Append(b, uint64(t.RTT.Microseconds())) - if t.Parameters == nil { - return b - } return t.Parameters.MarshalForSessionTicket(b) } -func (t *sessionTicket) Unmarshal(b []byte, using0RTT bool) error { +func (t *sessionTicket) Unmarshal(b []byte) error { rev, l, err := quicvarint.Parse(b) if err != nil { return errors.New("failed to read session ticket revision") @@ -36,21 +30,11 @@ func (t *sessionTicket) Unmarshal(b []byte, using0RTT bool) error { if rev != sessionTicketRevision { return fmt.Errorf("unknown session ticket revision: %d", rev) } - rtt, l, err := quicvarint.Parse(b) - if err != nil { - return errors.New("failed to read RTT") - } - b = b[l:] - if using0RTT { - var tp wire.TransportParameters - if err := tp.UnmarshalFromSessionTicket(b); err != nil { - return fmt.Errorf("unmarshaling transport parameters from session ticket failed: %s", err.Error()) - } - t.Parameters = &tp - } else if len(b) > 0 { - return fmt.Errorf("the session ticket has more bytes than expected") + var tp wire.TransportParameters + if err := tp.UnmarshalFromSessionTicket(b); err != nil { + return fmt.Errorf("unmarshaling transport parameters from session ticket failed: %s", err.Error()) } - t.RTT = time.Duration(rtt) * time.Microsecond + t.Parameters = &tp return nil } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/token_generator.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/token_generator.go index 84e58cfc7..933670dd7 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/token_generator.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/token_generator.go @@ -20,6 +20,8 @@ type Token struct { IsRetryToken bool SentTime time.Time encodedRemoteAddr []byte + // only set for tokens sent in NEW_TOKEN frames + RTT time.Duration // only set for retry tokens OriginalDestConnectionID protocol.ConnectionID RetrySrcConnectionID protocol.ConnectionID @@ -35,6 +37,7 @@ type token struct { IsRetryToken bool RemoteAddr []byte Timestamp int64 + RTT int64 // in mus OriginalDestConnectionID []byte RetrySrcConnectionID []byte } @@ -69,10 +72,11 @@ func (g *TokenGenerator) NewRetryToken( } // NewToken generates a new token to be sent in a NEW_TOKEN frame -func (g *TokenGenerator) NewToken(raddr net.Addr) ([]byte, error) { +func (g *TokenGenerator) NewToken(raddr net.Addr, rtt time.Duration) ([]byte, error) { data, err := asn1.Marshal(token{ RemoteAddr: encodeRemoteAddr(raddr), Timestamp: time.Now().UnixNano(), + RTT: rtt.Microseconds(), }) if err != nil { return nil, err @@ -107,6 +111,8 @@ func (g *TokenGenerator) DecodeToken(encrypted []byte) (*Token, error) { if t.IsRetryToken { token.OriginalDestConnectionID = protocol.ParseConnectionID(t.OriginalDestConnectionID) token.RetrySrcConnectionID = protocol.ParseConnectionID(t.RetrySrcConnectionID) + } else { + token.RTT = time.Duration(t.RTT) * time.Microsecond } return token, nil } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/updatable_aead.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/updatable_aead.go index ceaa8047a..ae63eec89 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/updatable_aead.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/handshake/updatable_aead.go @@ -6,17 +6,26 @@ import ( "crypto/tls" "encoding/binary" "fmt" - "time" + "sync/atomic" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" - "github.com/quic-go/quic-go/logging" + "github.com/quic-go/quic-go/qlog" + "github.com/quic-go/quic-go/qlogwriter" ) -// KeyUpdateInterval is the maximum number of packets we send or receive before initiating a key update. -// It's a package-level variable to allow modifying it for testing purposes. -var KeyUpdateInterval uint64 = protocol.KeyUpdateInterval +var keyUpdateInterval atomic.Uint64 + +func init() { + keyUpdateInterval.Store(protocol.KeyUpdateInterval) +} + +func SetKeyUpdateInterval(v uint64) (reset func()) { + old := keyUpdateInterval.Swap(v) + return func() { keyUpdateInterval.Store(old) } +} // FirstKeyUpdateInterval is the maximum number of packets we send or receive before initiating the first key update. // It's a package-level variable to allow modifying it for testing purposes. @@ -34,7 +43,7 @@ type updatableAEAD struct { invalidPacketCount uint64 // Time when the keys should be dropped. Keys are dropped on the next call to Open(). - prevRcvAEADExpiry time.Time + prevRcvAEADExpiry monotime.Time prevRcvAEAD cipher.AEAD firstRcvdWithCurrentKey protocol.PacketNumber @@ -57,7 +66,7 @@ type updatableAEAD struct { rttStats *utils.RTTStats - tracer *logging.ConnectionTracer + qlogger qlogwriter.Recorder logger utils.Logger version protocol.Version @@ -70,14 +79,14 @@ var ( _ ShortHeaderSealer = &updatableAEAD{} ) -func newUpdatableAEAD(rttStats *utils.RTTStats, tracer *logging.ConnectionTracer, logger utils.Logger, version protocol.Version) *updatableAEAD { +func newUpdatableAEAD(rttStats *utils.RTTStats, qlogger qlogwriter.Recorder, logger utils.Logger, version protocol.Version) *updatableAEAD { return &updatableAEAD{ firstPacketNumber: protocol.InvalidPacketNumber, largestAcked: protocol.InvalidPacketNumber, firstRcvdWithCurrentKey: protocol.InvalidPacketNumber, firstSentWithCurrentKey: protocol.InvalidPacketNumber, rttStats: rttStats, - tracer: tracer, + qlogger: qlogger, logger: logger, version: version, } @@ -86,10 +95,17 @@ func newUpdatableAEAD(rttStats *utils.RTTStats, tracer *logging.ConnectionTracer func (a *updatableAEAD) rollKeys() { if a.prevRcvAEAD != nil { a.logger.Debugf("Dropping key phase %d ahead of scheduled time. Drop time was: %s", a.keyPhase-1, a.prevRcvAEADExpiry) - if a.tracer != nil && a.tracer.DroppedKey != nil { - a.tracer.DroppedKey(a.keyPhase - 1) + if a.qlogger != nil { + a.qlogger.RecordEvent(qlog.KeyDiscarded{ + KeyType: qlog.KeyTypeClient1RTT, + KeyPhase: a.keyPhase - 1, + }) + a.qlogger.RecordEvent(qlog.KeyDiscarded{ + KeyType: qlog.KeyTypeServer1RTT, + KeyPhase: a.keyPhase - 1, + }) } - a.prevRcvAEADExpiry = time.Time{} + a.prevRcvAEADExpiry = 0 } a.keyPhase++ @@ -107,7 +123,7 @@ func (a *updatableAEAD) rollKeys() { a.nextSendAEAD = createAEAD(a.suite, a.nextSendTrafficSecret, a.version) } -func (a *updatableAEAD) startKeyDropTimer(now time.Time) { +func (a *updatableAEAD) startKeyDropTimer(now monotime.Time) { d := 3 * a.rttStats.PTO(true) a.logger.Debugf("Starting key drop timer to drop key phase %d (in %s)", a.keyPhase-1, d) a.prevRcvAEADExpiry = now.Add(d) @@ -163,7 +179,7 @@ func (a *updatableAEAD) DecodePacketNumber(wirePN protocol.PacketNumber, wirePNL return protocol.DecodePacketNumber(wirePNLen, a.highestRcvdPN, wirePN) } -func (a *updatableAEAD) Open(dst, src []byte, rcvTime time.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, ad []byte) ([]byte, error) { +func (a *updatableAEAD) Open(dst, src []byte, rcvTime monotime.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, ad []byte) ([]byte, error) { dec, err := a.open(dst, src, rcvTime, pn, kp, ad) if err == ErrDecryptionFailed { a.invalidPacketCount++ @@ -177,13 +193,20 @@ func (a *updatableAEAD) Open(dst, src []byte, rcvTime time.Time, pn protocol.Pac return dec, err } -func (a *updatableAEAD) open(dst, src []byte, rcvTime time.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, ad []byte) ([]byte, error) { +func (a *updatableAEAD) open(dst, src []byte, rcvTime monotime.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, ad []byte) ([]byte, error) { if a.prevRcvAEAD != nil && !a.prevRcvAEADExpiry.IsZero() && rcvTime.After(a.prevRcvAEADExpiry) { a.prevRcvAEAD = nil a.logger.Debugf("Dropping key phase %d", a.keyPhase-1) - a.prevRcvAEADExpiry = time.Time{} - if a.tracer != nil && a.tracer.DroppedKey != nil { - a.tracer.DroppedKey(a.keyPhase - 1) + a.prevRcvAEADExpiry = 0 + if a.qlogger != nil { + a.qlogger.RecordEvent(qlog.KeyDiscarded{ + KeyType: qlog.KeyTypeClient1RTT, + KeyPhase: a.keyPhase - 1, + }) + a.qlogger.RecordEvent(qlog.KeyDiscarded{ + KeyType: qlog.KeyTypeServer1RTT, + KeyPhase: a.keyPhase - 1, + }) } } binary.BigEndian.PutUint64(a.nonceBuf[len(a.nonceBuf)-8:], uint64(pn)) @@ -216,8 +239,17 @@ func (a *updatableAEAD) open(dst, src []byte, rcvTime time.Time, pn protocol.Pac // The peer initiated this key update. It's safe to drop the keys for the previous generation now. // Start a timer to drop the previous key generation. a.startKeyDropTimer(rcvTime) - if a.tracer != nil && a.tracer.UpdatedKey != nil { - a.tracer.UpdatedKey(a.keyPhase, true) + if a.qlogger != nil { + a.qlogger.RecordEvent(qlog.KeyUpdated{ + Trigger: qlog.KeyUpdateRemote, + KeyType: qlog.KeyTypeClient1RTT, + KeyPhase: a.keyPhase, + }) + a.qlogger.RecordEvent(qlog.KeyUpdated{ + Trigger: qlog.KeyUpdateRemote, + KeyType: qlog.KeyTypeServer1RTT, + KeyPhase: a.keyPhase, + }) } a.firstRcvdWithCurrentKey = pn return dec, err @@ -293,11 +325,11 @@ func (a *updatableAEAD) shouldInitiateKeyUpdate() bool { return true } } - if a.numRcvdWithCurrentKey >= KeyUpdateInterval { + if a.numRcvdWithCurrentKey >= keyUpdateInterval.Load() { a.logger.Debugf("Received %d packets with current key phase. Initiating key update to the next key phase: %d", a.numRcvdWithCurrentKey, a.keyPhase+1) return true } - if a.numSentWithCurrentKey >= KeyUpdateInterval { + if a.numSentWithCurrentKey >= keyUpdateInterval.Load() { a.logger.Debugf("Sent %d packets with current key phase. Initiating key update to the next key phase: %d", a.numSentWithCurrentKey, a.keyPhase+1) return true } @@ -307,9 +339,17 @@ func (a *updatableAEAD) shouldInitiateKeyUpdate() bool { func (a *updatableAEAD) KeyPhase() protocol.KeyPhaseBit { if a.shouldInitiateKeyUpdate() { a.rollKeys() - a.logger.Debugf("Initiating key update to key phase %d", a.keyPhase) - if a.tracer != nil && a.tracer.UpdatedKey != nil { - a.tracer.UpdatedKey(a.keyPhase, false) + if a.qlogger != nil { + a.qlogger.RecordEvent(qlog.KeyUpdated{ + Trigger: qlog.KeyUpdateLocal, + KeyType: qlog.KeyTypeClient1RTT, + KeyPhase: a.keyPhase, + }) + a.qlogger.RecordEvent(qlog.KeyUpdated{ + Trigger: qlog.KeyUpdateLocal, + KeyType: qlog.KeyTypeServer1RTT, + KeyPhase: a.keyPhase, + }) } } return a.keyPhase.Bit() diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/monotime/time.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/monotime/time.go new file mode 100644 index 000000000..eda61dc69 --- /dev/null +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/monotime/time.go @@ -0,0 +1,90 @@ +// Package monotime provides a monotonic time representation that is useful for +// measuring elapsed time. +// It is designed as a memory optimized drop-in replacement for time.Time, with +// a monotime.Time consuming just 8 bytes instead of 24 bytes. +package monotime + +import ( + "time" +) + +// The absolute value doesn't matter, but it should be in the past, +// so that every timestamp obtained with Now() is non-zero, +// even on systems with low timer resolutions (e.g. Windows). +var start = time.Now().Add(-time.Hour) + +// A Time represents an instant in monotonic time. +// Times can be compared using the comparison operators, but the specific +// value is implementation-dependent and should not be relied upon. +// The zero value of Time doesn't have any specific meaning. +type Time int64 + +// Now returns the current monotonic time. +func Now() Time { + return Time(time.Since(start).Nanoseconds()) +} + +// Sub returns the duration t-t2. If the result exceeds the maximum (or minimum) +// value that can be stored in a Duration, the maximum (or minimum) duration +// will be returned. +// To compute t-d for a duration d, use t.Add(-d). +func (t Time) Sub(t2 Time) time.Duration { + return time.Duration(t - t2) +} + +// Add returns the time t+d. +func (t Time) Add(d time.Duration) Time { + return Time(int64(t) + d.Nanoseconds()) +} + +// After reports whether the time instant t is after t2. +func (t Time) After(t2 Time) bool { + return t > t2 +} + +// Before reports whether the time instant t is before t2. +func (t Time) Before(t2 Time) bool { + return t < t2 +} + +// IsZero reports whether t represents the zero time instant. +func (t Time) IsZero() bool { + return t == 0 +} + +// Equal reports whether t and t2 represent the same time instant. +func (t Time) Equal(t2 Time) bool { + return t == t2 +} + +// ToTime converts the monotonic time to a time.Time value. +// The returned time.Time will have the same instant as the monotonic time, +// but may be subject to clock adjustments. +func (t Time) ToTime() time.Time { + if t.IsZero() { + return time.Time{} + } + return start.Add(time.Duration(t)) +} + +// Since returns the time elapsed since t. It is shorthand for Now().Sub(t). +func Since(t Time) time.Duration { + return Now().Sub(t) +} + +// Until returns the duration until t. +// It is shorthand for t.Sub(Now()). +// If t is in the past, the returned duration will be negative. +func Until(t Time) time.Duration { + return time.Duration(t - Now()) +} + +// FromTime converts a time.Time to a monotonic Time. +// The conversion is relative to the package's start time and may lose +// precision if the time.Time is far from the start time. +func FromTime(t time.Time) Time { + if t.IsZero() { + return 0 + } + return Time(t.Sub(start).Nanoseconds()) +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/connection_id.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/connection_id.go index 77259b5fa..ce3171fc5 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/connection_id.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/connection_id.go @@ -2,8 +2,8 @@ package protocol import ( "crypto/rand" + "encoding/hex" "errors" - "fmt" "io" ) @@ -26,7 +26,7 @@ func (c ArbitraryLenConnectionID) String() string { if c.Len() == 0 { return "(empty)" } - return fmt.Sprintf("%x", c.Bytes()) + return hex.EncodeToString(c.Bytes()) } const maxConnectionIDLen = 20 @@ -100,7 +100,7 @@ func (c ConnectionID) String() string { if c.Len() == 0 { return "(empty)" } - return fmt.Sprintf("%x", c.Bytes()) + return hex.EncodeToString(c.Bytes()) } type DefaultConnectionIDGenerator struct { diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/params.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/params.go index f0aa3ad97..fe86317fb 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/params.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/params.go @@ -102,10 +102,6 @@ const DefaultIdleTimeout = 30 * time.Second // DefaultHandshakeIdleTimeout is the default idle timeout used before handshake completion. const DefaultHandshakeIdleTimeout = 5 * time.Second -// RetiredConnectionIDDeleteTimeout is the time we keep closed connections around in order to retransmit the CONNECTION_CLOSE. -// after this time all information about the old connection will be deleted -const RetiredConnectionIDDeleteTimeout = 5 * time.Second - // MinStreamFrameSize is the minimum size that has to be left in a packet, so that we add another STREAM frame. // This avoids splitting up STREAM frames into small pieces, which has 2 advantages: // 1. it reduces the framing overhead diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go index d056cb9de..a8813a662 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go @@ -2,6 +2,7 @@ package protocol import ( "fmt" + "sync/atomic" "time" ) @@ -95,6 +96,8 @@ func (e ECN) String() string { // A ByteCount in QUIC type ByteCount int64 +type AtomicByteCount atomic.Int64 + // MaxByteCount is the maximum value of a ByteCount const MaxByteCount = ByteCount(1<<62 - 1) @@ -123,6 +126,10 @@ const MinUnknownVersionPacketSize = MinInitialPacketSize // MinStatelessResetSize is the minimum size of a stateless reset packet that we send const MinStatelessResetSize = 1 /* first byte */ + 20 /* max. conn ID length */ + 4 /* max. packet number length */ + 1 /* min. payload length */ + 16 /* token */ +// MinReceivedStatelessResetSize is the minimum size of a received stateless reset, +// as specified in section 10.3 of RFC 9000. +const MinReceivedStatelessResetSize = 5 + 16 + // MinConnectionIDLenInitial is the minimum length of the destination connection ID on an Initial packet. const MinConnectionIDLenInitial = 8 diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/stream.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/stream.go index ad7de864b..6db4a95b9 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/stream.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/stream.go @@ -1,5 +1,7 @@ package protocol +import "github.com/quic-go/quic-go/quicvarint" + // StreamType encodes if this is a unidirectional or bidirectional stream type StreamType uint8 @@ -23,6 +25,30 @@ const ( // MaxStreamCount is the maximum stream count value that can be sent in MAX_STREAMS frames // and as the stream count in the transport parameters MaxStreamCount StreamNum = 1 << 60 + // MaxStreamID is the maximum stream ID + MaxStreamID StreamID = quicvarint.Max +) + +const ( + // FirstOutgoingBidiStreamClient is the first bidirectional stream opened by the client + FirstOutgoingBidiStreamClient StreamID = 0 + // FirstOutgoingUniStreamClient is the first unidirectional stream opened by the client + FirstOutgoingUniStreamClient StreamID = 2 + // FirstOutgoingBidiStreamServer is the first bidirectional stream opened by the server + FirstOutgoingBidiStreamServer StreamID = 1 + // FirstOutgoingUniStreamServer is the first unidirectional stream opened by the server + FirstOutgoingUniStreamServer StreamID = 3 +) + +const ( + // FirstIncomingBidiStreamServer is the first bidirectional stream accepted by the server + FirstIncomingBidiStreamServer = FirstOutgoingBidiStreamClient + // FirstIncomingUniStreamServer is the first unidirectional stream accepted by the server + FirstIncomingUniStreamServer = FirstOutgoingUniStreamClient + // FirstIncomingBidiStreamClient is the first bidirectional stream accepted by the client + FirstIncomingBidiStreamClient = FirstOutgoingBidiStreamServer + // FirstIncomingUniStreamClient is the first unidirectional stream accepted by the client + FirstIncomingUniStreamClient = FirstOutgoingUniStreamServer ) // StreamID calculates the stream ID. diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/version.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/version.go index 025ade9b4..8abca5060 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/version.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/protocol/version.go @@ -1,13 +1,13 @@ package protocol import ( + "crypto/rand" "encoding/binary" "fmt" "math" + mrand "math/rand/v2" + "slices" "sync" - "time" - - "golang.org/x/exp/rand" ) // Version is a version number as int @@ -37,7 +37,6 @@ func IsValidVersion(v Version) bool { } func (vn Version) String() string { - //nolint:exhaustive switch vn { case VersionUnknown: return "unknown" @@ -65,12 +64,7 @@ func (vn Version) toGQUICVersion() int { // IsSupportedVersion returns true if the server supports this version func IsSupportedVersion(supported []Version, v Version) bool { - for _, t := range supported { - if t == v { - return true - } - } - return false + return slices.Contains(supported, v) } // ChooseSupportedVersion finds the best version in the overlap of ours and theirs @@ -79,10 +73,8 @@ func IsSupportedVersion(supported []Version, v Version) bool { // The bool returned indicates if a matching version was found. func ChooseSupportedVersion(ours, theirs []Version) (Version, bool) { for _, ourVer := range ours { - for _, theirVer := range theirs { - if ourVer == theirVer { - return ourVer, true - } + if slices.Contains(theirs, ourVer) { + return ourVer, true } } return 0, false @@ -90,13 +82,22 @@ func ChooseSupportedVersion(ours, theirs []Version) (Version, bool) { var ( versionNegotiationMx sync.Mutex - versionNegotiationRand = rand.New(rand.NewSource(uint64(time.Now().UnixNano()))) + versionNegotiationRand mrand.Rand ) +func init() { + var seed [16]byte + rand.Read(seed[:]) + versionNegotiationRand = *mrand.New(mrand.NewPCG( + binary.BigEndian.Uint64(seed[:8]), + binary.BigEndian.Uint64(seed[8:]), + )) +} + // generateReservedVersion generates a reserved version (v & 0x0f0f0f0f == 0x0a0a0a0a) func generateReservedVersion() Version { var b [4]byte - _, _ = versionNegotiationRand.Read(b[:]) // ignore the error here. Failure to read random data doesn't break anything + binary.BigEndian.PutUint32(b[:], versionNegotiationRand.Uint32()) return Version((binary.BigEndian.Uint32(b[:]) | 0x0a0a0a0a) & 0xfafafafa) } @@ -105,7 +106,7 @@ func generateReservedVersion() Version { func GetGreasedVersions(supported []Version) []Version { versionNegotiationMx.Lock() defer versionNegotiationMx.Unlock() - randPos := rand.Intn(len(supported) + 1) + randPos := versionNegotiationRand.IntN(len(supported) + 1) greased := make([]Version, len(supported)+1) copy(greased, supported[:randPos]) greased[randPos] = generateReservedVersion() diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/buffered_write_closer.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/buffered_write_closer.go index b5b9d6fc7..80ebfecb1 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/buffered_write_closer.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/buffered_write_closer.go @@ -19,7 +19,7 @@ func NewBufferedWriteCloser(writer *bufio.Writer, closer io.Closer) io.WriteClos } func (h bufferedWriteCloser) Close() error { - if err := h.Writer.Flush(); err != nil { + if err := h.Flush(); err != nil { return err } return h.Closer.Close() diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/connstats.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/connstats.go new file mode 100644 index 000000000..19d883126 --- /dev/null +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/connstats.go @@ -0,0 +1,14 @@ +package utils + +import "sync/atomic" + +// ConnectionStats stores stats for the connection. See the public +// ConnectionStats struct in connection.go for more information +type ConnectionStats struct { + BytesSent atomic.Uint64 + PacketsSent atomic.Uint64 + BytesReceived atomic.Uint64 + PacketsReceived atomic.Uint64 + BytesLost atomic.Uint64 + PacketsLost atomic.Uint64 +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/log.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/log.go index 89b52c0d9..558803ef2 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/log.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/log.go @@ -31,9 +31,9 @@ type Logger interface { WithPrefix(prefix string) Logger Debug() bool - Errorf(format string, args ...interface{}) - Infof(format string, args ...interface{}) - Debugf(format string, args ...interface{}) + Errorf(format string, args ...any) + Infof(format string, args ...any) + Debugf(format string, args ...any) } // DefaultLogger is used by quic-go for logging. @@ -61,27 +61,27 @@ func (l *defaultLogger) SetLogTimeFormat(format string) { } // Debugf logs something -func (l *defaultLogger) Debugf(format string, args ...interface{}) { +func (l *defaultLogger) Debugf(format string, args ...any) { if l.logLevel == LogLevelDebug { l.logMessage(format, args...) } } // Infof logs something -func (l *defaultLogger) Infof(format string, args ...interface{}) { +func (l *defaultLogger) Infof(format string, args ...any) { if l.logLevel >= LogLevelInfo { l.logMessage(format, args...) } } // Errorf logs something -func (l *defaultLogger) Errorf(format string, args ...interface{}) { +func (l *defaultLogger) Errorf(format string, args ...any) { if l.logLevel >= LogLevelError { l.logMessage(format, args...) } } -func (l *defaultLogger) logMessage(format string, args ...interface{}) { +func (l *defaultLogger) logMessage(format string, args ...any) { var pre string if len(l.timeFormat) > 0 { diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/rtt_stats.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/rtt_stats.go index 0efd8354a..275313073 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/rtt_stats.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/rtt_stats.go @@ -1,6 +1,7 @@ package utils import ( + "sync/atomic" "time" "github.com/quic-go/quic-go/internal/protocol" @@ -11,44 +12,63 @@ const ( oneMinusAlpha = 1 - rttAlpha rttBeta = 0.25 oneMinusBeta = 1 - rttBeta - // The default RTT used before an RTT sample is taken. - defaultInitialRTT = 100 * time.Millisecond ) +// The default RTT used before an RTT sample is taken +const DefaultInitialRTT = 100 * time.Millisecond + // RTTStats provides round-trip statistics type RTTStats struct { hasMeasurement bool - minRTT time.Duration - latestRTT time.Duration - smoothedRTT time.Duration - meanDeviation time.Duration + minRTT atomic.Int64 // nanoseconds + latestRTT atomic.Int64 // nanoseconds + smoothedRTT atomic.Int64 // nanoseconds + meanDeviation atomic.Int64 // nanoseconds + + maxAckDelay atomic.Int64 // nanoseconds +} - maxAckDelay time.Duration +func NewRTTStats() *RTTStats { + var rttStats RTTStats + rttStats.minRTT.Store(DefaultInitialRTT.Nanoseconds()) + rttStats.latestRTT.Store(DefaultInitialRTT.Nanoseconds()) + rttStats.smoothedRTT.Store(DefaultInitialRTT.Nanoseconds()) + return &rttStats } // MinRTT Returns the minRTT for the entire connection. // May return Zero if no valid updates have occurred. -func (r *RTTStats) MinRTT() time.Duration { return r.minRTT } +func (r *RTTStats) MinRTT() time.Duration { + return time.Duration(r.minRTT.Load()) +} // LatestRTT returns the most recent rtt measurement. // May return Zero if no valid updates have occurred. -func (r *RTTStats) LatestRTT() time.Duration { return r.latestRTT } +func (r *RTTStats) LatestRTT() time.Duration { + return time.Duration(r.latestRTT.Load()) +} // SmoothedRTT returns the smoothed RTT for the connection. // May return Zero if no valid updates have occurred. -func (r *RTTStats) SmoothedRTT() time.Duration { return r.smoothedRTT } +func (r *RTTStats) SmoothedRTT() time.Duration { + return time.Duration(r.smoothedRTT.Load()) +} // MeanDeviation gets the mean deviation -func (r *RTTStats) MeanDeviation() time.Duration { return r.meanDeviation } +func (r *RTTStats) MeanDeviation() time.Duration { + return time.Duration(r.meanDeviation.Load()) +} // MaxAckDelay gets the max_ack_delay advertised by the peer -func (r *RTTStats) MaxAckDelay() time.Duration { return r.maxAckDelay } +func (r *RTTStats) MaxAckDelay() time.Duration { + return time.Duration(r.maxAckDelay.Load()) +} // PTO gets the probe timeout duration. func (r *RTTStats) PTO(includeMaxAckDelay bool) time.Duration { - if r.SmoothedRTT() == 0 { - return 2 * defaultInitialRTT + if !r.hasMeasurement { + return 2 * DefaultInitialRTT } pto := r.SmoothedRTT() + max(4*r.MeanDeviation(), protocol.TimerGranularity) if includeMaxAckDelay { @@ -67,36 +87,45 @@ func (r *RTTStats) UpdateRTT(sendDelta, ackDelay time.Duration) { // ackDelay but the raw observed sendDelta, since poor clock granularity at // the client may cause a high ackDelay to result in underestimation of the // r.minRTT. - if r.minRTT == 0 || r.minRTT > sendDelta { - r.minRTT = sendDelta + minRTT := time.Duration(r.minRTT.Load()) + if !r.hasMeasurement || minRTT > sendDelta { + minRTT = sendDelta + r.minRTT.Store(sendDelta.Nanoseconds()) } // Correct for ackDelay if information received from the peer results in a // an RTT sample at least as large as minRTT. Otherwise, only use the // sendDelta. sample := sendDelta - if sample-r.minRTT >= ackDelay { + if sample-minRTT >= ackDelay { sample -= ackDelay } - r.latestRTT = sample + r.latestRTT.Store(sample.Nanoseconds()) // First time call. if !r.hasMeasurement { r.hasMeasurement = true - r.smoothedRTT = sample - r.meanDeviation = sample / 2 + r.smoothedRTT.Store(sample.Nanoseconds()) + r.meanDeviation.Store(sample.Nanoseconds() / 2) } else { - r.meanDeviation = time.Duration(oneMinusBeta*float32(r.meanDeviation/time.Microsecond)+rttBeta*float32((r.smoothedRTT-sample).Abs()/time.Microsecond)) * time.Microsecond - r.smoothedRTT = time.Duration((float32(r.smoothedRTT/time.Microsecond)*oneMinusAlpha)+(float32(sample/time.Microsecond)*rttAlpha)) * time.Microsecond + smoothedRTT := r.SmoothedRTT() + meanDev := time.Duration(oneMinusBeta*float32(r.MeanDeviation()/time.Microsecond)+rttBeta*float32((smoothedRTT-sample).Abs()/time.Microsecond)) * time.Microsecond + newSmoothedRTT := time.Duration((float32(smoothedRTT/time.Microsecond)*oneMinusAlpha)+(float32(sample/time.Microsecond)*rttAlpha)) * time.Microsecond + r.meanDeviation.Store(meanDev.Nanoseconds()) + r.smoothedRTT.Store(newSmoothedRTT.Nanoseconds()) } } +func (r *RTTStats) HasMeasurement() bool { + return r.hasMeasurement +} + // SetMaxAckDelay sets the max_ack_delay func (r *RTTStats) SetMaxAckDelay(mad time.Duration) { - r.maxAckDelay = mad + r.maxAckDelay.Store(int64(mad)) } // SetInitialRTT sets the initial RTT. -// It is used during the 0-RTT handshake when restoring the RTT stats from the session state. +// It is used during handshake when restoring the RTT stats from the token. func (r *RTTStats) SetInitialRTT(t time.Duration) { // On the server side, by the time we get to process the session ticket, // we might already have obtained an RTT measurement. @@ -105,15 +134,26 @@ func (r *RTTStats) SetInitialRTT(t time.Duration) { if r.hasMeasurement { return } - r.smoothedRTT = t - r.latestRTT = t + r.smoothedRTT.Store(int64(t)) + r.latestRTT.Store(int64(t)) } func (r *RTTStats) ResetForPathMigration() { r.hasMeasurement = false - r.minRTT = 0 - r.latestRTT = 0 - r.smoothedRTT = 0 - r.meanDeviation = 0 + r.minRTT.Store(DefaultInitialRTT.Nanoseconds()) + r.latestRTT.Store(DefaultInitialRTT.Nanoseconds()) + r.smoothedRTT.Store(DefaultInitialRTT.Nanoseconds()) + r.meanDeviation.Store(0) // max_ack_delay remains valid } + +func (r *RTTStats) Clone() *RTTStats { + out := &RTTStats{} + out.hasMeasurement = r.hasMeasurement + out.minRTT.Store(r.minRTT.Load()) + out.latestRTT.Store(r.latestRTT.Load()) + out.smoothedRTT.Store(r.smoothedRTT.Load()) + out.meanDeviation.Store(r.meanDeviation.Load()) + out.maxAckDelay.Store(r.maxAckDelay.Load()) + return out +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/timer.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/timer.go deleted file mode 100644 index 361106c8a..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/utils/timer.go +++ /dev/null @@ -1,57 +0,0 @@ -package utils - -import ( - "math" - "time" -) - -// A Timer wrapper that behaves correctly when resetting -type Timer struct { - t *time.Timer - read bool - deadline time.Time -} - -// NewTimer creates a new timer that is not set -func NewTimer() *Timer { - return &Timer{t: time.NewTimer(time.Duration(math.MaxInt64))} -} - -// Chan returns the channel of the wrapped timer -func (t *Timer) Chan() <-chan time.Time { - return t.t.C -} - -// Reset the timer, no matter whether the value was read or not -func (t *Timer) Reset(deadline time.Time) { - if deadline.Equal(t.deadline) && !t.read { - // No need to reset the timer - return - } - - // We need to drain the timer if the value from its channel was not read yet. - // See https://groups.google.com/forum/#!topic/golang-dev/c9UUfASVPoU - if !t.t.Stop() && !t.read { - <-t.t.C - } - if !deadline.IsZero() { - t.t.Reset(time.Until(deadline)) - } - - t.read = false - t.deadline = deadline -} - -// SetRead should be called after the value from the chan was read -func (t *Timer) SetRead() { - t.read = true -} - -func (t *Timer) Deadline() time.Time { - return t.deadline -} - -// Stop stops the timer -func (t *Timer) Stop() { - t.t.Stop() -} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame.go index 8befef4f2..68bebfa79 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame.go @@ -21,9 +21,9 @@ type AckFrame struct { } // parseAckFrame reads an ACK frame -func parseAckFrame(frame *AckFrame, b []byte, typ uint64, ackDelayExponent uint8, _ protocol.Version) (int, error) { +func parseAckFrame(frame *AckFrame, b []byte, typ FrameType, ackDelayExponent uint8, _ protocol.Version) (int, error) { startLen := len(b) - ecn := typ == ackECNFrameType + ecn := typ == FrameTypeAckECN la, l, err := quicvarint.Parse(b) if err != nil { @@ -122,9 +122,9 @@ func parseAckFrame(frame *AckFrame, b []byte, typ uint64, ackDelayExponent uint8 func (f *AckFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { hasECN := f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0 if hasECN { - b = append(b, ackECNFrameType) + b = append(b, byte(FrameTypeAckECN)) } else { - b = append(b, ackFrameType) + b = append(b, byte(FrameTypeAck)) } b = quicvarint.Append(b, uint64(f.LargestAcked())) b = quicvarint.Append(b, encodeAckDelay(f.DelayTime)) diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/ack_frequency_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/ack_frequency_frame.go new file mode 100644 index 000000000..0735d70ba --- /dev/null +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/ack_frequency_frame.go @@ -0,0 +1,65 @@ +package wire + +import ( + "math" + "time" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/quicvarint" +) + +type AckFrequencyFrame struct { + SequenceNumber uint64 + AckElicitingThreshold uint64 + RequestMaxAckDelay time.Duration + ReorderingThreshold protocol.PacketNumber +} + +func parseAckFrequencyFrame(b []byte, _ protocol.Version) (*AckFrequencyFrame, int, error) { + startLen := len(b) + seq, l, err := quicvarint.Parse(b) + if err != nil { + return nil, 0, replaceUnexpectedEOF(err) + } + b = b[l:] + aeth, l, err := quicvarint.Parse(b) + if err != nil { + return nil, 0, replaceUnexpectedEOF(err) + } + b = b[l:] + mad, l, err := quicvarint.Parse(b) + if err != nil { + return nil, 0, replaceUnexpectedEOF(err) + } + // prevents overflows if the peer sends a very large value + maxAckDelay := time.Duration(mad) * time.Microsecond + if maxAckDelay < 0 { + maxAckDelay = math.MaxInt64 + } + b = b[l:] + rth, l, err := quicvarint.Parse(b) + if err != nil { + return nil, 0, replaceUnexpectedEOF(err) + } + b = b[l:] + + return &AckFrequencyFrame{ + SequenceNumber: seq, + AckElicitingThreshold: aeth, + RequestMaxAckDelay: maxAckDelay, + ReorderingThreshold: protocol.PacketNumber(rth), + }, startLen - len(b), nil +} + +func (f *AckFrequencyFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + b = quicvarint.Append(b, uint64(FrameTypeAckFrequency)) + b = quicvarint.Append(b, f.SequenceNumber) + b = quicvarint.Append(b, f.AckElicitingThreshold) + b = quicvarint.Append(b, uint64(f.RequestMaxAckDelay/time.Microsecond)) + return quicvarint.Append(b, uint64(f.ReorderingThreshold)), nil +} + +func (f *AckFrequencyFrame) Length(_ protocol.Version) protocol.ByteCount { + return protocol.ByteCount(2 + quicvarint.Len(f.SequenceNumber) + quicvarint.Len(f.AckElicitingThreshold) + + quicvarint.Len(uint64(f.RequestMaxAckDelay/time.Microsecond)) + quicvarint.Len(uint64(f.ReorderingThreshold))) +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/connection_close_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/connection_close_frame.go index be11a1b2e..6c71aab6b 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/connection_close_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/connection_close_frame.go @@ -15,9 +15,9 @@ type ConnectionCloseFrame struct { ReasonPhrase string } -func parseConnectionCloseFrame(b []byte, typ uint64, _ protocol.Version) (*ConnectionCloseFrame, int, error) { +func parseConnectionCloseFrame(b []byte, typ FrameType, _ protocol.Version) (*ConnectionCloseFrame, int, error) { startLen := len(b) - f := &ConnectionCloseFrame{IsApplicationError: typ == applicationCloseFrameType} + f := &ConnectionCloseFrame{IsApplicationError: typ == FrameTypeApplicationClose} ec, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) @@ -60,9 +60,9 @@ func (f *ConnectionCloseFrame) Length(protocol.Version) protocol.ByteCount { func (f *ConnectionCloseFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { if f.IsApplicationError { - b = append(b, applicationCloseFrameType) + b = append(b, byte(FrameTypeApplicationClose)) } else { - b = append(b, connectionCloseFrameType) + b = append(b, byte(FrameTypeConnectionClose)) } b = quicvarint.Append(b, f.ErrorCode) diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/crypto_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/crypto_frame.go index 0aa7fe7bc..60a713f74 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/crypto_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/crypto_frame.go @@ -38,7 +38,7 @@ func parseCryptoFrame(b []byte, _ protocol.Version) (*CryptoFrame, int, error) { } func (f *CryptoFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { - b = append(b, cryptoFrameType) + b = append(b, byte(FrameTypeCrypto)) b = quicvarint.Append(b, uint64(f.Offset)) b = quicvarint.Append(b, uint64(len(f.Data))) b = append(b, f.Data...) diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/data_blocked_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/data_blocked_frame.go index c97d4c629..11c72ea32 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/data_blocked_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/data_blocked_frame.go @@ -19,7 +19,7 @@ func parseDataBlockedFrame(b []byte, _ protocol.Version) (*DataBlockedFrame, int } func (f *DataBlockedFrame) Append(b []byte, version protocol.Version) ([]byte, error) { - b = append(b, dataBlockedFrameType) + b = append(b, byte(FrameTypeDataBlocked)) return quicvarint.Append(b, uint64(f.MaximumData)), nil } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/datagram_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/datagram_frame.go index 071fda9a0..a6034867e 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/datagram_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/datagram_frame.go @@ -19,10 +19,10 @@ type DatagramFrame struct { Data []byte } -func parseDatagramFrame(b []byte, typ uint64, _ protocol.Version) (*DatagramFrame, int, error) { +func parseDatagramFrame(b []byte, typ FrameType, _ protocol.Version) (*DatagramFrame, int, error) { startLen := len(b) f := &DatagramFrame{} - f.DataLenPresent = typ&0x1 > 0 + f.DataLenPresent = uint64(typ)&0x1 > 0 var length uint64 if f.DataLenPresent { diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/extended_header.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/extended_header.go index 1c6ad991d..6ab9fb98a 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/extended_header.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/extended_header.go @@ -61,7 +61,6 @@ func (h *ExtendedHeader) Append(b []byte, v protocol.Version) ([]byte, error) { var packetType uint8 if v == protocol.Version2 { - //nolint:exhaustive switch h.Type { case protocol.PacketTypeInitial: packetType = 0b01 @@ -73,7 +72,6 @@ func (h *ExtendedHeader) Append(b []byte, v protocol.Version) ([]byte, error) { packetType = 0b00 } } else { - //nolint:exhaustive switch h.Type { case protocol.PacketTypeInitial: packetType = 0b00 diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/frame.go index 10d4eebc3..09ea92f75 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/frame.go @@ -19,3 +19,15 @@ func IsProbingFrame(f Frame) bool { } return false } + +// IsProbingFrameType returns true if the FrameType is a probing frame. +// See section 9.1 of RFC 9000. +func IsProbingFrameType(f FrameType) bool { + //nolint:exhaustive // PATH_CHALLENGE, PATH_RESPONSE and NEW_CONNECTION_ID are the only probing frames + switch f { + case FrameTypePathChallenge, FrameTypePathResponse, FrameTypeNewConnectionID: + return true + default: + return false + } +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/frame_parser.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/frame_parser.go index 59d41444a..afc3bedb9 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/frame_parser.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/frame_parser.go @@ -4,42 +4,20 @@ import ( "errors" "fmt" "io" - "reflect" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/quicvarint" ) -const ( - pingFrameType = 0x1 - ackFrameType = 0x2 - ackECNFrameType = 0x3 - resetStreamFrameType = 0x4 - stopSendingFrameType = 0x5 - cryptoFrameType = 0x6 - newTokenFrameType = 0x7 - maxDataFrameType = 0x10 - maxStreamDataFrameType = 0x11 - bidiMaxStreamsFrameType = 0x12 - uniMaxStreamsFrameType = 0x13 - dataBlockedFrameType = 0x14 - streamDataBlockedFrameType = 0x15 - bidiStreamBlockedFrameType = 0x16 - uniStreamBlockedFrameType = 0x17 - newConnectionIDFrameType = 0x18 - retireConnectionIDFrameType = 0x19 - pathChallengeFrameType = 0x1a - pathResponseFrameType = 0x1b - connectionCloseFrameType = 0x1c - applicationCloseFrameType = 0x1d - handshakeDoneFrameType = 0x1e -) +var errUnknownFrameType = errors.New("unknown frame type") // The FrameParser parses QUIC frames, one by one. type FrameParser struct { - ackDelayExponent uint8 - supportsDatagrams bool + ackDelayExponent uint8 + supportsDatagrams bool + supportsResetStreamAt bool + supportsAckFrequency bool // To avoid allocating when parsing, keep a single ACK frame struct. // It is used over and over again. @@ -47,27 +25,24 @@ type FrameParser struct { } // NewFrameParser creates a new frame parser. -func NewFrameParser(supportsDatagrams bool) *FrameParser { +func NewFrameParser(supportsDatagrams, supportsResetStreamAt, supportsAckFrequency bool) *FrameParser { return &FrameParser{ - supportsDatagrams: supportsDatagrams, - ackFrame: &AckFrame{}, + supportsDatagrams: supportsDatagrams, + supportsResetStreamAt: supportsResetStreamAt, + supportsAckFrequency: supportsAckFrequency, + ackFrame: &AckFrame{}, } } -// ParseNext parses the next frame. -// It skips PADDING frames. -func (p *FrameParser) ParseNext(data []byte, encLevel protocol.EncryptionLevel, v protocol.Version) (int, Frame, error) { - frame, l, err := p.parseNext(data, encLevel, v) - return l, frame, err -} - -func (p *FrameParser) parseNext(b []byte, encLevel protocol.EncryptionLevel, v protocol.Version) (Frame, int, error) { +// ParseType parses the frame type of the next frame. +// It skips over PADDING frames. +func (p *FrameParser) ParseType(b []byte, encLevel protocol.EncryptionLevel) (FrameType, int, error) { var parsed int for len(b) != 0 { typ, l, err := quicvarint.Parse(b) parsed += l if err != nil { - return nil, parsed, &qerr.TransportError{ + return 0, parsed, &qerr.TransportError{ ErrorCode: qerr.FrameEncodingError, ErrorMessage: err.Error(), } @@ -76,111 +51,131 @@ func (p *FrameParser) parseNext(b []byte, encLevel protocol.EncryptionLevel, v p if typ == 0x0 { // skip PADDING frames continue } - - f, l, err := p.parseFrame(b, typ, encLevel, v) - parsed += l - if err != nil { - return nil, parsed, &qerr.TransportError{ + ft := FrameType(typ) + valid := ft.isValidRFC9000() || + (p.supportsDatagrams && ft.IsDatagramFrameType()) || + (p.supportsResetStreamAt && ft == FrameTypeResetStreamAt) || + (p.supportsAckFrequency && (ft == FrameTypeAckFrequency || ft == FrameTypeImmediateAck)) + if !valid { + return 0, parsed, &qerr.TransportError{ + ErrorCode: qerr.FrameEncodingError, FrameType: typ, + ErrorMessage: errUnknownFrameType.Error(), + } + } + if !ft.isAllowedAtEncLevel(encLevel) { + return 0, parsed, &qerr.TransportError{ ErrorCode: qerr.FrameEncodingError, - ErrorMessage: err.Error(), + FrameType: typ, + ErrorMessage: fmt.Sprintf("%d not allowed at encryption level %s", ft, encLevel), } } - return f, parsed, nil + return ft, parsed, nil } - return nil, parsed, nil + return 0, parsed, io.EOF } -func (p *FrameParser) parseFrame(b []byte, typ uint64, encLevel protocol.EncryptionLevel, v protocol.Version) (Frame, int, error) { - var frame Frame - var err error - var l int - if typ&0xf8 == 0x8 { - frame, l, err = parseStreamFrame(b, typ, v) - } else { - switch typ { - case pingFrameType: - frame = &PingFrame{} - case ackFrameType, ackECNFrameType: - ackDelayExponent := p.ackDelayExponent - if encLevel != protocol.Encryption1RTT { - ackDelayExponent = protocol.DefaultAckDelayExponent - } - p.ackFrame.Reset() - l, err = parseAckFrame(p.ackFrame, b, typ, ackDelayExponent, v) - frame = p.ackFrame - case resetStreamFrameType: - frame, l, err = parseResetStreamFrame(b, v) - case stopSendingFrameType: - frame, l, err = parseStopSendingFrame(b, v) - case cryptoFrameType: - frame, l, err = parseCryptoFrame(b, v) - case newTokenFrameType: - frame, l, err = parseNewTokenFrame(b, v) - case maxDataFrameType: - frame, l, err = parseMaxDataFrame(b, v) - case maxStreamDataFrameType: - frame, l, err = parseMaxStreamDataFrame(b, v) - case bidiMaxStreamsFrameType, uniMaxStreamsFrameType: - frame, l, err = parseMaxStreamsFrame(b, typ, v) - case dataBlockedFrameType: - frame, l, err = parseDataBlockedFrame(b, v) - case streamDataBlockedFrameType: - frame, l, err = parseStreamDataBlockedFrame(b, v) - case bidiStreamBlockedFrameType, uniStreamBlockedFrameType: - frame, l, err = parseStreamsBlockedFrame(b, typ, v) - case newConnectionIDFrameType: - frame, l, err = parseNewConnectionIDFrame(b, v) - case retireConnectionIDFrameType: - frame, l, err = parseRetireConnectionIDFrame(b, v) - case pathChallengeFrameType: - frame, l, err = parsePathChallengeFrame(b, v) - case pathResponseFrameType: - frame, l, err = parsePathResponseFrame(b, v) - case connectionCloseFrameType, applicationCloseFrameType: - frame, l, err = parseConnectionCloseFrame(b, typ, v) - case handshakeDoneFrameType: - frame = &HandshakeDoneFrame{} - case 0x30, 0x31: - if p.supportsDatagrams { - frame, l, err = parseDatagramFrame(b, typ, v) - break - } - fallthrough - default: - err = errors.New("unknown frame type") +func (p *FrameParser) ParseStreamFrame(frameType FrameType, data []byte, v protocol.Version) (*StreamFrame, int, error) { + frame, n, err := ParseStreamFrame(data, frameType, v) + if err != nil { + return nil, n, &qerr.TransportError{ + ErrorCode: qerr.FrameEncodingError, + FrameType: uint64(frameType), + ErrorMessage: err.Error(), } } - if err != nil { - return nil, 0, err + return frame, n, nil +} + +func (p *FrameParser) ParseAckFrame(frameType FrameType, data []byte, encLevel protocol.EncryptionLevel, v protocol.Version) (*AckFrame, int, error) { + ackDelayExponent := p.ackDelayExponent + if encLevel != protocol.Encryption1RTT { + ackDelayExponent = protocol.DefaultAckDelayExponent } - if !p.isAllowedAtEncLevel(frame, encLevel) { - return nil, l, fmt.Errorf("%s not allowed at encryption level %s", reflect.TypeOf(frame).Elem().Name(), encLevel) + p.ackFrame.Reset() + l, err := parseAckFrame(p.ackFrame, data, frameType, ackDelayExponent, v) + if err != nil { + return nil, l, &qerr.TransportError{ + ErrorCode: qerr.FrameEncodingError, + FrameType: uint64(frameType), + ErrorMessage: err.Error(), + } } - return frame, l, nil + + return p.ackFrame, l, nil } -func (p *FrameParser) isAllowedAtEncLevel(f Frame, encLevel protocol.EncryptionLevel) bool { - switch encLevel { - case protocol.EncryptionInitial, protocol.EncryptionHandshake: - switch f.(type) { - case *CryptoFrame, *AckFrame, *ConnectionCloseFrame, *PingFrame: - return true - default: - return false - } - case protocol.Encryption0RTT: - switch f.(type) { - case *CryptoFrame, *AckFrame, *ConnectionCloseFrame, *NewTokenFrame, *PathResponseFrame, *RetireConnectionIDFrame: - return false - default: - return true +func (p *FrameParser) ParseDatagramFrame(frameType FrameType, data []byte, v protocol.Version) (*DatagramFrame, int, error) { + f, l, err := parseDatagramFrame(data, frameType, v) + if err != nil { + return nil, 0, &qerr.TransportError{ + ErrorCode: qerr.FrameEncodingError, + FrameType: uint64(frameType), + ErrorMessage: err.Error(), } - case protocol.Encryption1RTT: - return true + } + return f, l, nil +} + +// ParseLessCommonFrame parses everything except STREAM, ACK or DATAGRAM. +// These cases should be handled separately for performance reasons. +func (p *FrameParser) ParseLessCommonFrame(frameType FrameType, data []byte, v protocol.Version) (Frame, int, error) { + var frame Frame + var l int + var err error + //nolint:exhaustive // Common frames should already be handled. + switch frameType { + case FrameTypePing: + frame = &PingFrame{} + case FrameTypeResetStream: + frame, l, err = parseResetStreamFrame(data, false, v) + case FrameTypeStopSending: + frame, l, err = parseStopSendingFrame(data, v) + case FrameTypeCrypto: + frame, l, err = parseCryptoFrame(data, v) + case FrameTypeNewToken: + frame, l, err = parseNewTokenFrame(data, v) + case FrameTypeMaxData: + frame, l, err = parseMaxDataFrame(data, v) + case FrameTypeMaxStreamData: + frame, l, err = parseMaxStreamDataFrame(data, v) + case FrameTypeBidiMaxStreams, FrameTypeUniMaxStreams: + frame, l, err = parseMaxStreamsFrame(data, frameType, v) + case FrameTypeDataBlocked: + frame, l, err = parseDataBlockedFrame(data, v) + case FrameTypeStreamDataBlocked: + frame, l, err = parseStreamDataBlockedFrame(data, v) + case FrameTypeBidiStreamBlocked, FrameTypeUniStreamBlocked: + frame, l, err = parseStreamsBlockedFrame(data, frameType, v) + case FrameTypeNewConnectionID: + frame, l, err = parseNewConnectionIDFrame(data, v) + case FrameTypeRetireConnectionID: + frame, l, err = parseRetireConnectionIDFrame(data, v) + case FrameTypePathChallenge: + frame, l, err = parsePathChallengeFrame(data, v) + case FrameTypePathResponse: + frame, l, err = parsePathResponseFrame(data, v) + case FrameTypeConnectionClose, FrameTypeApplicationClose: + frame, l, err = parseConnectionCloseFrame(data, frameType, v) + case FrameTypeHandshakeDone: + frame = &HandshakeDoneFrame{} + case FrameTypeResetStreamAt: + frame, l, err = parseResetStreamFrame(data, true, v) + case FrameTypeAckFrequency: + frame, l, err = parseAckFrequencyFrame(data, v) + case FrameTypeImmediateAck: + frame = &ImmediateAckFrame{} default: - panic("unknown encryption level") + err = errUnknownFrameType + } + if err != nil { + return frame, l, &qerr.TransportError{ + ErrorCode: qerr.FrameEncodingError, + FrameType: uint64(frameType), + ErrorMessage: err.Error(), + } } + return frame, l, err } // SetAckDelayExponent sets the acknowledgment delay exponent (sent in the transport parameters). diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/frame_type.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/frame_type.go new file mode 100644 index 000000000..d3d086d91 --- /dev/null +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/frame_type.go @@ -0,0 +1,81 @@ +package wire + +import "github.com/quic-go/quic-go/internal/protocol" + +type FrameType uint64 + +// These constants correspond to those defined in RFC 9000. +// Stream frame types are not listed explicitly here; use FrameType.IsStreamFrameType() to identify them. +const ( + FrameTypePing FrameType = 0x1 + FrameTypeAck FrameType = 0x2 + FrameTypeAckECN FrameType = 0x3 + FrameTypeResetStream FrameType = 0x4 + FrameTypeStopSending FrameType = 0x5 + FrameTypeCrypto FrameType = 0x6 + FrameTypeNewToken FrameType = 0x7 + + FrameTypeMaxData FrameType = 0x10 + FrameTypeMaxStreamData FrameType = 0x11 + FrameTypeBidiMaxStreams FrameType = 0x12 + FrameTypeUniMaxStreams FrameType = 0x13 + FrameTypeDataBlocked FrameType = 0x14 + FrameTypeStreamDataBlocked FrameType = 0x15 + FrameTypeBidiStreamBlocked FrameType = 0x16 + FrameTypeUniStreamBlocked FrameType = 0x17 + FrameTypeNewConnectionID FrameType = 0x18 + FrameTypeRetireConnectionID FrameType = 0x19 + FrameTypePathChallenge FrameType = 0x1a + FrameTypePathResponse FrameType = 0x1b + FrameTypeConnectionClose FrameType = 0x1c + FrameTypeApplicationClose FrameType = 0x1d + FrameTypeHandshakeDone FrameType = 0x1e + // https://datatracker.ietf.org/doc/draft-ietf-quic-reliable-stream-reset/07/ + FrameTypeResetStreamAt FrameType = 0x24 + // https://datatracker.ietf.org/doc/draft-ietf-quic-ack-frequency/11/ + FrameTypeAckFrequency FrameType = 0xaf + FrameTypeImmediateAck FrameType = 0x1f + + FrameTypeDatagramNoLength FrameType = 0x30 + FrameTypeDatagramWithLength FrameType = 0x31 +) + +func (t FrameType) IsStreamFrameType() bool { + return t >= 0x8 && t <= 0xf +} + +func (t FrameType) isValidRFC9000() bool { + return t <= 0x1e +} + +func (t FrameType) IsAckFrameType() bool { + return t == FrameTypeAck || t == FrameTypeAckECN +} + +func (t FrameType) IsDatagramFrameType() bool { + return t == FrameTypeDatagramNoLength || t == FrameTypeDatagramWithLength +} + +func (t FrameType) isAllowedAtEncLevel(encLevel protocol.EncryptionLevel) bool { + //nolint:exhaustive + switch encLevel { + case protocol.EncryptionInitial, protocol.EncryptionHandshake: + switch t { + case FrameTypeCrypto, FrameTypeAck, FrameTypeAckECN, FrameTypeConnectionClose, FrameTypePing: + return true + default: + return false + } + case protocol.Encryption0RTT: + switch t { + case FrameTypeCrypto, FrameTypeAck, FrameTypeAckECN, FrameTypeConnectionClose, FrameTypeNewToken, FrameTypePathResponse, FrameTypeRetireConnectionID: + return false + default: + return true + } + case protocol.Encryption1RTT: + return true + default: + panic("unknown encryption level") + } +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/handshake_done_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/handshake_done_frame.go index 85dd64745..bf95f525b 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/handshake_done_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/handshake_done_frame.go @@ -8,7 +8,7 @@ import ( type HandshakeDoneFrame struct{} func (f *HandshakeDoneFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { - return append(b, handshakeDoneFrameType), nil + return append(b, byte(FrameTypeHandshakeDone)), nil } // Length of a written frame diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/immediate_ack_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/immediate_ack_frame.go new file mode 100644 index 000000000..aeff71b4e --- /dev/null +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/immediate_ack_frame.go @@ -0,0 +1,18 @@ +package wire + +import ( + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/quicvarint" +) + +// An ImmediateAckFrame is an IMMEDIATE_ACK frame +type ImmediateAckFrame struct{} + +func (f *ImmediateAckFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { + return quicvarint.Append(b, uint64(FrameTypeImmediateAck)), nil +} + +// Length of a written frame +func (f *ImmediateAckFrame) Length(_ protocol.Version) protocol.ByteCount { + return protocol.ByteCount(quicvarint.Len(uint64(FrameTypeImmediateAck))) +} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/max_data_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/max_data_frame.go index 5819c0273..bfbdcba66 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/max_data_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/max_data_frame.go @@ -22,7 +22,7 @@ func parseMaxDataFrame(b []byte, _ protocol.Version) (*MaxDataFrame, int, error) } func (f *MaxDataFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { - b = append(b, maxDataFrameType) + b = append(b, byte(FrameTypeMaxData)) b = quicvarint.Append(b, uint64(f.MaximumData)) return b, nil } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/max_stream_data_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/max_stream_data_frame.go index db9091af8..0966ea469 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/max_stream_data_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/max_stream_data_frame.go @@ -31,7 +31,7 @@ func parseMaxStreamDataFrame(b []byte, _ protocol.Version) (*MaxStreamDataFrame, } func (f *MaxStreamDataFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { - b = append(b, maxStreamDataFrameType) + b = append(b, byte(FrameTypeMaxStreamData)) b = quicvarint.Append(b, uint64(f.StreamID)) b = quicvarint.Append(b, uint64(f.MaximumStreamData)) return b, nil diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/max_streams_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/max_streams_frame.go index a8745bd12..30612e23b 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/max_streams_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/max_streams_frame.go @@ -13,12 +13,13 @@ type MaxStreamsFrame struct { MaxStreamNum protocol.StreamNum } -func parseMaxStreamsFrame(b []byte, typ uint64, _ protocol.Version) (*MaxStreamsFrame, int, error) { +func parseMaxStreamsFrame(b []byte, typ FrameType, _ protocol.Version) (*MaxStreamsFrame, int, error) { f := &MaxStreamsFrame{} + //nolint:exhaustive // Function will only be called with BidiMaxStreamsFrameType or UniMaxStreamsFrameType switch typ { - case bidiMaxStreamsFrameType: + case FrameTypeBidiMaxStreams: f.Type = protocol.StreamTypeBidi - case uniMaxStreamsFrameType: + case FrameTypeUniMaxStreams: f.Type = protocol.StreamTypeUni } streamID, l, err := quicvarint.Parse(b) @@ -35,9 +36,9 @@ func parseMaxStreamsFrame(b []byte, typ uint64, _ protocol.Version) (*MaxStreams func (f *MaxStreamsFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { switch f.Type { case protocol.StreamTypeBidi: - b = append(b, bidiMaxStreamsFrameType) + b = append(b, byte(FrameTypeBidiMaxStreams)) case protocol.StreamTypeUni: - b = append(b, uniMaxStreamsFrameType) + b = append(b, byte(FrameTypeUniMaxStreams)) } b = quicvarint.Append(b, uint64(f.MaxStreamNum)) return b, nil diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/new_connection_id_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/new_connection_id_frame.go index 852d46ef1..058319266 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/new_connection_id_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/new_connection_id_frame.go @@ -30,7 +30,7 @@ func parseNewConnectionIDFrame(b []byte, _ protocol.Version) (*NewConnectionIDFr } b = b[l:] if ret > seq { - //nolint:stylecheck + //nolint:staticcheck // SA1021: Retire Prior To is the name of the field return nil, 0, fmt.Errorf("Retire Prior To value (%d) larger than Sequence Number (%d)", ret, seq) } if len(b) == 0 { @@ -61,7 +61,7 @@ func parseNewConnectionIDFrame(b []byte, _ protocol.Version) (*NewConnectionIDFr } func (f *NewConnectionIDFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { - b = append(b, newConnectionIDFrameType) + b = append(b, byte(FrameTypeNewConnectionID)) b = quicvarint.Append(b, f.SequenceNumber) b = quicvarint.Append(b, f.RetirePriorTo) connIDLen := f.ConnectionID.Len() diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/new_token_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/new_token_frame.go index f1d4d00fe..73d356b1a 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/new_token_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/new_token_frame.go @@ -31,7 +31,7 @@ func parseNewTokenFrame(b []byte, _ protocol.Version) (*NewTokenFrame, int, erro } func (f *NewTokenFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { - b = append(b, newTokenFrameType) + b = append(b, byte(FrameTypeNewToken)) b = quicvarint.Append(b, uint64(len(f.Token))) b = append(b, f.Token...) return b, nil diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/path_challenge_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/path_challenge_frame.go index 2aca989fa..7a4a767e5 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/path_challenge_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/path_challenge_frame.go @@ -21,7 +21,7 @@ func parsePathChallengeFrame(b []byte, _ protocol.Version) (*PathChallengeFrame, } func (f *PathChallengeFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { - b = append(b, pathChallengeFrameType) + b = append(b, byte(FrameTypePathChallenge)) b = append(b, f.Data[:]...) return b, nil } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/path_response_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/path_response_frame.go index 76532c852..e76d037b1 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/path_response_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/path_response_frame.go @@ -21,7 +21,7 @@ func parsePathResponseFrame(b []byte, _ protocol.Version) (*PathResponseFrame, i } func (f *PathResponseFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { - b = append(b, pathResponseFrameType) + b = append(b, byte(FrameTypePathResponse)) b = append(b, f.Data[:]...) return b, nil } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/ping_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/ping_frame.go index 71f8d16c3..5d344d447 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/ping_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/ping_frame.go @@ -8,7 +8,7 @@ import ( type PingFrame struct{} func (f *PingFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { - return append(b, pingFrameType), nil + return append(b, byte(FrameTypePing)), nil } // Length of a written frame diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/pool.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/pool.go index 18ab43793..d656fad4b 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/pool.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/pool.go @@ -9,7 +9,7 @@ import ( var pool sync.Pool func init() { - pool.New = func() interface{} { + pool.New = func() any { return &StreamFrame{ Data: make([]byte, 0, protocol.MaxPacketBufferSize), fromPool: true, diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/reset_stream_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/reset_stream_frame.go index a20029af2..4101b76b2 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/reset_stream_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/reset_stream_frame.go @@ -1,55 +1,79 @@ package wire import ( + "fmt" + "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/quicvarint" ) -// A ResetStreamFrame is a RESET_STREAM frame in QUIC +// A ResetStreamFrame is a RESET_STREAM or RESET_STREAM_AT frame in QUIC type ResetStreamFrame struct { - StreamID protocol.StreamID - ErrorCode qerr.StreamErrorCode - FinalSize protocol.ByteCount + StreamID protocol.StreamID + ErrorCode qerr.StreamErrorCode + FinalSize protocol.ByteCount + ReliableSize protocol.ByteCount } -func parseResetStreamFrame(b []byte, _ protocol.Version) (*ResetStreamFrame, int, error) { +func parseResetStreamFrame(b []byte, isResetStreamAt bool, _ protocol.Version) (*ResetStreamFrame, int, error) { startLen := len(b) - var streamID protocol.StreamID - var byteOffset protocol.ByteCount - sid, l, err := quicvarint.Parse(b) + streamID, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] - streamID = protocol.StreamID(sid) errorCode, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] - bo, l, err := quicvarint.Parse(b) + finalSize, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } - byteOffset = protocol.ByteCount(bo) + b = b[l:] + + var reliableSize uint64 + if isResetStreamAt { + reliableSize, l, err = quicvarint.Parse(b) + if err != nil { + return nil, 0, replaceUnexpectedEOF(err) + } + b = b[l:] + } + if reliableSize > finalSize { + return nil, 0, fmt.Errorf("RESET_STREAM_AT: reliable size can't be larger than final size (%d vs %d)", reliableSize, finalSize) + } return &ResetStreamFrame{ - StreamID: streamID, - ErrorCode: qerr.StreamErrorCode(errorCode), - FinalSize: byteOffset, - }, startLen - len(b) + l, nil + StreamID: protocol.StreamID(streamID), + ErrorCode: qerr.StreamErrorCode(errorCode), + FinalSize: protocol.ByteCount(finalSize), + ReliableSize: protocol.ByteCount(reliableSize), + }, startLen - len(b), nil } func (f *ResetStreamFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { - b = append(b, resetStreamFrameType) + if f.ReliableSize == 0 { + b = quicvarint.Append(b, uint64(FrameTypeResetStream)) + } else { + b = quicvarint.Append(b, uint64(FrameTypeResetStreamAt)) + } b = quicvarint.Append(b, uint64(f.StreamID)) b = quicvarint.Append(b, uint64(f.ErrorCode)) b = quicvarint.Append(b, uint64(f.FinalSize)) + if f.ReliableSize > 0 { + b = quicvarint.Append(b, uint64(f.ReliableSize)) + } return b, nil } // Length of a written frame func (f *ResetStreamFrame) Length(protocol.Version) protocol.ByteCount { - return 1 + protocol.ByteCount(quicvarint.Len(uint64(f.StreamID))+quicvarint.Len(uint64(f.ErrorCode))+quicvarint.Len(uint64(f.FinalSize))) + size := 1 // the frame type for both RESET_STREAM and RESET_STREAM_AT fits into 1 byte + if f.ReliableSize > 0 { + size += quicvarint.Len(uint64(f.ReliableSize)) + } + return protocol.ByteCount(size + quicvarint.Len(uint64(f.StreamID)) + quicvarint.Len(uint64(f.ErrorCode)) + quicvarint.Len(uint64(f.FinalSize))) } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/retire_connection_id_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/retire_connection_id_frame.go index 27aeff842..1927f9dc0 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/retire_connection_id_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/retire_connection_id_frame.go @@ -19,7 +19,7 @@ func parseRetireConnectionIDFrame(b []byte, _ protocol.Version) (*RetireConnecti } func (f *RetireConnectionIDFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { - b = append(b, retireConnectionIDFrameType) + b = append(b, byte(FrameTypeRetireConnectionID)) b = quicvarint.Append(b, f.SequenceNumber) return b, nil } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/stop_sending_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/stop_sending_frame.go index a2326f8ec..2b15c7109 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/stop_sending_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/stop_sending_frame.go @@ -38,7 +38,7 @@ func (f *StopSendingFrame) Length(_ protocol.Version) protocol.ByteCount { } func (f *StopSendingFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { - b = append(b, stopSendingFrameType) + b = append(b, byte(FrameTypeStopSending)) b = quicvarint.Append(b, uint64(f.StreamID)) b = quicvarint.Append(b, uint64(f.ErrorCode)) return b, nil diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/stream_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/stream_frame.go index cdc32722f..e53962b19 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/stream_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/stream_frame.go @@ -19,7 +19,7 @@ type StreamFrame struct { fromPool bool } -func parseStreamFrame(b []byte, typ uint64, _ protocol.Version) (*StreamFrame, int, error) { +func ParseStreamFrame(b []byte, typ FrameType, _ protocol.Version) (*StreamFrame, int, error) { startLen := len(b) hasOffset := typ&0b100 > 0 fin := typ&0b1 > 0 diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/streams_blocked_frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/streams_blocked_frame.go index c946fec31..d98fde46c 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/streams_blocked_frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/streams_blocked_frame.go @@ -13,12 +13,13 @@ type StreamsBlockedFrame struct { StreamLimit protocol.StreamNum } -func parseStreamsBlockedFrame(b []byte, typ uint64, _ protocol.Version) (*StreamsBlockedFrame, int, error) { +func parseStreamsBlockedFrame(b []byte, typ FrameType, _ protocol.Version) (*StreamsBlockedFrame, int, error) { f := &StreamsBlockedFrame{} + //nolint:exhaustive // This will only be called with a BidiStreamBlockedFrameType or a UniStreamBlockedFrameType. switch typ { - case bidiStreamBlockedFrameType: + case FrameTypeBidiStreamBlocked: f.Type = protocol.StreamTypeBidi - case uniStreamBlockedFrameType: + case FrameTypeUniStreamBlocked: f.Type = protocol.StreamTypeUni } streamLimit, l, err := quicvarint.Parse(b) @@ -35,9 +36,9 @@ func parseStreamsBlockedFrame(b []byte, typ uint64, _ protocol.Version) (*Stream func (f *StreamsBlockedFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { switch f.Type { case protocol.StreamTypeBidi: - b = append(b, bidiStreamBlockedFrameType) + b = append(b, byte(FrameTypeBidiStreamBlocked)) case protocol.StreamTypeUni: - b = append(b, uniStreamBlockedFrameType) + b = append(b, byte(FrameTypeUniStreamBlocked)) } b = quicvarint.Append(b, uint64(f.StreamLimit)) return b, nil diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go index cee74b8fa..ff101b6e7 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "math" "net/netip" "slices" "time" @@ -45,6 +46,10 @@ const ( retrySourceConnectionIDParameterID transportParameterID = 0x10 // RFC 9221 maxDatagramFrameSizeParameterID transportParameterID = 0x20 + // https://datatracker.ietf.org/doc/draft-ietf-quic-reliable-stream-reset/06/ + resetStreamAtParameterID transportParameterID = 0x17f7586d2cb571 + // https://datatracker.ietf.org/doc/draft-ietf-quic-ack-frequency/11/ + minAckDelayParameterID transportParameterID = 0xff04de1b ) // PreferredAddress is the value encoding in the preferred_address transport parameter @@ -82,7 +87,9 @@ type TransportParameters struct { StatelessResetToken *protocol.StatelessResetToken ActiveConnectionIDLimit uint64 - MaxDatagramFrameSize protocol.ByteCount + MaxDatagramFrameSize protocol.ByteCount // RFC 9221 + EnableResetStreamAt bool // https://datatracker.ietf.org/doc/draft-ietf-quic-reliable-stream-reset/06/ + MinAckDelay *time.Duration } // Unmarshal the transport parameters @@ -103,12 +110,12 @@ func (p *TransportParameters) unmarshal(b []byte, sentBy protocol.Perspective, f var ( readOriginalDestinationConnectionID bool readInitialSourceConnectionID bool - readActiveConnectionIDLimit bool ) p.AckDelayExponent = protocol.DefaultAckDelayExponent p.MaxAckDelay = protocol.DefaultMaxAckDelay p.MaxDatagramFrameSize = protocol.InvalidByteCount + p.ActiveConnectionIDLimit = protocol.DefaultActiveConnectionIDLimit for len(b) > 0 { paramIDInt, l, err := quicvarint.Parse(b) @@ -127,9 +134,6 @@ func (p *TransportParameters) unmarshal(b []byte, sentBy protocol.Perspective, f } parameterIDs = append(parameterIDs, paramID) switch paramID { - case activeConnectionIDLimitParameterID: - readActiveConnectionIDLimit = true - fallthrough case maxIdleTimeoutParameterID, maxUDPPayloadSizeParameterID, initialMaxDataParameterID, @@ -140,7 +144,9 @@ func (p *TransportParameters) unmarshal(b []byte, sentBy protocol.Perspective, f initialMaxStreamsUniParameterID, maxAckDelayParameterID, maxDatagramFrameSizeParameterID, - ackDelayExponentParameterID: + ackDelayExponentParameterID, + activeConnectionIDLimitParameterID, + minAckDelayParameterID: if err := p.readNumericTransportParameter(b, paramID, int(paramLen)); err != nil { return err } @@ -199,13 +205,19 @@ func (p *TransportParameters) unmarshal(b []byte, sentBy protocol.Perspective, f connID := protocol.ParseConnectionID(b[:paramLen]) b = b[paramLen:] p.RetrySourceConnectionID = &connID + case resetStreamAtParameterID: + if paramLen != 0 { + return fmt.Errorf("wrong length for reset_stream_at: %d (expected empty)", paramLen) + } + p.EnableResetStreamAt = true default: b = b[paramLen:] } } - if !readActiveConnectionIDLimit { - p.ActiveConnectionIDLimit = protocol.DefaultActiveConnectionIDLimit + // min_ack_delay must be less or equal to max_ack_delay + if p.MinAckDelay != nil && *p.MinAckDelay > p.MaxAckDelay { + return fmt.Errorf("min_ack_delay (%s) is greater than max_ack_delay (%s)", *p.MinAckDelay, p.MaxAckDelay) } if !fromSessionTicket { if sentBy == protocol.PerspectiveServer && !readOriginalDestinationConnectionID { @@ -245,11 +257,15 @@ func (p *TransportParameters) readPreferredAddress(b []byte, expectedLen int) er copy(ipv4[:], b[:4]) port4 := binary.BigEndian.Uint16(b[4:]) b = b[4+2:] - pa.IPv4 = netip.AddrPortFrom(netip.AddrFrom4(ipv4), port4) + if port4 != 0 && ipv4 != [4]byte{} { + pa.IPv4 = netip.AddrPortFrom(netip.AddrFrom4(ipv4), port4) + } var ipv6 [16]byte copy(ipv6[:], b[:16]) port6 := binary.BigEndian.Uint16(b[16:]) - pa.IPv6 = netip.AddrPortFrom(netip.AddrFrom16(ipv6), port6) + if port6 != 0 && ipv6 != [16]byte{} { + pa.IPv6 = netip.AddrPortFrom(netip.AddrFrom16(ipv6), port6) + } b = b[16+2:] connIDLen := int(b[0]) b = b[1:] @@ -322,6 +338,12 @@ func (p *TransportParameters) readNumericTransportParameter(b []byte, paramID tr p.ActiveConnectionIDLimit = val case maxDatagramFrameSizeParameterID: p.MaxDatagramFrameSize = protocol.ByteCount(val) + case minAckDelayParameterID: + mad := time.Duration(val) * time.Microsecond + if mad < 0 { + mad = math.MaxInt64 + } + p.MinAckDelay = &mad default: return fmt.Errorf("TransportParameter BUG: transport parameter %d not found", paramID) } @@ -391,12 +413,20 @@ func (p *TransportParameters) Marshal(pers protocol.Perspective) []byte { if p.PreferredAddress != nil { b = quicvarint.Append(b, uint64(preferredAddressParameterID)) b = quicvarint.Append(b, 4+2+16+2+1+uint64(p.PreferredAddress.ConnectionID.Len())+16) - ip4 := p.PreferredAddress.IPv4.Addr().As4() - b = append(b, ip4[:]...) - b = binary.BigEndian.AppendUint16(b, p.PreferredAddress.IPv4.Port()) - ip6 := p.PreferredAddress.IPv6.Addr().As16() - b = append(b, ip6[:]...) - b = binary.BigEndian.AppendUint16(b, p.PreferredAddress.IPv6.Port()) + if p.PreferredAddress.IPv4.IsValid() { + ipv4 := p.PreferredAddress.IPv4.Addr().As4() + b = append(b, ipv4[:]...) + b = binary.BigEndian.AppendUint16(b, p.PreferredAddress.IPv4.Port()) + } else { + b = append(b, make([]byte, 6)...) + } + if p.PreferredAddress.IPv6.IsValid() { + ipv6 := p.PreferredAddress.IPv6.Addr().As16() + b = append(b, ipv6[:]...) + b = binary.BigEndian.AppendUint16(b, p.PreferredAddress.IPv6.Port()) + } else { + b = append(b, make([]byte, 18)...) + } b = append(b, uint8(p.PreferredAddress.ConnectionID.Len())) b = append(b, p.PreferredAddress.ConnectionID.Bytes()...) b = append(b, p.PreferredAddress.StatelessResetToken[:]...) @@ -416,9 +446,18 @@ func (p *TransportParameters) Marshal(pers protocol.Perspective) []byte { b = quicvarint.Append(b, uint64(p.RetrySourceConnectionID.Len())) b = append(b, p.RetrySourceConnectionID.Bytes()...) } + // QUIC datagrams if p.MaxDatagramFrameSize != protocol.InvalidByteCount { b = p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize)) } + // QUIC Stream Resets with Partial Delivery + if p.EnableResetStreamAt { + b = quicvarint.Append(b, uint64(resetStreamAtParameterID)) + b = quicvarint.Append(b, 0) + } + if p.MinAckDelay != nil { + b = p.marshalVarintParam(b, minAckDelayParameterID, uint64(*p.MinAckDelay/time.Microsecond)) + } if pers == protocol.PerspectiveClient && len(AdditionalTransportParametersClient) > 0 { for k, v := range AdditionalTransportParametersClient { @@ -460,12 +499,18 @@ func (p *TransportParameters) MarshalForSessionTicket(b []byte) []byte { b = p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum)) // initial_max_uni_streams b = p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum)) + // active_connection_id_limit + b = p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit) // max_datagram_frame_size if p.MaxDatagramFrameSize != protocol.InvalidByteCount { b = p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize)) } - // active_connection_id_limit - return p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit) + // reset_stream_at + if p.EnableResetStreamAt { + b = quicvarint.Append(b, uint64(resetStreamAtParameterID)) + b = quicvarint.Append(b, 0) + } + return b } // UnmarshalFromSessionTicket unmarshals transport parameters from a session ticket. @@ -512,13 +557,13 @@ func (p *TransportParameters) ValidForUpdate(saved *TransportParameters) bool { // String returns a string representation, intended for logging. func (p *TransportParameters) String() string { logString := "&wire.TransportParameters{OriginalDestinationConnectionID: %s, InitialSourceConnectionID: %s, " - logParams := []interface{}{p.OriginalDestinationConnectionID, p.InitialSourceConnectionID} + logParams := []any{p.OriginalDestinationConnectionID, p.InitialSourceConnectionID} if p.RetrySourceConnectionID != nil { logString += "RetrySourceConnectionID: %s, " logParams = append(logParams, p.RetrySourceConnectionID) } logString += "InitialMaxStreamDataBidiLocal: %d, InitialMaxStreamDataBidiRemote: %d, InitialMaxStreamDataUni: %d, InitialMaxData: %d, MaxBidiStreamNum: %d, MaxUniStreamNum: %d, MaxIdleTimeout: %s, AckDelayExponent: %d, MaxAckDelay: %s, ActiveConnectionIDLimit: %d" - logParams = append(logParams, []interface{}{p.InitialMaxStreamDataBidiLocal, p.InitialMaxStreamDataBidiRemote, p.InitialMaxStreamDataUni, p.InitialMaxData, p.MaxBidiStreamNum, p.MaxUniStreamNum, p.MaxIdleTimeout, p.AckDelayExponent, p.MaxAckDelay, p.ActiveConnectionIDLimit}...) + logParams = append(logParams, []any{p.InitialMaxStreamDataBidiLocal, p.InitialMaxStreamDataBidiRemote, p.InitialMaxStreamDataUni, p.InitialMaxData, p.MaxBidiStreamNum, p.MaxUniStreamNum, p.MaxIdleTimeout, p.AckDelayExponent, p.MaxAckDelay, p.ActiveConnectionIDLimit}...) if p.StatelessResetToken != nil { // the client never sends a stateless reset token logString += ", StatelessResetToken: %#x" logParams = append(logParams, *p.StatelessResetToken) @@ -527,6 +572,12 @@ func (p *TransportParameters) String() string { logString += ", MaxDatagramFrameSize: %d" logParams = append(logParams, p.MaxDatagramFrameSize) } + logString += ", EnableResetStreamAt: %t" + logParams = append(logParams, p.EnableResetStreamAt) + if p.MinAckDelay != nil { + logString += ", MinAckDelay: %s" + logParams = append(logParams, *p.MinAckDelay) + } logString += "}" return fmt.Sprintf(logString, logParams...) } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/version_negotiation.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/version_negotiation.go index a551aa8c9..407cb629c 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/version_negotiation.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/internal/wire/version_negotiation.go @@ -16,11 +16,11 @@ func ParseVersionNegotiationPacket(b []byte) (dest, src protocol.ArbitraryLenCon } b = b[n:] if len(b) == 0 { - //nolint:stylecheck + //nolint:staticcheck // SA1021: the packet is called Version Negotiation packet return nil, nil, nil, errors.New("Version Negotiation packet has empty version list") } if len(b)%4 != 0 { - //nolint:stylecheck + //nolint:staticcheck // SA1021: the packet is called Version Negotiation packet return nil, nil, nil, errors.New("Version Negotiation packet has a version list with an invalid length") } versions := make([]protocol.Version, len(b)/4) diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/connection_tracer.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/connection_tracer.go deleted file mode 100644 index f218e046f..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/connection_tracer.go +++ /dev/null @@ -1,44 +0,0 @@ -package logging - -import ( - "net" - "time" -) - -//go:generate go run generate_multiplexer.go ConnectionTracer connection_tracer.go multiplexer.tmpl connection_tracer_multiplexer.go - -// A ConnectionTracer records events. -type ConnectionTracer struct { - StartedConnection func(local, remote net.Addr, srcConnID, destConnID ConnectionID) - NegotiatedVersion func(chosen Version, clientVersions, serverVersions []Version) - ClosedConnection func(err error) - SentTransportParameters func(parameters *TransportParameters) - ReceivedTransportParameters func(parameters *TransportParameters) - RestoredTransportParameters func(parameters *TransportParameters) // for 0-RTT - SentLongHeaderPacket func(hdr *ExtendedHeader, size ByteCount, ecn ECN, ack *AckFrame, frames []Frame) - SentShortHeaderPacket func(hdr *ShortHeader, size ByteCount, ecn ECN, ack *AckFrame, frames []Frame) - ReceivedVersionNegotiationPacket func(dest, src ArbitraryLenConnectionID, versions []Version) - ReceivedRetry func(hdr *Header) - ReceivedLongHeaderPacket func(hdr *ExtendedHeader, size ByteCount, ecn ECN, frames []Frame) - ReceivedShortHeaderPacket func(hdr *ShortHeader, size ByteCount, ecn ECN, frames []Frame) - BufferedPacket func(packetType PacketType, size ByteCount) - DroppedPacket func(packetType PacketType, pn PacketNumber, size ByteCount, reason PacketDropReason) - UpdatedMetrics func(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int) - AcknowledgedPacket func(encLevel EncryptionLevel, pn PacketNumber) - LostPacket func(encLevel EncryptionLevel, pn PacketNumber, reason PacketLossReason) - UpdatedMTU func(mtu ByteCount, done bool) - UpdatedCongestionState func(state CongestionState) - UpdatedPTOCount func(value uint32) - UpdatedKeyFromTLS func(encLevel EncryptionLevel, p Perspective) - UpdatedKey func(keyPhase KeyPhase, remote bool) - DroppedEncryptionLevel func(encLevel EncryptionLevel) - DroppedKey func(keyPhase KeyPhase) - SetLossTimer func(timerType TimerType, encLevel EncryptionLevel, time time.Time) - LossTimerExpired func(timerType TimerType, encLevel EncryptionLevel) - LossTimerCanceled func() - ECNStateUpdated func(state ECNState, trigger ECNStateTrigger) - ChoseALPN func(protocol string) - // Close is called when the connection is closed. - Close func() - Debug func(name, msg string) -} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/connection_tracer_multiplexer.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/connection_tracer_multiplexer.go deleted file mode 100644 index 3a87058c8..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/connection_tracer_multiplexer.go +++ /dev/null @@ -1,236 +0,0 @@ -// Code generated by generate_multiplexer.go; DO NOT EDIT. - -package logging - -import ( - "net" - "time" -) - -func NewMultiplexedConnectionTracer(tracers ...*ConnectionTracer) *ConnectionTracer { - if len(tracers) == 0 { - return nil - } - if len(tracers) == 1 { - return tracers[0] - } - return &ConnectionTracer{ - StartedConnection: func(local net.Addr, remote net.Addr, srcConnID ConnectionID, destConnID ConnectionID) { - for _, t := range tracers { - if t.StartedConnection != nil { - t.StartedConnection(local, remote, srcConnID, destConnID) - } - } - }, - NegotiatedVersion: func(chosen Version, clientVersions []Version, serverVersions []Version) { - for _, t := range tracers { - if t.NegotiatedVersion != nil { - t.NegotiatedVersion(chosen, clientVersions, serverVersions) - } - } - }, - ClosedConnection: func(err error) { - for _, t := range tracers { - if t.ClosedConnection != nil { - t.ClosedConnection(err) - } - } - }, - SentTransportParameters: func(parameters *TransportParameters) { - for _, t := range tracers { - if t.SentTransportParameters != nil { - t.SentTransportParameters(parameters) - } - } - }, - ReceivedTransportParameters: func(parameters *TransportParameters) { - for _, t := range tracers { - if t.ReceivedTransportParameters != nil { - t.ReceivedTransportParameters(parameters) - } - } - }, - RestoredTransportParameters: func(parameters *TransportParameters) { - for _, t := range tracers { - if t.RestoredTransportParameters != nil { - t.RestoredTransportParameters(parameters) - } - } - }, - SentLongHeaderPacket: func(hdr *ExtendedHeader, size ByteCount, ecn ECN, ack *AckFrame, frames []Frame) { - for _, t := range tracers { - if t.SentLongHeaderPacket != nil { - t.SentLongHeaderPacket(hdr, size, ecn, ack, frames) - } - } - }, - SentShortHeaderPacket: func(hdr *ShortHeader, size ByteCount, ecn ECN, ack *AckFrame, frames []Frame) { - for _, t := range tracers { - if t.SentShortHeaderPacket != nil { - t.SentShortHeaderPacket(hdr, size, ecn, ack, frames) - } - } - }, - ReceivedVersionNegotiationPacket: func(dest ArbitraryLenConnectionID, src ArbitraryLenConnectionID, versions []Version) { - for _, t := range tracers { - if t.ReceivedVersionNegotiationPacket != nil { - t.ReceivedVersionNegotiationPacket(dest, src, versions) - } - } - }, - ReceivedRetry: func(hdr *Header) { - for _, t := range tracers { - if t.ReceivedRetry != nil { - t.ReceivedRetry(hdr) - } - } - }, - ReceivedLongHeaderPacket: func(hdr *ExtendedHeader, size ByteCount, ecn ECN, frames []Frame) { - for _, t := range tracers { - if t.ReceivedLongHeaderPacket != nil { - t.ReceivedLongHeaderPacket(hdr, size, ecn, frames) - } - } - }, - ReceivedShortHeaderPacket: func(hdr *ShortHeader, size ByteCount, ecn ECN, frames []Frame) { - for _, t := range tracers { - if t.ReceivedShortHeaderPacket != nil { - t.ReceivedShortHeaderPacket(hdr, size, ecn, frames) - } - } - }, - BufferedPacket: func(packetType PacketType, size ByteCount) { - for _, t := range tracers { - if t.BufferedPacket != nil { - t.BufferedPacket(packetType, size) - } - } - }, - DroppedPacket: func(packetType PacketType, pn PacketNumber, size ByteCount, reason PacketDropReason) { - for _, t := range tracers { - if t.DroppedPacket != nil { - t.DroppedPacket(packetType, pn, size, reason) - } - } - }, - UpdatedMetrics: func(rttStats *RTTStats, cwnd ByteCount, bytesInFlight ByteCount, packetsInFlight int) { - for _, t := range tracers { - if t.UpdatedMetrics != nil { - t.UpdatedMetrics(rttStats, cwnd, bytesInFlight, packetsInFlight) - } - } - }, - AcknowledgedPacket: func(encLevel EncryptionLevel, pn PacketNumber) { - for _, t := range tracers { - if t.AcknowledgedPacket != nil { - t.AcknowledgedPacket(encLevel, pn) - } - } - }, - LostPacket: func(encLevel EncryptionLevel, pn PacketNumber, reason PacketLossReason) { - for _, t := range tracers { - if t.LostPacket != nil { - t.LostPacket(encLevel, pn, reason) - } - } - }, - UpdatedMTU: func(mtu ByteCount, done bool) { - for _, t := range tracers { - if t.UpdatedMTU != nil { - t.UpdatedMTU(mtu, done) - } - } - }, - UpdatedCongestionState: func(state CongestionState) { - for _, t := range tracers { - if t.UpdatedCongestionState != nil { - t.UpdatedCongestionState(state) - } - } - }, - UpdatedPTOCount: func(value uint32) { - for _, t := range tracers { - if t.UpdatedPTOCount != nil { - t.UpdatedPTOCount(value) - } - } - }, - UpdatedKeyFromTLS: func(encLevel EncryptionLevel, p Perspective) { - for _, t := range tracers { - if t.UpdatedKeyFromTLS != nil { - t.UpdatedKeyFromTLS(encLevel, p) - } - } - }, - UpdatedKey: func(keyPhase KeyPhase, remote bool) { - for _, t := range tracers { - if t.UpdatedKey != nil { - t.UpdatedKey(keyPhase, remote) - } - } - }, - DroppedEncryptionLevel: func(encLevel EncryptionLevel) { - for _, t := range tracers { - if t.DroppedEncryptionLevel != nil { - t.DroppedEncryptionLevel(encLevel) - } - } - }, - DroppedKey: func(keyPhase KeyPhase) { - for _, t := range tracers { - if t.DroppedKey != nil { - t.DroppedKey(keyPhase) - } - } - }, - SetLossTimer: func(timerType TimerType, encLevel EncryptionLevel, time time.Time) { - for _, t := range tracers { - if t.SetLossTimer != nil { - t.SetLossTimer(timerType, encLevel, time) - } - } - }, - LossTimerExpired: func(timerType TimerType, encLevel EncryptionLevel) { - for _, t := range tracers { - if t.LossTimerExpired != nil { - t.LossTimerExpired(timerType, encLevel) - } - } - }, - LossTimerCanceled: func() { - for _, t := range tracers { - if t.LossTimerCanceled != nil { - t.LossTimerCanceled() - } - } - }, - ECNStateUpdated: func(state ECNState, trigger ECNStateTrigger) { - for _, t := range tracers { - if t.ECNStateUpdated != nil { - t.ECNStateUpdated(state, trigger) - } - } - }, - ChoseALPN: func(protocol string) { - for _, t := range tracers { - if t.ChoseALPN != nil { - t.ChoseALPN(protocol) - } - } - }, - Close: func() { - for _, t := range tracers { - if t.Close != nil { - t.Close() - } - } - }, - Debug: func(name string, msg string) { - for _, t := range tracers { - if t.Debug != nil { - t.Debug(name, msg) - } - } - }, - } -} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/frame.go deleted file mode 100644 index 9a055db35..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/frame.go +++ /dev/null @@ -1,66 +0,0 @@ -package logging - -import "github.com/quic-go/quic-go/internal/wire" - -// A Frame is a QUIC frame -type Frame interface{} - -// The AckRange is used within the AckFrame. -// It is a range of packet numbers that is being acknowledged. -type AckRange = wire.AckRange - -type ( - // An AckFrame is an ACK frame. - AckFrame = wire.AckFrame - // A ConnectionCloseFrame is a CONNECTION_CLOSE frame. - ConnectionCloseFrame = wire.ConnectionCloseFrame - // A DataBlockedFrame is a DATA_BLOCKED frame. - DataBlockedFrame = wire.DataBlockedFrame - // A HandshakeDoneFrame is a HANDSHAKE_DONE frame. - HandshakeDoneFrame = wire.HandshakeDoneFrame - // A MaxDataFrame is a MAX_DATA frame. - MaxDataFrame = wire.MaxDataFrame - // A MaxStreamDataFrame is a MAX_STREAM_DATA frame. - MaxStreamDataFrame = wire.MaxStreamDataFrame - // A MaxStreamsFrame is a MAX_STREAMS_FRAME. - MaxStreamsFrame = wire.MaxStreamsFrame - // A NewConnectionIDFrame is a NEW_CONNECTION_ID frame. - NewConnectionIDFrame = wire.NewConnectionIDFrame - // A NewTokenFrame is a NEW_TOKEN frame. - NewTokenFrame = wire.NewTokenFrame - // A PathChallengeFrame is a PATH_CHALLENGE frame. - PathChallengeFrame = wire.PathChallengeFrame - // A PathResponseFrame is a PATH_RESPONSE frame. - PathResponseFrame = wire.PathResponseFrame - // A PingFrame is a PING frame. - PingFrame = wire.PingFrame - // A ResetStreamFrame is a RESET_STREAM frame. - ResetStreamFrame = wire.ResetStreamFrame - // A RetireConnectionIDFrame is a RETIRE_CONNECTION_ID frame. - RetireConnectionIDFrame = wire.RetireConnectionIDFrame - // A StopSendingFrame is a STOP_SENDING frame. - StopSendingFrame = wire.StopSendingFrame - // A StreamsBlockedFrame is a STREAMS_BLOCKED frame. - StreamsBlockedFrame = wire.StreamsBlockedFrame - // A StreamDataBlockedFrame is a STREAM_DATA_BLOCKED frame. - StreamDataBlockedFrame = wire.StreamDataBlockedFrame -) - -// A CryptoFrame is a CRYPTO frame. -type CryptoFrame struct { - Offset ByteCount - Length ByteCount -} - -// A StreamFrame is a STREAM frame. -type StreamFrame struct { - StreamID StreamID - Offset ByteCount - Length ByteCount - Fin bool -} - -// A DatagramFrame is a DATAGRAM frame. -type DatagramFrame struct { - Length ByteCount -} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/generate_multiplexer.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/generate_multiplexer.go deleted file mode 100644 index c152b846a..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/generate_multiplexer.go +++ /dev/null @@ -1,161 +0,0 @@ -//go:build generate - -package main - -import ( - "bytes" - "fmt" - "go/ast" - "go/parser" - "go/printer" - "go/token" - "log" - "os" - "strings" - "text/template" - - "golang.org/x/tools/imports" -) - -func main() { - if len(os.Args) != 5 { - log.Fatalf("Usage: %s ", os.Args[0]) - } - - structName := os.Args[1] - inputFile := os.Args[2] - templateFile := os.Args[3] - outputFile := os.Args[4] - - fset := token.NewFileSet() - - // Parse the input file containing the struct type - file, err := parser.ParseFile(fset, inputFile, nil, parser.AllErrors) - if err != nil { - log.Fatalf("Failed to parse file: %v", err) - } - - var fields []*ast.Field - - // Find the specified struct type in the AST - for _, decl := range file.Decls { - genDecl, ok := decl.(*ast.GenDecl) - if !ok || genDecl.Tok != token.TYPE { - continue - } - for _, spec := range genDecl.Specs { - typeSpec, ok := spec.(*ast.TypeSpec) - if !ok || typeSpec.Name.Name != structName { - continue - } - structType, ok := typeSpec.Type.(*ast.StructType) - if !ok { - log.Fatalf("%s is not a struct", structName) - } - fields = structType.Fields.List - break - } - } - - if fields == nil { - log.Fatalf("Could not find %s type", structName) - } - - // Prepare data for the template - type FieldData struct { - Name string - Params string - Args string - HasParams bool - ReturnTypes string - HasReturn bool - } - - var fieldDataList []FieldData - - for _, field := range fields { - funcType, ok := field.Type.(*ast.FuncType) - if !ok { - continue - } - for _, name := range field.Names { - fieldData := FieldData{Name: name.Name} - - // extract parameters - var params []string - var args []string - if funcType.Params != nil { - for i, param := range funcType.Params.List { - // We intentionally reject unnamed (and, further down, "_") function parameters. - // We could auto-generate parameter names, - // but having meaningful variable names will be more helpful for the user. - if len(param.Names) == 0 { - log.Fatalf("encountered unnamed parameter at position %d in function %s", i, fieldData.Name) - } - var buf bytes.Buffer - printer.Fprint(&buf, fset, param.Type) - paramType := buf.String() - for _, paramName := range param.Names { - if paramName.Name == "_" { - log.Fatalf("encountered underscore parameter at position %d in function %s", i, fieldData.Name) - } - params = append(params, fmt.Sprintf("%s %s", paramName.Name, paramType)) - args = append(args, paramName.Name) - } - } - } - fieldData.Params = strings.Join(params, ", ") - fieldData.Args = strings.Join(args, ", ") - fieldData.HasParams = len(params) > 0 - - // extract return types - if funcType.Results != nil && len(funcType.Results.List) > 0 { - fieldData.HasReturn = true - var returns []string - for _, result := range funcType.Results.List { - var buf bytes.Buffer - printer.Fprint(&buf, fset, result.Type) - returns = append(returns, buf.String()) - } - if len(returns) == 1 { - fieldData.ReturnTypes = fmt.Sprintf(" %s", returns[0]) - } else { - fieldData.ReturnTypes = fmt.Sprintf(" (%s)", strings.Join(returns, ", ")) - } - } - - fieldDataList = append(fieldDataList, fieldData) - } - } - - // Read the template from file - templateContent, err := os.ReadFile(templateFile) - if err != nil { - log.Fatalf("Failed to read template file: %v", err) - } - - // Generate the code using the template - tmpl, err := template.New("multiplexer").Funcs(template.FuncMap{"join": strings.Join}).Parse(string(templateContent)) - if err != nil { - log.Fatalf("Failed to parse template: %v", err) - } - - var generatedCode bytes.Buffer - generatedCode.WriteString("// Code generated by generate_multiplexer.go; DO NOT EDIT.\n\n") - if err = tmpl.Execute(&generatedCode, map[string]interface{}{ - "Fields": fieldDataList, - "StructName": structName, - }); err != nil { - log.Fatalf("Failed to execute template: %v", err) - } - - // Format the generated code and add imports - formattedCode, err := imports.Process(outputFile, generatedCode.Bytes(), nil) - if err != nil { - log.Fatalf("Failed to process imports: %v", err) - } - - if err := os.WriteFile(outputFile, formattedCode, 0o644); err != nil { - log.Fatalf("Failed to write output file: %v", err) - } -} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/interface.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/interface.go deleted file mode 100644 index 1f8edb92c..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/interface.go +++ /dev/null @@ -1,111 +0,0 @@ -// Package logging defines a logging interface for quic-go. -// This package should not be considered stable -package logging - -import ( - "github.com/quic-go/quic-go/internal/protocol" - "github.com/quic-go/quic-go/internal/qerr" - "github.com/quic-go/quic-go/internal/utils" - "github.com/quic-go/quic-go/internal/wire" -) - -type ( - // A ByteCount is used to count bytes. - ByteCount = protocol.ByteCount - // ECN is the ECN value - ECN = protocol.ECN - // A ConnectionID is a QUIC Connection ID. - ConnectionID = protocol.ConnectionID - // An ArbitraryLenConnectionID is a QUIC Connection ID that can be up to 255 bytes long. - ArbitraryLenConnectionID = protocol.ArbitraryLenConnectionID - // The EncryptionLevel is the encryption level of a packet. - EncryptionLevel = protocol.EncryptionLevel - // The KeyPhase is the key phase of the 1-RTT keys. - KeyPhase = protocol.KeyPhase - // The KeyPhaseBit is the value of the key phase bit of the 1-RTT packets. - KeyPhaseBit = protocol.KeyPhaseBit - // The PacketNumber is the packet number of a packet. - PacketNumber = protocol.PacketNumber - // The Perspective is the role of a QUIC endpoint (client or server). - Perspective = protocol.Perspective - // A StatelessResetToken is a stateless reset token. - StatelessResetToken = protocol.StatelessResetToken - // The StreamID is the stream ID. - StreamID = protocol.StreamID - // The StreamNum is the number of the stream. - StreamNum = protocol.StreamNum - // The StreamType is the type of the stream (unidirectional or bidirectional). - StreamType = protocol.StreamType - // The Version is the QUIC version. - Version = protocol.Version - - // The Header is the QUIC packet header, before removing header protection. - Header = wire.Header - // The ExtendedHeader is the QUIC Long Header packet header, after removing header protection. - ExtendedHeader = wire.ExtendedHeader - // The TransportParameters are QUIC transport parameters. - TransportParameters = wire.TransportParameters - // The PreferredAddress is the preferred address sent in the transport parameters. - PreferredAddress = wire.PreferredAddress - - // A TransportError is a transport-level error code. - TransportError = qerr.TransportErrorCode - // An ApplicationError is an application-defined error code. - ApplicationError = qerr.TransportErrorCode - - // The RTTStats contain statistics used by the congestion controller. - RTTStats = utils.RTTStats -) - -const ( - // ECNUnsupported means that no ECN value was set / received - ECNUnsupported = protocol.ECNUnsupported - // ECTNot is Not-ECT - ECTNot = protocol.ECNNon - // ECT0 is ECT(0) - ECT0 = protocol.ECT0 - // ECT1 is ECT(1) - ECT1 = protocol.ECT1 - // ECNCE is CE - ECNCE = protocol.ECNCE -) - -const ( - // KeyPhaseZero is key phase bit 0 - KeyPhaseZero = protocol.KeyPhaseZero - // KeyPhaseOne is key phase bit 1 - KeyPhaseOne = protocol.KeyPhaseOne -) - -const ( - // PerspectiveServer is used for a QUIC server - PerspectiveServer = protocol.PerspectiveServer - // PerspectiveClient is used for a QUIC client - PerspectiveClient = protocol.PerspectiveClient -) - -const ( - // EncryptionInitial is the Initial encryption level - EncryptionInitial = protocol.EncryptionInitial - // EncryptionHandshake is the Handshake encryption level - EncryptionHandshake = protocol.EncryptionHandshake - // Encryption1RTT is the 1-RTT encryption level - Encryption1RTT = protocol.Encryption1RTT - // Encryption0RTT is the 0-RTT encryption level - Encryption0RTT = protocol.Encryption0RTT -) - -const ( - // StreamTypeUni is a unidirectional stream - StreamTypeUni = protocol.StreamTypeUni - // StreamTypeBidi is a bidirectional stream - StreamTypeBidi = protocol.StreamTypeBidi -) - -// The ShortHeader is the QUIC Short Header packet header, after removing header protection. -type ShortHeader struct { - DestConnectionID ConnectionID - PacketNumber PacketNumber - PacketNumberLen protocol.PacketNumberLen - KeyPhase KeyPhaseBit -} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/multiplexer.tmpl b/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/multiplexer.tmpl deleted file mode 100644 index 9ba52e0ff..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/multiplexer.tmpl +++ /dev/null @@ -1,21 +0,0 @@ -package logging - -func NewMultiplexed{{ .StructName }} (tracers ...*{{ .StructName }}) *{{ .StructName }} { - if len(tracers) == 0 { - return nil - } - if len(tracers) == 1 { - return tracers[0] - } - return &{{ .StructName }}{ - {{- range .Fields }} - {{ .Name }}: func({{ .Params }}){{ .ReturnTypes }} { - for _, t := range tracers { - if t.{{ .Name }} != nil { - t.{{ .Name }}({{ .Args }}) - } - } - }, - {{- end }} - } -} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/packet_header.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/packet_header.go deleted file mode 100644 index 6b8df58d8..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/packet_header.go +++ /dev/null @@ -1,24 +0,0 @@ -package logging - -import ( - "github.com/quic-go/quic-go/internal/protocol" -) - -// PacketTypeFromHeader determines the packet type from a *wire.Header. -func PacketTypeFromHeader(hdr *Header) PacketType { - if hdr.Version == 0 { - return PacketTypeVersionNegotiation - } - switch hdr.Type { - case protocol.PacketTypeInitial: - return PacketTypeInitial - case protocol.PacketTypeHandshake: - return PacketTypeHandshake - case protocol.PacketType0RTT: - return PacketType0RTT - case protocol.PacketTypeRetry: - return PacketTypeRetry - default: - return PacketTypeNotDetermined - } -} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/tracer.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/tracer.go deleted file mode 100644 index 4fe014627..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/tracer.go +++ /dev/null @@ -1,14 +0,0 @@ -package logging - -import "net" - -//go:generate go run generate_multiplexer.go Tracer tracer.go multiplexer.tmpl tracer_multiplexer.go - -// A Tracer traces events. -type Tracer struct { - SentPacket func(dest net.Addr, hdr *Header, size ByteCount, frames []Frame) - SentVersionNegotiationPacket func(dest net.Addr, destConnID, srcConnID ArbitraryLenConnectionID, versions []Version) - DroppedPacket func(addr net.Addr, packetType PacketType, size ByteCount, reason PacketDropReason) - Debug func(name, msg string) - Close func() -} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/tracer_multiplexer.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/tracer_multiplexer.go deleted file mode 100644 index f0878cfe7..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/tracer_multiplexer.go +++ /dev/null @@ -1,51 +0,0 @@ -// Code generated by generate_multiplexer.go; DO NOT EDIT. - -package logging - -import "net" - -func NewMultiplexedTracer(tracers ...*Tracer) *Tracer { - if len(tracers) == 0 { - return nil - } - if len(tracers) == 1 { - return tracers[0] - } - return &Tracer{ - SentPacket: func(dest net.Addr, hdr *Header, size ByteCount, frames []Frame) { - for _, t := range tracers { - if t.SentPacket != nil { - t.SentPacket(dest, hdr, size, frames) - } - } - }, - SentVersionNegotiationPacket: func(dest net.Addr, destConnID ArbitraryLenConnectionID, srcConnID ArbitraryLenConnectionID, versions []Version) { - for _, t := range tracers { - if t.SentVersionNegotiationPacket != nil { - t.SentVersionNegotiationPacket(dest, destConnID, srcConnID, versions) - } - } - }, - DroppedPacket: func(addr net.Addr, packetType PacketType, size ByteCount, reason PacketDropReason) { - for _, t := range tracers { - if t.DroppedPacket != nil { - t.DroppedPacket(addr, packetType, size, reason) - } - } - }, - Debug: func(name string, msg string) { - for _, t := range tracers { - if t.Debug != nil { - t.Debug(name, msg) - } - } - }, - Close: func() { - for _, t := range tracers { - if t.Close != nil { - t.Close() - } - } - }, - } -} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/types.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/types.go deleted file mode 100644 index 65da35595..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/logging/types.go +++ /dev/null @@ -1,130 +0,0 @@ -package logging - -// PacketType is the packet type of a QUIC packet -type PacketType uint8 - -const ( - // PacketTypeInitial is the packet type of an Initial packet - PacketTypeInitial PacketType = iota - // PacketTypeHandshake is the packet type of a Handshake packet - PacketTypeHandshake - // PacketTypeRetry is the packet type of a Retry packet - PacketTypeRetry - // PacketType0RTT is the packet type of a 0-RTT packet - PacketType0RTT - // PacketTypeVersionNegotiation is the packet type of a Version Negotiation packet - PacketTypeVersionNegotiation - // PacketType1RTT is a 1-RTT packet - PacketType1RTT - // PacketTypeStatelessReset is a stateless reset - PacketTypeStatelessReset - // PacketTypeNotDetermined is the packet type when it could not be determined - PacketTypeNotDetermined -) - -type PacketLossReason uint8 - -const ( - // PacketLossReorderingThreshold: when a packet is deemed lost due to reordering threshold - PacketLossReorderingThreshold PacketLossReason = iota - // PacketLossTimeThreshold: when a packet is deemed lost due to time threshold - PacketLossTimeThreshold -) - -type PacketDropReason uint8 - -const ( - // PacketDropKeyUnavailable is used when a packet is dropped because keys are unavailable - PacketDropKeyUnavailable PacketDropReason = iota - // PacketDropUnknownConnectionID is used when a packet is dropped because the connection ID is unknown - PacketDropUnknownConnectionID - // PacketDropHeaderParseError is used when a packet is dropped because header parsing failed - PacketDropHeaderParseError - // PacketDropPayloadDecryptError is used when a packet is dropped because decrypting the payload failed - PacketDropPayloadDecryptError - // PacketDropProtocolViolation is used when a packet is dropped due to a protocol violation - PacketDropProtocolViolation - // PacketDropDOSPrevention is used when a packet is dropped to mitigate a DoS attack - PacketDropDOSPrevention - // PacketDropUnsupportedVersion is used when a packet is dropped because the version is not supported - PacketDropUnsupportedVersion - // PacketDropUnexpectedPacket is used when an unexpected packet is received - PacketDropUnexpectedPacket - // PacketDropUnexpectedSourceConnectionID is used when a packet with an unexpected source connection ID is received - PacketDropUnexpectedSourceConnectionID - // PacketDropUnexpectedVersion is used when a packet with an unexpected version is received - PacketDropUnexpectedVersion - // PacketDropDuplicate is used when a duplicate packet is received - PacketDropDuplicate -) - -// TimerType is the type of the loss detection timer -type TimerType uint8 - -const ( - // TimerTypeACK is the timer type for the early retransmit timer - TimerTypeACK TimerType = iota + 1 - // TimerTypePTO is the timer type for the PTO retransmit timer - TimerTypePTO - // TimerTypePathProbe is the timer type for the path probe retransmit timer - TimerTypePathProbe -) - -// TimeoutReason is the reason why a connection is closed -type TimeoutReason uint8 - -const ( - // TimeoutReasonHandshake is used when the connection is closed due to a handshake timeout - // This reason is not defined in the qlog draft, but very useful for debugging. - TimeoutReasonHandshake TimeoutReason = iota - // TimeoutReasonIdle is used when the connection is closed due to an idle timeout - // This reason is not defined in the qlog draft, but very useful for debugging. - TimeoutReasonIdle -) - -type CongestionState uint8 - -const ( - // CongestionStateSlowStart is the slow start phase of Reno / Cubic - CongestionStateSlowStart CongestionState = iota - // CongestionStateCongestionAvoidance is the slow start phase of Reno / Cubic - CongestionStateCongestionAvoidance - // CongestionStateRecovery is the recovery phase of Reno / Cubic - CongestionStateRecovery - // CongestionStateApplicationLimited means that the congestion controller is application limited - CongestionStateApplicationLimited -) - -// ECNState is the state of the ECN state machine (see Appendix A.4 of RFC 9000) -type ECNState uint8 - -const ( - // ECNStateTesting is the testing state - ECNStateTesting ECNState = 1 + iota - // ECNStateUnknown is the unknown state - ECNStateUnknown - // ECNStateFailed is the failed state - ECNStateFailed - // ECNStateCapable is the capable state - ECNStateCapable -) - -// ECNStateTrigger is a trigger for an ECN state transition. -type ECNStateTrigger uint8 - -const ( - ECNTriggerNoTrigger ECNStateTrigger = iota - // ECNFailedNoECNCounts is emitted when an ACK acknowledges ECN-marked packets, - // but doesn't contain any ECN counts - ECNFailedNoECNCounts - // ECNFailedDecreasedECNCounts is emitted when an ACK frame decreases ECN counts - ECNFailedDecreasedECNCounts - // ECNFailedLostAllTestingPackets is emitted when all ECN testing packets are declared lost - ECNFailedLostAllTestingPackets - // ECNFailedMoreECNCountsThanSent is emitted when an ACK contains more ECN counts than ECN-marked packets were sent - ECNFailedMoreECNCountsThanSent - // ECNFailedTooFewECNCounts is emitted when an ACK contains fewer ECN counts than it acknowledges packets - ECNFailedTooFewECNCounts - // ECNFailedManglingDetected is emitted when the path marks all ECN-marked packets as CE - ECNFailedManglingDetected -) diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/mockgen.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/mockgen.go index 1a8b28db8..65160d0d2 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/mockgen.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/mockgen.go @@ -2,63 +2,46 @@ package quic -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_send_conn_test.go github.com/quic-go/quic-go SendConn" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_send_conn_test.go github.com/quic-go/quic-go SendConn" type SendConn = sendConn -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_raw_conn_test.go github.com/quic-go/quic-go RawConn" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_raw_conn_test.go github.com/quic-go/quic-go RawConn" type RawConn = rawConn -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_sender_test.go github.com/quic-go/quic-go Sender" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_sender_test.go github.com/quic-go/quic-go Sender" type Sender = sender -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_internal_test.go github.com/quic-go/quic-go StreamI" -type StreamI = streamI - -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_receive_stream_internal_test.go github.com/quic-go/quic-go ReceiveStreamI" -type ReceiveStreamI = receiveStreamI - -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_send_stream_internal_test.go github.com/quic-go/quic-go SendStreamI" -type SendStreamI = sendStreamI - -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_sender_test.go github.com/quic-go/quic-go StreamSender" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_sender_test.go github.com/quic-go/quic-go StreamSender" type StreamSender = streamSender -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_control_frame_getter_test.go github.com/quic-go/quic-go StreamControlFrameGetter" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_control_frame_getter_test.go github.com/quic-go/quic-go StreamControlFrameGetter" type StreamControlFrameGetter = streamControlFrameGetter -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_frame_source_test.go github.com/quic-go/quic-go FrameSource" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_frame_getter_test.go github.com/quic-go/quic-go StreamFrameGetter" +type StreamFrameGetter = streamFrameGetter + +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_frame_source_test.go github.com/quic-go/quic-go FrameSource" type FrameSource = frameSource -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_ack_frame_source_test.go github.com/quic-go/quic-go AckFrameSource" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_ack_frame_source_test.go github.com/quic-go/quic-go AckFrameSource" type AckFrameSource = ackFrameSource -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_manager_test.go github.com/quic-go/quic-go StreamManager" -type StreamManager = streamManager - -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_sealing_manager_test.go github.com/quic-go/quic-go SealingManager" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_sealing_manager_test.go github.com/quic-go/quic-go SealingManager" type SealingManager = sealingManager -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_unpacker_test.go github.com/quic-go/quic-go Unpacker" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_unpacker_test.go github.com/quic-go/quic-go Unpacker" type Unpacker = unpacker -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packer_test.go github.com/quic-go/quic-go Packer" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packer_test.go github.com/quic-go/quic-go Packer" type Packer = packer -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_mtu_discoverer_test.go github.com/quic-go/quic-go MTUDiscoverer" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_mtu_discoverer_test.go github.com/quic-go/quic-go MTUDiscoverer" type MTUDiscoverer = mtuDiscoverer -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_conn_runner_test.go github.com/quic-go/quic-go ConnRunner" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_conn_runner_test.go github.com/quic-go/quic-go ConnRunner" type ConnRunner = connRunner -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_quic_conn_test.go github.com/quic-go/quic-go QUICConn" -type QUICConn = quicConn - -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_test.go github.com/quic-go/quic-go PacketHandler" +//go:generate sh -c "go tool mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_test.go github.com/quic-go/quic-go PacketHandler" type PacketHandler = packetHandler -//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_manager_test.go github.com/quic-go/quic-go PacketHandlerManager" - -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_manager_test.go github.com/quic-go/quic-go PacketHandlerManager" -type PacketHandlerManager = packetHandlerManager - -//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -package quic -self_package github.com/quic-go/quic-go -self_package github.com/quic-go/quic-go -destination mock_packetconn_test.go net PacketConn" +//go:generate sh -c "go tool mockgen -typed -package quic -self_package github.com/quic-go/quic-go -self_package github.com/quic-go/quic-go -destination mock_packetconn_test.go net PacketConn" diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/mtu_discoverer.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/mtu_discoverer.go index 096eba146..950757f0c 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/mtu_discoverer.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/mtu_discoverer.go @@ -1,23 +1,23 @@ package quic import ( - "time" - "github.com/quic-go/quic-go/internal/ackhandler" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" - "github.com/quic-go/quic-go/logging" + "github.com/quic-go/quic-go/qlog" + "github.com/quic-go/quic-go/qlogwriter" ) type mtuDiscoverer interface { // Start starts the MTU discovery process. // It's unnecessary to call ShouldSendProbe before that. - Start(now time.Time) - ShouldSendProbe(now time.Time) bool + Start(now monotime.Time) + ShouldSendProbe(now monotime.Time) bool CurrentSize() protocol.ByteCount - GetPing(now time.Time) (ping ackhandler.Frame, datagramSize protocol.ByteCount) - Reset(now time.Time, start, max protocol.ByteCount) + GetPing(now monotime.Time) (ping ackhandler.Frame, datagramSize protocol.ByteCount) + Reset(now monotime.Time, start, max protocol.ByteCount) } const ( @@ -88,7 +88,7 @@ const ( // MTU discovery concludes once the interval min and max has been narrowed down to maxMTUDiff. type mtuFinder struct { - lastProbeTime time.Time + lastProbeTime monotime.Time rttStats *utils.RTTStats @@ -104,7 +104,7 @@ type mtuFinder struct { // We're therefore not concerned about overflows of this counter. generation uint8 - tracer *logging.ConnectionTracer + qlogger qlogwriter.Recorder } var _ mtuDiscoverer = &mtuFinder{} @@ -112,12 +112,12 @@ var _ mtuDiscoverer = &mtuFinder{} func newMTUDiscoverer( rttStats *utils.RTTStats, start, max protocol.ByteCount, - tracer *logging.ConnectionTracer, + qlogger qlogwriter.Recorder, ) *mtuFinder { f := &mtuFinder{ inFlight: protocol.InvalidByteCount, rttStats: rttStats, - tracer: tracer, + qlogger: qlogger, } f.init(start, max) return f @@ -147,11 +147,11 @@ func (f *mtuFinder) max() protocol.ByteCount { return f.lost[len(f.lost)-1] } -func (f *mtuFinder) Start(now time.Time) { +func (f *mtuFinder) Start(now monotime.Time) { f.lastProbeTime = now // makes sure the first probe packet is not sent immediately } -func (f *mtuFinder) ShouldSendProbe(now time.Time) bool { +func (f *mtuFinder) ShouldSendProbe(now monotime.Time) bool { if f.lastProbeTime.IsZero() { return false } @@ -161,7 +161,7 @@ func (f *mtuFinder) ShouldSendProbe(now time.Time) bool { return !now.Before(f.lastProbeTime.Add(mtuProbeDelay * f.rttStats.SmoothedRTT())) } -func (f *mtuFinder) GetPing(now time.Time) (ackhandler.Frame, protocol.ByteCount) { +func (f *mtuFinder) GetPing(now monotime.Time) (ackhandler.Frame, protocol.ByteCount) { var size protocol.ByteCount if f.lastProbeWasLost { size = (f.min + f.lost[0]) / 2 @@ -180,7 +180,7 @@ func (f *mtuFinder) CurrentSize() protocol.ByteCount { return f.min } -func (f *mtuFinder) Reset(now time.Time, start, max protocol.ByteCount) { +func (f *mtuFinder) Reset(now monotime.Time, start, max protocol.ByteCount) { f.generation++ f.lastProbeTime = now f.lastProbeWasLost = false @@ -224,8 +224,11 @@ func (h *mtuFinderAckHandler) OnAcked(wire.Frame) { } } } - if h.tracer != nil && h.tracer.UpdatedMTU != nil { - h.tracer.UpdatedMTU(size, h.done()) + if h.qlogger != nil { + h.qlogger.RecordEvent(qlog.MTUUpdated{ + Value: int(size), + Done: h.done(), + }) } } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/oss-fuzz.sh b/plugins/traefik/vendor/github.com/quic-go/quic-go/oss-fuzz.sh index 92a57a2cc..3884a10f7 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/oss-fuzz.sh +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/oss-fuzz.sh @@ -3,12 +3,12 @@ # Install Go manually, since oss-fuzz ships with an outdated Go version. # See https://github.com/google/oss-fuzz/pull/10643. export CXX="${CXX} -lresolv" # required by Go 1.20 -wget https://go.dev/dl/go1.23.0.linux-amd64.tar.gz \ +wget https://go.dev/dl/go1.25.0.linux-amd64.tar.gz \ && mkdir temp-go \ && rm -rf /root/.go/* \ - && tar -C temp-go/ -xzf go1.23.0.linux-amd64.tar.gz \ + && tar -C temp-go/ -xzf go1.25.0.linux-amd64.tar.gz \ && mv temp-go/go/* /root/.go/ \ - && rm -rf temp-go go1.23.0.linux-amd64.tar.gz + && rm -rf temp-go go1.25.0.linux-amd64.tar.gz ( # fuzz qpack diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/packet_handler_map.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/packet_handler_map.go deleted file mode 100644 index 848419848..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/packet_handler_map.go +++ /dev/null @@ -1,228 +0,0 @@ -package quic - -import ( - "io" - "net" - "sync" - "time" - - "github.com/quic-go/quic-go/internal/protocol" - "github.com/quic-go/quic-go/internal/utils" -) - -type connCapabilities struct { - // This connection has the Don't Fragment (DF) bit set. - // This means it makes to run DPLPMTUD. - DF bool - // GSO (Generic Segmentation Offload) supported - GSO bool - // ECN (Explicit Congestion Notifications) supported - ECN bool -} - -// rawConn is a connection that allow reading of a receivedPackeh. -type rawConn interface { - ReadPacket() (receivedPacket, error) - // WritePacket writes a packet on the wire. - // gsoSize is the size of a single packet, or 0 to disable GSO. - // It is invalid to set gsoSize if capabilities.GSO is not set. - WritePacket(b []byte, addr net.Addr, packetInfoOOB []byte, gsoSize uint16, ecn protocol.ECN) (int, error) - LocalAddr() net.Addr - SetReadDeadline(time.Time) error - io.Closer - - capabilities() connCapabilities -} - -type closePacket struct { - payload []byte - addr net.Addr - info packetInfo -} - -type packetHandlerMap struct { - mutex sync.Mutex - handlers map[protocol.ConnectionID]packetHandler - resetTokens map[protocol.StatelessResetToken] /* stateless reset token */ packetHandler - - closed bool - closeChan chan struct{} - - enqueueClosePacket func(closePacket) - - deleteRetiredConnsAfter time.Duration - - logger utils.Logger -} - -var _ packetHandlerManager = &packetHandlerMap{} - -func newPacketHandlerMap(enqueueClosePacket func(closePacket), logger utils.Logger) *packetHandlerMap { - h := &packetHandlerMap{ - closeChan: make(chan struct{}), - handlers: make(map[protocol.ConnectionID]packetHandler), - resetTokens: make(map[protocol.StatelessResetToken]packetHandler), - deleteRetiredConnsAfter: protocol.RetiredConnectionIDDeleteTimeout, - enqueueClosePacket: enqueueClosePacket, - logger: logger, - } - if h.logger.Debug() { - go h.logUsage() - } - return h -} - -func (h *packetHandlerMap) logUsage() { - ticker := time.NewTicker(2 * time.Second) - var printedZero bool - for { - select { - case <-h.closeChan: - return - case <-ticker.C: - } - - h.mutex.Lock() - numHandlers := len(h.handlers) - numTokens := len(h.resetTokens) - h.mutex.Unlock() - // If the number tracked handlers and tokens is zero, only print it a single time. - hasZero := numHandlers == 0 && numTokens == 0 - if !hasZero || (hasZero && !printedZero) { - h.logger.Debugf("Tracking %d connection IDs and %d reset tokens.\n", numHandlers, numTokens) - printedZero = false - if hasZero { - printedZero = true - } - } - } -} - -func (h *packetHandlerMap) Get(id protocol.ConnectionID) (packetHandler, bool) { - h.mutex.Lock() - defer h.mutex.Unlock() - - handler, ok := h.handlers[id] - return handler, ok -} - -func (h *packetHandlerMap) Add(id protocol.ConnectionID, handler packetHandler) bool /* was added */ { - h.mutex.Lock() - defer h.mutex.Unlock() - - if _, ok := h.handlers[id]; ok { - h.logger.Debugf("Not adding connection ID %s, as it already exists.", id) - return false - } - h.handlers[id] = handler - h.logger.Debugf("Adding connection ID %s.", id) - return true -} - -func (h *packetHandlerMap) AddWithConnID(clientDestConnID, newConnID protocol.ConnectionID, handler packetHandler) bool { - h.mutex.Lock() - defer h.mutex.Unlock() - - if _, ok := h.handlers[clientDestConnID]; ok { - h.logger.Debugf("Not adding connection ID %s for a new connection, as it already exists.", clientDestConnID) - return false - } - h.handlers[clientDestConnID] = handler - h.handlers[newConnID] = handler - h.logger.Debugf("Adding connection IDs %s and %s for a new connection.", clientDestConnID, newConnID) - return true -} - -func (h *packetHandlerMap) Remove(id protocol.ConnectionID) { - h.mutex.Lock() - delete(h.handlers, id) - h.mutex.Unlock() - h.logger.Debugf("Removing connection ID %s.", id) -} - -func (h *packetHandlerMap) Retire(id protocol.ConnectionID) { - h.logger.Debugf("Retiring connection ID %s in %s.", id, h.deleteRetiredConnsAfter) - time.AfterFunc(h.deleteRetiredConnsAfter, func() { - h.mutex.Lock() - delete(h.handlers, id) - h.mutex.Unlock() - h.logger.Debugf("Removing connection ID %s after it has been retired.", id) - }) -} - -// ReplaceWithClosed is called when a connection is closed. -// Depending on which side closed the connection, we need to: -// * remote close: absorb delayed packets -// * local close: retransmit the CONNECTION_CLOSE packet, in case it was lost -func (h *packetHandlerMap) ReplaceWithClosed(ids []protocol.ConnectionID, connClosePacket []byte) { - var handler packetHandler - if connClosePacket != nil { - handler = newClosedLocalConn( - func(addr net.Addr, info packetInfo) { - h.enqueueClosePacket(closePacket{payload: connClosePacket, addr: addr, info: info}) - }, - h.logger, - ) - } else { - handler = newClosedRemoteConn() - } - - h.mutex.Lock() - for _, id := range ids { - h.handlers[id] = handler - } - h.mutex.Unlock() - h.logger.Debugf("Replacing connection for connection IDs %s with a closed connection.", ids) - - time.AfterFunc(h.deleteRetiredConnsAfter, func() { - h.mutex.Lock() - for _, id := range ids { - delete(h.handlers, id) - } - h.mutex.Unlock() - h.logger.Debugf("Removing connection IDs %s for a closed connection after it has been retired.", ids) - }) -} - -func (h *packetHandlerMap) AddResetToken(token protocol.StatelessResetToken, handler packetHandler) { - h.mutex.Lock() - h.resetTokens[token] = handler - h.mutex.Unlock() -} - -func (h *packetHandlerMap) RemoveResetToken(token protocol.StatelessResetToken) { - h.mutex.Lock() - delete(h.resetTokens, token) - h.mutex.Unlock() -} - -func (h *packetHandlerMap) GetByResetToken(token protocol.StatelessResetToken) (packetHandler, bool) { - h.mutex.Lock() - defer h.mutex.Unlock() - - handler, ok := h.resetTokens[token] - return handler, ok -} - -func (h *packetHandlerMap) Close(e error) { - h.mutex.Lock() - - if h.closed { - h.mutex.Unlock() - return - } - - close(h.closeChan) - - var wg sync.WaitGroup - for _, handler := range h.handlers { - wg.Add(1) - go func(handler packetHandler) { - handler.destroy(e) - wg.Done() - }(handler) - } - h.closed = true - h.mutex.Unlock() - wg.Wait() -} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/packet_packer.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/packet_packer.go index 720f19583..e3933da6b 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/packet_packer.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/packet_packer.go @@ -5,12 +5,11 @@ import ( "encoding/binary" "errors" "fmt" - "time" - - "golang.org/x/exp/rand" + "math/rand/v2" "github.com/quic-go/quic-go/internal/ackhandler" "github.com/quic-go/quic-go/internal/handshake" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/wire" @@ -19,13 +18,13 @@ import ( var errNothingToPack = errors.New("nothing to pack") type packer interface { - PackCoalescedPacket(onlyAck bool, maxPacketSize protocol.ByteCount, now time.Time, v protocol.Version) (*coalescedPacket, error) - PackAckOnlyPacket(maxPacketSize protocol.ByteCount, now time.Time, v protocol.Version) (shortHeaderPacket, *packetBuffer, error) - AppendPacket(buf *packetBuffer, maxPacketSize protocol.ByteCount, now time.Time, v protocol.Version) (shortHeaderPacket, error) - MaybePackPTOProbePacket(protocol.EncryptionLevel, protocol.ByteCount, time.Time, protocol.Version) (*coalescedPacket, error) + PackCoalescedPacket(onlyAck bool, maxPacketSize protocol.ByteCount, now monotime.Time, v protocol.Version) (*coalescedPacket, error) + PackAckOnlyPacket(maxPacketSize protocol.ByteCount, now monotime.Time, v protocol.Version) (shortHeaderPacket, *packetBuffer, error) + AppendPacket(_ *packetBuffer, maxPacketSize protocol.ByteCount, now monotime.Time, v protocol.Version) (shortHeaderPacket, error) + PackPTOProbePacket(_ protocol.EncryptionLevel, _ protocol.ByteCount, addPingIfEmpty bool, now monotime.Time, v protocol.Version) (*coalescedPacket, error) PackConnectionClose(*qerr.TransportError, protocol.ByteCount, protocol.Version) (*coalescedPacket, error) PackApplicationClose(*qerr.ApplicationError, protocol.ByteCount, protocol.Version) (*coalescedPacket, error) - PackPathProbePacket(protocol.ConnectionID, ackhandler.Frame, protocol.Version) (shortHeaderPacket, *packetBuffer, error) + PackPathProbePacket(protocol.ConnectionID, []ackhandler.Frame, protocol.Version) (shortHeaderPacket, *packetBuffer, error) PackMTUProbePacket(ping ackhandler.Frame, size protocol.ByteCount, v protocol.Version) (shortHeaderPacket, *packetBuffer, error) SetToken([]byte) @@ -109,11 +108,11 @@ type sealingManager interface { type frameSource interface { HasData() bool - Append([]ackhandler.Frame, []ackhandler.StreamFrame, protocol.ByteCount, time.Time, protocol.Version) ([]ackhandler.Frame, []ackhandler.StreamFrame, protocol.ByteCount) + Append([]ackhandler.Frame, []ackhandler.StreamFrame, protocol.ByteCount, monotime.Time, protocol.Version) ([]ackhandler.Frame, []ackhandler.StreamFrame, protocol.ByteCount) } type ackFrameSource interface { - GetAckFrame(_ protocol.EncryptionLevel, now time.Time, onlyIfQueued bool) *wire.AckFrame + GetAckFrame(_ protocol.EncryptionLevel, now monotime.Time, onlyIfQueued bool) *wire.AckFrame } type packetPacker struct { @@ -123,7 +122,7 @@ type packetPacker struct { perspective protocol.Perspective cryptoSetup sealingManager - initialStream *cryptoStream + initialStream *initialCryptoStream handshakeStream *cryptoStream token []byte @@ -143,7 +142,8 @@ var _ packer = &packetPacker{} func newPacketPacker( srcConnID protocol.ConnectionID, getDestConnID func() protocol.ConnectionID, - initialStream, handshakeStream *cryptoStream, + initialStream *initialCryptoStream, + handshakeStream *cryptoStream, packetNumberManager packetNumberManager, retransmissionQueue *retransmissionQueue, cryptoSetup sealingManager, @@ -152,7 +152,7 @@ func newPacketPacker( datagramQueue *datagramQueue, perspective protocol.Perspective, ) *packetPacker { - var b [8]byte + var b [16]byte _, _ = crand.Read(b[:]) return &packetPacker{ @@ -166,7 +166,7 @@ func newPacketPacker( perspective: perspective, framer: framer, acks: acks, - rand: *rand.New(rand.NewSource(binary.BigEndian.Uint64(b[:]))), + rand: *rand.New(rand.NewPCG(binary.BigEndian.Uint64(b[:8]), binary.BigEndian.Uint64(b[8:]))), pnManager: packetNumberManager, } } @@ -330,7 +330,7 @@ func (p *packetPacker) initialPaddingLen(frames []ackhandler.Frame, currentSize, // PackCoalescedPacket packs a new packet. // It packs an Initial / Handshake if there is data to send in these packet number spaces. // It should only be called before the handshake is confirmed. -func (p *packetPacker) PackCoalescedPacket(onlyAck bool, maxSize protocol.ByteCount, now time.Time, v protocol.Version) (*coalescedPacket, error) { +func (p *packetPacker) PackCoalescedPacket(onlyAck bool, maxSize protocol.ByteCount, now monotime.Time, v protocol.Version) (*coalescedPacket, error) { var ( initialHdr, handshakeHdr, zeroRTTHdr *wire.ExtendedHeader initialPayload, handshakePayload, zeroRTTPayload, oneRTTPayload payload @@ -348,6 +348,7 @@ func (p *packetPacker) PackCoalescedPacket(onlyAck bool, maxSize protocol.ByteCo maxSize-protocol.ByteCount(initialSealer.Overhead()), protocol.EncryptionInitial, now, + false, onlyAck, true, v, @@ -370,6 +371,7 @@ func (p *packetPacker) PackCoalescedPacket(onlyAck bool, maxSize protocol.ByteCo maxSize-size-protocol.ByteCount(handshakeSealer.Overhead()), protocol.EncryptionHandshake, now, + false, onlyAck, size == 0, v, @@ -458,7 +460,7 @@ func (p *packetPacker) PackCoalescedPacket(onlyAck bool, maxSize protocol.ByteCo // PackAckOnlyPacket packs a packet containing only an ACK in the application data packet number space. // It should be called after the handshake is confirmed. -func (p *packetPacker) PackAckOnlyPacket(maxSize protocol.ByteCount, now time.Time, v protocol.Version) (shortHeaderPacket, *packetBuffer, error) { +func (p *packetPacker) PackAckOnlyPacket(maxSize protocol.ByteCount, now monotime.Time, v protocol.Version) (shortHeaderPacket, *packetBuffer, error) { buf := getPacketBuffer() packet, err := p.appendPacket(buf, true, maxSize, now, v) return packet, buf, err @@ -466,7 +468,7 @@ func (p *packetPacker) PackAckOnlyPacket(maxSize protocol.ByteCount, now time.Ti // AppendPacket packs a packet in the application data packet number space. // It should be called after the handshake is confirmed. -func (p *packetPacker) AppendPacket(buf *packetBuffer, maxSize protocol.ByteCount, now time.Time, v protocol.Version) (shortHeaderPacket, error) { +func (p *packetPacker) AppendPacket(buf *packetBuffer, maxSize protocol.ByteCount, now monotime.Time, v protocol.Version) (shortHeaderPacket, error) { return p.appendPacket(buf, false, maxSize, now, v) } @@ -474,7 +476,7 @@ func (p *packetPacker) appendPacket( buf *packetBuffer, onlyAck bool, maxPacketSize protocol.ByteCount, - now time.Time, + now monotime.Time, v protocol.Version, ) (shortHeaderPacket, error) { sealer, err := p.cryptoSetup.Get1RTTSealer() @@ -496,7 +498,8 @@ func (p *packetPacker) appendPacket( func (p *packetPacker) maybeGetCryptoPacket( maxPacketSize protocol.ByteCount, encLevel protocol.EncryptionLevel, - now time.Time, + now monotime.Time, + addPingIfEmpty bool, onlyAck, ackAllowed bool, v protocol.Version, ) (*wire.ExtendedHeader, payload) { @@ -510,32 +513,35 @@ func (p *packetPacker) maybeGetCryptoPacket( return nil, payload{} } - var s *cryptoStream - var handler ackhandler.FrameHandler - var hasRetransmission bool + var hasCryptoData func() bool + var popCryptoFrame func(maxLen protocol.ByteCount) *wire.CryptoFrame //nolint:exhaustive // Initial and Handshake are the only two encryption levels here. switch encLevel { case protocol.EncryptionInitial: - s = p.initialStream - handler = p.retransmissionQueue.InitialAckHandler() - hasRetransmission = p.retransmissionQueue.HasInitialData() + hasCryptoData = p.initialStream.HasData + popCryptoFrame = p.initialStream.PopCryptoFrame case protocol.EncryptionHandshake: - s = p.handshakeStream - handler = p.retransmissionQueue.HandshakeAckHandler() - hasRetransmission = p.retransmissionQueue.HasHandshakeData() + hasCryptoData = p.handshakeStream.HasData + popCryptoFrame = p.handshakeStream.PopCryptoFrame } + handler := p.retransmissionQueue.AckHandler(encLevel) + hasRetransmission := p.retransmissionQueue.HasData(encLevel) - hasData := s.HasData() var ack *wire.AckFrame if ackAllowed { - ack = p.acks.GetAckFrame(encLevel, now, !hasRetransmission && !hasData) + ack = p.acks.GetAckFrame(encLevel, now, !hasRetransmission && !hasCryptoData()) } - if !hasData && !hasRetransmission && ack == nil { - // nothing to send - return nil, payload{} + var pl payload + if !hasCryptoData() && !hasRetransmission && ack == nil { + if !addPingIfEmpty { + // nothing to send + return nil, payload{} + } + ping := &wire.PingFrame{} + pl.frames = append(pl.frames, ackhandler.Frame{Frame: ping, Handler: emptyHandler{}}) + pl.length += ping.Length(v) } - var pl payload if ack != nil { pl.ack = ack pl.length = ack.Length(v) @@ -545,44 +551,40 @@ func (p *packetPacker) maybeGetCryptoPacket( maxPacketSize -= hdr.GetLength(v) if hasRetransmission { for { - var f ackhandler.Frame - //nolint:exhaustive // 0-RTT packets can't contain any retransmissions - switch encLevel { - case protocol.EncryptionInitial: - f.Frame = p.retransmissionQueue.GetInitialFrame(maxPacketSize, v) - f.Handler = p.retransmissionQueue.InitialAckHandler() - case protocol.EncryptionHandshake: - f.Frame = p.retransmissionQueue.GetHandshakeFrame(maxPacketSize, v) - f.Handler = p.retransmissionQueue.HandshakeAckHandler() - } - if f.Frame == nil { + frame := p.retransmissionQueue.GetFrame(encLevel, maxPacketSize, v) + if frame == nil { break } - pl.frames = append(pl.frames, f) - frameLen := f.Frame.Length(v) + pl.frames = append(pl.frames, ackhandler.Frame{ + Frame: frame, + Handler: p.retransmissionQueue.AckHandler(encLevel), + }) + frameLen := frame.Length(v) pl.length += frameLen maxPacketSize -= frameLen } - } else if s.HasData() { - cf := s.PopCryptoFrame(maxPacketSize) - pl.frames = []ackhandler.Frame{{Frame: cf, Handler: handler}} - pl.length += cf.Length(v) + return hdr, pl + } else { + for hasCryptoData() { + cf := popCryptoFrame(maxPacketSize) + if cf == nil { + break + } + pl.frames = append(pl.frames, ackhandler.Frame{Frame: cf, Handler: handler}) + pl.length += cf.Length(v) + maxPacketSize -= cf.Length(v) + } } return hdr, pl } -func (p *packetPacker) maybeGetAppDataPacketFor0RTT( - sealer sealer, - maxPacketSize protocol.ByteCount, - now time.Time, - v protocol.Version, -) (*wire.ExtendedHeader, payload) { +func (p *packetPacker) maybeGetAppDataPacketFor0RTT(sealer sealer, maxSize protocol.ByteCount, now monotime.Time, v protocol.Version) (*wire.ExtendedHeader, payload) { if p.perspective != protocol.PerspectiveClient { return nil, payload{} } hdr := p.getLongHeader(protocol.Encryption0RTT, v) - maxPayloadSize := maxPacketSize - hdr.GetLength(v) - protocol.ByteCount(sealer.Overhead()) + maxPayloadSize := maxSize - hdr.GetLength(v) - protocol.ByteCount(sealer.Overhead()) return hdr, p.maybeGetAppDataPacket(maxPayloadSize, false, false, now, v) } @@ -590,7 +592,7 @@ func (p *packetPacker) maybeGetShortHeaderPacket( sealer handshake.ShortHeaderSealer, hdrLen, maxPacketSize protocol.ByteCount, onlyAck, ackAllowed bool, - now time.Time, + now monotime.Time, v protocol.Version, ) payload { maxPayloadSize := maxPacketSize - hdrLen - protocol.ByteCount(sealer.Overhead()) @@ -600,7 +602,7 @@ func (p *packetPacker) maybeGetShortHeaderPacket( func (p *packetPacker) maybeGetAppDataPacket( maxPayloadSize protocol.ByteCount, onlyAck, ackAllowed bool, - now time.Time, + now monotime.Time, v protocol.Version, ) payload { pl := p.composeNextPacket(maxPayloadSize, onlyAck, ackAllowed, now, v) @@ -626,9 +628,9 @@ func (p *packetPacker) maybeGetAppDataPacket( } func (p *packetPacker) composeNextPacket( - maxFrameSize protocol.ByteCount, + maxPayloadSize protocol.ByteCount, onlyAck, ackAllowed bool, - now time.Time, + now monotime.Time, v protocol.Version, ) payload { if onlyAck { @@ -639,7 +641,7 @@ func (p *packetPacker) composeNextPacket( } hasData := p.framer.HasData() - hasRetransmission := p.retransmissionQueue.HasAppData() + hasRetransmission := p.retransmissionQueue.HasData(protocol.Encryption1RTT) var hasAck bool var pl payload @@ -654,7 +656,7 @@ func (p *packetPacker) composeNextPacket( if p.datagramQueue != nil { if f := p.datagramQueue.Peek(); f != nil { size := f.Length(v) - if size <= maxFrameSize-pl.length { // DATAGRAM frame fits + if size <= maxPayloadSize-pl.length { // DATAGRAM frame fits pl.frames = append(pl.frames, ackhandler.Frame{Frame: f}) pl.length += size p.datagramQueue.Pop() @@ -674,15 +676,15 @@ func (p *packetPacker) composeNextPacket( if hasRetransmission { for { - remainingLen := maxFrameSize - pl.length + remainingLen := maxPayloadSize - pl.length if remainingLen < protocol.MinStreamFrameSize { break } - f := p.retransmissionQueue.GetAppDataFrame(remainingLen, v) + f := p.retransmissionQueue.GetFrame(protocol.Encryption1RTT, remainingLen, v) if f == nil { break } - pl.frames = append(pl.frames, ackhandler.Frame{Frame: f, Handler: p.retransmissionQueue.AppDataAckHandler()}) + pl.frames = append(pl.frames, ackhandler.Frame{Frame: f, Handler: p.retransmissionQueue.AckHandler(protocol.Encryption1RTT)}) pl.length += f.Length(v) } } @@ -690,7 +692,7 @@ func (p *packetPacker) composeNextPacket( if hasData { var lengthAdded protocol.ByteCount startLen := len(pl.frames) - pl.frames, pl.streamFrames, lengthAdded = p.framer.Append(pl.frames, pl.streamFrames, maxFrameSize-pl.length, now, v) + pl.frames, pl.streamFrames, lengthAdded = p.framer.Append(pl.frames, pl.streamFrames, maxPayloadSize-pl.length, now, v) pl.length += lengthAdded // add handlers for the control frames that were added for i := startLen; i < len(pl.frames); i++ { @@ -702,40 +704,23 @@ func (p *packetPacker) composeNextPacket( // Path probing is currently not supported, therefore we don't need to set the OnAcked callback yet. // PATH_CHALLENGE and PATH_RESPONSE are never retransmitted. default: - pl.frames[i].Handler = p.retransmissionQueue.AppDataAckHandler() + // we might be packing a 0-RTT packet, but we need to use the 1-RTT ack handler anyway + pl.frames[i].Handler = p.retransmissionQueue.AckHandler(protocol.Encryption1RTT) } } } return pl } -func (p *packetPacker) MaybePackPTOProbePacket( +func (p *packetPacker) PackPTOProbePacket( encLevel protocol.EncryptionLevel, maxPacketSize protocol.ByteCount, - now time.Time, + addPingIfEmpty bool, + now monotime.Time, v protocol.Version, ) (*coalescedPacket, error) { if encLevel == protocol.Encryption1RTT { - s, err := p.cryptoSetup.Get1RTTSealer() - if err != nil { - return nil, err - } - kp := s.KeyPhase() - connID := p.getDestConnID() - pn, pnLen := p.pnManager.PeekPacketNumber(protocol.Encryption1RTT) - hdrLen := wire.ShortHeaderLen(connID, pnLen) - pl := p.maybeGetAppDataPacket(maxPacketSize-protocol.ByteCount(s.Overhead())-hdrLen, false, true, now, v) - if pl.length == 0 { - return nil, nil - } - buffer := getPacketBuffer() - packet := &coalescedPacket{buffer: buffer} - shp, err := p.appendShortHeaderPacket(buffer, connID, pn, pnLen, kp, pl, 0, maxPacketSize, s, false, v) - if err != nil { - return nil, err - } - packet.shortHdrPacket = &shp - return packet, nil + return p.packPTOProbePacket1RTT(maxPacketSize, addPingIfEmpty, now, v) } var sealer handshake.LongHeaderSealer @@ -756,7 +741,15 @@ func (p *packetPacker) MaybePackPTOProbePacket( default: panic("unknown encryption level") } - hdr, pl := p.maybeGetCryptoPacket(maxPacketSize-protocol.ByteCount(sealer.Overhead()), encLevel, now, false, true, v) + hdr, pl := p.maybeGetCryptoPacket( + maxPacketSize-protocol.ByteCount(sealer.Overhead()), + encLevel, + now, + addPingIfEmpty, + false, + true, + v, + ) if pl.length == 0 { return nil, nil } @@ -776,6 +769,34 @@ func (p *packetPacker) MaybePackPTOProbePacket( return packet, nil } +func (p *packetPacker) packPTOProbePacket1RTT(maxPacketSize protocol.ByteCount, addPingIfEmpty bool, now monotime.Time, v protocol.Version) (*coalescedPacket, error) { + s, err := p.cryptoSetup.Get1RTTSealer() + if err != nil { + return nil, err + } + kp := s.KeyPhase() + connID := p.getDestConnID() + pn, pnLen := p.pnManager.PeekPacketNumber(protocol.Encryption1RTT) + hdrLen := wire.ShortHeaderLen(connID, pnLen) + pl := p.maybeGetAppDataPacket(maxPacketSize-protocol.ByteCount(s.Overhead())-hdrLen, false, true, now, v) + if pl.length == 0 { + if !addPingIfEmpty { + return nil, nil + } + ping := &wire.PingFrame{} + pl.frames = append(pl.frames, ackhandler.Frame{Frame: ping, Handler: emptyHandler{}}) + pl.length += ping.Length(v) + } + buffer := getPacketBuffer() + packet := &coalescedPacket{buffer: buffer} + shp, err := p.appendShortHeaderPacket(buffer, connID, pn, pnLen, kp, pl, 0, maxPacketSize, s, false, v) + if err != nil { + return nil, err + } + packet.shortHdrPacket = &shp + return packet, nil +} + func (p *packetPacker) PackMTUProbePacket(ping ackhandler.Frame, size protocol.ByteCount, v protocol.Version) (shortHeaderPacket, *packetBuffer, error) { pl := payload{ frames: []ackhandler.Frame{ping}, @@ -794,16 +815,20 @@ func (p *packetPacker) PackMTUProbePacket(ping ackhandler.Frame, size protocol.B return packet, buffer, err } -func (p *packetPacker) PackPathProbePacket(connID protocol.ConnectionID, f ackhandler.Frame, v protocol.Version) (shortHeaderPacket, *packetBuffer, error) { +func (p *packetPacker) PackPathProbePacket(connID protocol.ConnectionID, frames []ackhandler.Frame, v protocol.Version) (shortHeaderPacket, *packetBuffer, error) { pn, pnLen := p.pnManager.PeekPacketNumber(protocol.Encryption1RTT) buf := getPacketBuffer() s, err := p.cryptoSetup.Get1RTTSealer() if err != nil { return shortHeaderPacket{}, nil, err } + var l protocol.ByteCount + for _, f := range frames { + l += f.Frame.Length(v) + } payload := payload{ - frames: []ackhandler.Frame{f}, - length: f.Frame.Length(v), + frames: frames, + length: l, } padding := protocol.MinInitialPacketSize - p.shortHeaderPacketLength(connID, pnLen, payload) - protocol.ByteCount(s.Overhead()) packet, err := p.appendShortHeaderPacket(buf, connID, pn, pnLen, s.KeyPhase(), payload, padding, protocol.MinInitialPacketSize, s, false, v) @@ -979,3 +1004,10 @@ func (p *packetPacker) encryptPacket(raw []byte, sealer sealer, pn protocol.Pack func (p *packetPacker) SetToken(token []byte) { p.token = token } + +type emptyHandler struct{} + +var _ ackhandler.FrameHandler = emptyHandler{} + +func (emptyHandler) OnAcked(wire.Frame) {} +func (emptyHandler) OnLost(wire.Frame) {} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/packet_unpacker.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/packet_unpacker.go index 9e0fa9d90..0729636e2 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/packet_unpacker.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/packet_unpacker.go @@ -2,9 +2,9 @@ package quic import ( "fmt" - "time" "github.com/quic-go/quic-go/internal/handshake" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/wire" @@ -106,7 +106,7 @@ func (u *packetUnpacker) UnpackLongHeader(hdr *wire.Header, data []byte) (*unpac }, nil } -func (u *packetUnpacker) UnpackShortHeader(rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) { +func (u *packetUnpacker) UnpackShortHeader(rcvTime monotime.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) { opener, err := u.cs.Get1RTTOpener() if err != nil { return 0, 0, 0, nil, err @@ -144,7 +144,7 @@ func (u *packetUnpacker) unpackLongHeaderPacket(opener handshake.LongHeaderOpene return extHdr, decrypted, nil } -func (u *packetUnpacker) unpackShortHeaderPacket(opener handshake.ShortHeaderOpener, rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) { +func (u *packetUnpacker) unpackShortHeaderPacket(opener handshake.ShortHeaderOpener, rcvTime monotime.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) { l, pn, pnLen, kp, parseErr := u.unpackShortHeader(opener, data) // If the reserved bits are set incorrectly, we still need to continue unpacking. // This avoids a timing side-channel, which otherwise might allow an attacker @@ -197,8 +197,7 @@ func (u *packetUnpacker) unpackLongHeader(hd headerDecryptor, hdr *wire.Header, func unpackLongHeader(hd headerDecryptor, hdr *wire.Header, data []byte) (*wire.ExtendedHeader, error) { hdrLen := hdr.ParsedLen() if protocol.ByteCount(len(data)) < hdrLen+4+16 { - //nolint:stylecheck - return nil, fmt.Errorf("Packet too small. Expected at least 20 bytes after the header, got %d", protocol.ByteCount(len(data))-hdrLen) + return nil, fmt.Errorf("packet too small, expected at least 20 bytes after the header, got %d", protocol.ByteCount(len(data))-hdrLen) } // The packet number can be up to 4 bytes long, but we won't know the length until we decrypt it. // 1. save a copy of the 4 bytes diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/path_manager.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/path_manager.go index 6d940921d..dd188ea14 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/path_manager.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/path_manager.go @@ -3,8 +3,11 @@ package quic import ( "crypto/rand" "net" + "slices" + "time" "github.com/quic-go/quic-go/internal/ackhandler" + "github.com/quic-go/quic-go/internal/monotime" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" @@ -12,10 +15,23 @@ import ( type pathID int64 +const invalidPathID pathID = -1 + +// Maximum number of paths to keep track of. +// If the peer probes another path (before the pathTimeout of an existing path expires), +// this probing attempt is ignored. const maxPaths = 3 +// If no packet is received for a path for pathTimeout, +// the path can be evicted when the peer probes another path. +// This prevents an attacker from churning through paths by duplicating packets and +// sending them with spoofed source addresses. +const pathTimeout = 5 * time.Second + type path struct { + id pathID addr net.Addr + lastPacketTime monotime.Time pathChallenge [8]byte validated bool rcvdNonProbing bool @@ -23,7 +39,8 @@ type path struct { type pathManager struct { nextPathID pathID - paths map[pathID]*path + // ordered by lastPacketTime, with the most recently used path at the end + paths []*path getConnID func(pathID) (_ protocol.ConnectionID, ok bool) retireConnID func(pathID) @@ -37,7 +54,7 @@ func newPathManager( logger utils.Logger, ) *pathManager { return &pathManager{ - paths: make(map[pathID]*path), + paths: make([]*path, 0, maxPaths+1), getConnID: getConnID, retireConnID: retireConnID, logger: logger, @@ -46,47 +63,88 @@ func newPathManager( // Returns a path challenge frame if one should be sent. // May return nil. -func (pm *pathManager) HandlePacket(p receivedPacket, isNonProbing bool) (_ protocol.ConnectionID, _ ackhandler.Frame, shouldSwitch bool) { - for _, path := range pm.paths { - if addrsEqual(path.addr, p.remoteAddr) { +func (pm *pathManager) HandlePacket( + remoteAddr net.Addr, + t monotime.Time, + pathChallenge *wire.PathChallengeFrame, // may be nil if the packet didn't contain a PATH_CHALLENGE + isNonProbing bool, +) (_ protocol.ConnectionID, _ []ackhandler.Frame, shouldSwitch bool) { + var p *path + for i, path := range pm.paths { + if addrsEqual(path.addr, remoteAddr) { + p = path + p.lastPacketTime = t // already sent a PATH_CHALLENGE for this path if isNonProbing { path.rcvdNonProbing = true } if pm.logger.Debug() { - pm.logger.Debugf("received packet for path %s that was already probed, validated: %t", p.remoteAddr, path.validated) + pm.logger.Debugf("received packet for path %s that was already probed, validated: %t", remoteAddr, path.validated) + } + shouldSwitch = path.validated && path.rcvdNonProbing + if i != len(pm.paths)-1 { + // move the path to the end of the list + pm.paths = slices.Delete(pm.paths, i, i+1) + pm.paths = append(pm.paths, p) + } + if pathChallenge == nil { + return protocol.ConnectionID{}, nil, shouldSwitch } - return protocol.ConnectionID{}, ackhandler.Frame{}, path.validated && path.rcvdNonProbing } } if len(pm.paths) >= maxPaths { - if pm.logger.Debug() { - pm.logger.Debugf("received packet for previously unseen path %s, but already have %d paths", p.remoteAddr, len(pm.paths)) + if pm.paths[0].lastPacketTime.Add(pathTimeout).After(t) { + if pm.logger.Debug() { + pm.logger.Debugf("received packet for previously unseen path %s, but already have %d paths", remoteAddr, len(pm.paths)) + } + return protocol.ConnectionID{}, nil, shouldSwitch } - return protocol.ConnectionID{}, ackhandler.Frame{}, false + // evict the oldest path, if the last packet was received more than pathTimeout ago + pm.retireConnID(pm.paths[0].id) + pm.paths = pm.paths[1:] + } + + var pathID pathID + if p != nil { + pathID = p.id + } else { + pathID = pm.nextPathID } // previously unseen path, initiate path validation by sending a PATH_CHALLENGE - connID, ok := pm.getConnID(pm.nextPathID) + connID, ok := pm.getConnID(pathID) if !ok { - pm.logger.Debugf("skipping validation of new path %s since no connection ID is available", p.remoteAddr) - return protocol.ConnectionID{}, ackhandler.Frame{}, false + pm.logger.Debugf("skipping validation of new path %s since no connection ID is available", remoteAddr) + return protocol.ConnectionID{}, nil, shouldSwitch } - var b [8]byte - rand.Read(b[:]) - pm.paths[pm.nextPathID] = &path{ - addr: p.remoteAddr, - pathChallenge: b, - rcvdNonProbing: isNonProbing, + + frames := make([]ackhandler.Frame, 0, 2) + if p == nil { + var pathChallengeData [8]byte + rand.Read(pathChallengeData[:]) + p = &path{ + id: pm.nextPathID, + addr: remoteAddr, + lastPacketTime: t, + rcvdNonProbing: isNonProbing, + pathChallenge: pathChallengeData, + } + pm.nextPathID++ + pm.paths = append(pm.paths, p) + frames = append(frames, ackhandler.Frame{ + Frame: &wire.PathChallengeFrame{Data: p.pathChallenge}, + Handler: (*pathManagerAckHandler)(pm), + }) + pm.logger.Debugf("enqueueing PATH_CHALLENGE for new path %s", remoteAddr) } - pm.nextPathID++ - frame := ackhandler.Frame{ - Frame: &wire.PathChallengeFrame{Data: b}, - Handler: (*pathManagerAckHandler)(pm), + if pathChallenge != nil { + frames = append(frames, ackhandler.Frame{ + Frame: &wire.PathResponseFrame{Data: pathChallenge.Data}, + Handler: (*pathManagerAckHandler)(pm), + }) } - pm.logger.Debugf("enqueueing PATH_CHALLENGE for new path %s", p.remoteAddr) - return connID, frame, false + return connID, frames, shouldSwitch } func (pm *pathManager) HandlePathResponseFrame(f *wire.PathResponseFrame) { @@ -103,14 +161,15 @@ func (pm *pathManager) HandlePathResponseFrame(f *wire.PathResponseFrame) { // SwitchToPath is called when the connection switches to a new path func (pm *pathManager) SwitchToPath(addr net.Addr) { // retire all other paths - for id := range pm.paths { - if addrsEqual(pm.paths[id].addr, addr) { - pm.logger.Debugf("switching to path %d (%s)", id, addr) + for _, path := range pm.paths { + if addrsEqual(path.addr, addr) { + pm.logger.Debugf("switching to path %d (%s)", path.id, addr) continue } - pm.retireConnID(id) + pm.retireConnID(path.id) } clear(pm.paths) + pm.paths = pm.paths[:0] } type pathManagerAckHandler pathManager @@ -121,12 +180,14 @@ var _ ackhandler.FrameHandler = &pathManagerAckHandler{} func (pm *pathManagerAckHandler) OnAcked(f wire.Frame) {} func (pm *pathManagerAckHandler) OnLost(f wire.Frame) { - // TODO: retransmit the packet the first time it is lost - pc := f.(*wire.PathChallengeFrame) - for id, path := range pm.paths { + pc, ok := f.(*wire.PathChallengeFrame) + if !ok { + return + } + for i, path := range pm.paths { if path.pathChallenge == pc.Data { - delete(pm.paths, id) - pm.retireConnID(id) + pm.paths = slices.Delete(pm.paths, i, i+1) + pm.retireConnID(path.id) break } } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/path_manager_outgoing.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/path_manager_outgoing.go new file mode 100644 index 000000000..78f68eeab --- /dev/null +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/path_manager_outgoing.go @@ -0,0 +1,314 @@ +package quic + +import ( + "context" + "crypto/rand" + "errors" + "slices" + "sync" + "sync/atomic" + "time" + + "github.com/quic-go/quic-go/internal/ackhandler" + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/wire" +) + +var ( + // ErrPathClosed is returned when trying to switch to a path that has been closed. + ErrPathClosed = errors.New("path closed") + // ErrPathNotValidated is returned when trying to use a path before path probing has completed. + ErrPathNotValidated = errors.New("path not yet validated") +) + +var errPathDoesNotExist = errors.New("path does not exist") + +// Path is a network path. +type Path struct { + id pathID + pathManager *pathManagerOutgoing + tr *Transport + initialRTT time.Duration + + enablePath func() + validated atomic.Bool + abandon chan struct{} +} + +func (p *Path) Probe(ctx context.Context) error { + path := p.pathManager.addPath(p, p.enablePath) + + p.pathManager.enqueueProbe(p) + nextProbeDur := p.initialRTT + var timer *time.Timer + var timerChan <-chan time.Time + for { + select { + case <-ctx.Done(): + return context.Cause(ctx) + case <-path.Validated(): + p.validated.Store(true) + return nil + case <-timerChan: + nextProbeDur *= 2 // exponential backoff + p.pathManager.enqueueProbe(p) + case <-path.ProbeSent(): + case <-p.abandon: + return ErrPathClosed + } + + if timer != nil { + timer.Stop() + } + timer = time.NewTimer(nextProbeDur) + timerChan = timer.C + } +} + +// Switch switches the QUIC connection to this path. +// It immediately stops sending on the old path, and sends on this new path. +func (p *Path) Switch() error { + if err := p.pathManager.switchToPath(p.id); err != nil { + switch { + case errors.Is(err, ErrPathNotValidated): + return err + case errors.Is(err, errPathDoesNotExist) && !p.validated.Load(): + select { + case <-p.abandon: + return ErrPathClosed + default: + return ErrPathNotValidated + } + default: + return ErrPathClosed + } + } + return nil +} + +// Close abandons a path. +// It is not possible to close the path that’s currently active. +// After closing, it is not possible to probe this path again. +func (p *Path) Close() error { + select { + case <-p.abandon: + return nil + default: + } + + if err := p.pathManager.removePath(p.id); err != nil { + return err + } + close(p.abandon) + return nil +} + +type pathOutgoing struct { + pathChallenges [][8]byte // length is implicitly limited by exponential backoff + tr *Transport + isValidated bool + probeSent chan struct{} // receives when a PATH_CHALLENGE is sent + validated chan struct{} // closed when the path the corresponding PATH_RESPONSE is received + enablePath func() +} + +func (p *pathOutgoing) ProbeSent() <-chan struct{} { return p.probeSent } +func (p *pathOutgoing) Validated() <-chan struct{} { return p.validated } + +type pathManagerOutgoing struct { + getConnID func(pathID) (_ protocol.ConnectionID, ok bool) + retireConnID func(pathID) + scheduleSending func() + + mx sync.Mutex + activePath pathID + pathsToProbe []pathID + paths map[pathID]*pathOutgoing + nextPathID pathID + pathToSwitchTo *pathOutgoing +} + +// newPathManagerOutgoing creates a new pathManagerOutgoing object. This +// function must be side-effect free as it may be called multiple times for a +// single connection. +func newPathManagerOutgoing( + getConnID func(pathID) (_ protocol.ConnectionID, ok bool), + retireConnID func(pathID), + scheduleSending func(), +) *pathManagerOutgoing { + return &pathManagerOutgoing{ + activePath: 0, // at initialization time, we're guaranteed to be using the handshake path + nextPathID: 1, + getConnID: getConnID, + retireConnID: retireConnID, + scheduleSending: scheduleSending, + paths: make(map[pathID]*pathOutgoing, 4), + } +} + +func (pm *pathManagerOutgoing) addPath(p *Path, enablePath func()) *pathOutgoing { + pm.mx.Lock() + defer pm.mx.Unlock() + + // path might already exist, and just being re-probed + if existingPath, ok := pm.paths[p.id]; ok { + existingPath.validated = make(chan struct{}) + return existingPath + } + + path := &pathOutgoing{ + tr: p.tr, + probeSent: make(chan struct{}, 1), + validated: make(chan struct{}), + enablePath: enablePath, + } + pm.paths[p.id] = path + return path +} + +func (pm *pathManagerOutgoing) enqueueProbe(p *Path) { + pm.mx.Lock() + pm.pathsToProbe = append(pm.pathsToProbe, p.id) + pm.mx.Unlock() + pm.scheduleSending() +} + +func (pm *pathManagerOutgoing) removePath(id pathID) error { + if err := pm.removePathImpl(id); err != nil { + return err + } + pm.scheduleSending() + return nil +} + +func (pm *pathManagerOutgoing) removePathImpl(id pathID) error { + pm.mx.Lock() + defer pm.mx.Unlock() + + if id == pm.activePath { + return errors.New("cannot close active path") + } + p, ok := pm.paths[id] + if !ok { + return nil + } + if len(p.pathChallenges) > 0 { + pm.retireConnID(id) + } + delete(pm.paths, id) + return nil +} + +func (pm *pathManagerOutgoing) switchToPath(id pathID) error { + pm.mx.Lock() + defer pm.mx.Unlock() + + p, ok := pm.paths[id] + if !ok { + return errPathDoesNotExist + } + if !p.isValidated { + return ErrPathNotValidated + } + pm.pathToSwitchTo = p + pm.activePath = id + return nil +} + +func (pm *pathManagerOutgoing) NewPath(t *Transport, initialRTT time.Duration, enablePath func()) *Path { + pm.mx.Lock() + defer pm.mx.Unlock() + + id := pm.nextPathID + pm.nextPathID++ + return &Path{ + pathManager: pm, + id: id, + tr: t, + enablePath: enablePath, + initialRTT: initialRTT, + abandon: make(chan struct{}), + } +} + +func (pm *pathManagerOutgoing) NextPathToProbe() (_ protocol.ConnectionID, _ ackhandler.Frame, _ *Transport, hasPath bool) { + pm.mx.Lock() + defer pm.mx.Unlock() + + var p *pathOutgoing + id := invalidPathID + for _, pID := range pm.pathsToProbe { + var ok bool + p, ok = pm.paths[pID] + if ok { + id = pID + break + } + // if the path doesn't exist in the map, it might have been abandoned + pm.pathsToProbe = pm.pathsToProbe[1:] + } + if id == invalidPathID { + return protocol.ConnectionID{}, ackhandler.Frame{}, nil, false + } + + connID, ok := pm.getConnID(id) + if !ok { + return protocol.ConnectionID{}, ackhandler.Frame{}, nil, false + } + + var b [8]byte + _, _ = rand.Read(b[:]) + p.pathChallenges = append(p.pathChallenges, b) + + pm.pathsToProbe = pm.pathsToProbe[1:] + p.enablePath() + select { + case p.probeSent <- struct{}{}: + default: + } + frame := ackhandler.Frame{ + Frame: &wire.PathChallengeFrame{Data: b}, + Handler: (*pathManagerOutgoingAckHandler)(pm), + } + return connID, frame, p.tr, true +} + +func (pm *pathManagerOutgoing) HandlePathResponseFrame(f *wire.PathResponseFrame) { + pm.mx.Lock() + defer pm.mx.Unlock() + + for _, p := range pm.paths { + if slices.Contains(p.pathChallenges, f.Data) { + // path validated + if !p.isValidated { + // make sure that duplicate PATH_RESPONSE frames are ignored + p.isValidated = true + p.pathChallenges = nil + close(p.validated) + } + break + } + } +} + +func (pm *pathManagerOutgoing) ShouldSwitchPath() (*Transport, bool) { + pm.mx.Lock() + defer pm.mx.Unlock() + + if pm.pathToSwitchTo == nil { + return nil, false + } + p := pm.pathToSwitchTo + pm.pathToSwitchTo = nil + return p.tr, true +} + +type pathManagerOutgoingAckHandler pathManagerOutgoing + +var _ ackhandler.FrameHandler = &pathManagerOutgoingAckHandler{} + +// OnAcked is called when the PATH_CHALLENGE is acked. +// This doesn't validate the path, only receiving the PATH_RESPONSE does. +func (pm *pathManagerOutgoingAckHandler) OnAcked(wire.Frame) {} + +func (pm *pathManagerOutgoingAckHandler) OnLost(wire.Frame) {} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/connection_tracer.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/connection_tracer.go deleted file mode 100644 index b696c2c78..000000000 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/connection_tracer.go +++ /dev/null @@ -1,461 +0,0 @@ -package qlog - -import ( - "io" - "net" - "time" - - "github.com/quic-go/quic-go/internal/protocol" - "github.com/quic-go/quic-go/internal/utils" - "github.com/quic-go/quic-go/internal/wire" - "github.com/quic-go/quic-go/logging" - - "github.com/francoispqt/gojay" -) - -type connectionTracer struct { - w writer - lastMetrics *metrics - - perspective logging.Perspective -} - -// NewConnectionTracer creates a new tracer to record a qlog for a connection. -func NewConnectionTracer(w io.WriteCloser, p logging.Perspective, odcid protocol.ConnectionID) *logging.ConnectionTracer { - tr := &trace{ - VantagePoint: vantagePoint{Type: p.String()}, - CommonFields: commonFields{ - ODCID: &odcid, - GroupID: &odcid, - ReferenceTime: time.Now(), - }, - } - t := connectionTracer{ - w: *newWriter(w, tr), - perspective: p, - } - go t.w.Run() - return &logging.ConnectionTracer{ - StartedConnection: func(local, remote net.Addr, srcConnID, destConnID logging.ConnectionID) { - t.StartedConnection(local, remote, srcConnID, destConnID) - }, - NegotiatedVersion: func(chosen logging.Version, clientVersions, serverVersions []logging.Version) { - t.NegotiatedVersion(chosen, clientVersions, serverVersions) - }, - ClosedConnection: func(e error) { t.ClosedConnection(e) }, - SentTransportParameters: func(tp *wire.TransportParameters) { t.SentTransportParameters(tp) }, - ReceivedTransportParameters: func(tp *wire.TransportParameters) { t.ReceivedTransportParameters(tp) }, - RestoredTransportParameters: func(tp *wire.TransportParameters) { t.RestoredTransportParameters(tp) }, - SentLongHeaderPacket: func(hdr *logging.ExtendedHeader, size logging.ByteCount, ecn logging.ECN, ack *logging.AckFrame, frames []logging.Frame) { - t.SentLongHeaderPacket(hdr, size, ecn, ack, frames) - }, - SentShortHeaderPacket: func(hdr *logging.ShortHeader, size logging.ByteCount, ecn logging.ECN, ack *logging.AckFrame, frames []logging.Frame) { - t.SentShortHeaderPacket(hdr, size, ecn, ack, frames) - }, - ReceivedLongHeaderPacket: func(hdr *logging.ExtendedHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { - t.ReceivedLongHeaderPacket(hdr, size, ecn, frames) - }, - ReceivedShortHeaderPacket: func(hdr *logging.ShortHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { - t.ReceivedShortHeaderPacket(hdr, size, ecn, frames) - }, - ReceivedRetry: func(hdr *wire.Header) { - t.ReceivedRetry(hdr) - }, - ReceivedVersionNegotiationPacket: func(dest, src logging.ArbitraryLenConnectionID, versions []logging.Version) { - t.ReceivedVersionNegotiationPacket(dest, src, versions) - }, - BufferedPacket: func(pt logging.PacketType, size protocol.ByteCount) { - t.BufferedPacket(pt, size) - }, - DroppedPacket: func(pt logging.PacketType, pn logging.PacketNumber, size logging.ByteCount, reason logging.PacketDropReason) { - t.DroppedPacket(pt, pn, size, reason) - }, - UpdatedMetrics: func(rttStats *utils.RTTStats, cwnd, bytesInFlight protocol.ByteCount, packetsInFlight int) { - t.UpdatedMetrics(rttStats, cwnd, bytesInFlight, packetsInFlight) - }, - LostPacket: func(encLevel protocol.EncryptionLevel, pn protocol.PacketNumber, lossReason logging.PacketLossReason) { - t.LostPacket(encLevel, pn, lossReason) - }, - UpdatedMTU: func(mtu logging.ByteCount, done bool) { - t.UpdatedMTU(mtu, done) - }, - UpdatedCongestionState: func(state logging.CongestionState) { - t.UpdatedCongestionState(state) - }, - UpdatedPTOCount: func(value uint32) { - t.UpdatedPTOCount(value) - }, - UpdatedKeyFromTLS: func(encLevel protocol.EncryptionLevel, pers protocol.Perspective) { - t.UpdatedKeyFromTLS(encLevel, pers) - }, - UpdatedKey: func(keyPhase protocol.KeyPhase, remote bool) { - t.UpdatedKey(keyPhase, remote) - }, - DroppedEncryptionLevel: func(encLevel protocol.EncryptionLevel) { - t.DroppedEncryptionLevel(encLevel) - }, - DroppedKey: func(keyPhase protocol.KeyPhase) { - t.DroppedKey(keyPhase) - }, - SetLossTimer: func(tt logging.TimerType, encLevel protocol.EncryptionLevel, timeout time.Time) { - t.SetLossTimer(tt, encLevel, timeout) - }, - LossTimerExpired: func(tt logging.TimerType, encLevel protocol.EncryptionLevel) { - t.LossTimerExpired(tt, encLevel) - }, - LossTimerCanceled: func() { - t.LossTimerCanceled() - }, - ECNStateUpdated: func(state logging.ECNState, trigger logging.ECNStateTrigger) { - t.ECNStateUpdated(state, trigger) - }, - ChoseALPN: func(protocol string) { - t.recordEvent(time.Now(), eventALPNInformation{chosenALPN: protocol}) - }, - Debug: func(name, msg string) { - t.Debug(name, msg) - }, - Close: func() { - t.Close() - }, - } -} - -func (t *connectionTracer) recordEvent(eventTime time.Time, details eventDetails) { - t.w.RecordEvent(eventTime, details) -} - -func (t *connectionTracer) Close() { - t.w.Close() -} - -func (t *connectionTracer) StartedConnection(local, remote net.Addr, srcConnID, destConnID protocol.ConnectionID) { - // ignore this event if we're not dealing with UDP addresses here - localAddr, ok := local.(*net.UDPAddr) - if !ok { - return - } - remoteAddr, ok := remote.(*net.UDPAddr) - if !ok { - return - } - t.recordEvent(time.Now(), &eventConnectionStarted{ - SrcAddr: localAddr, - DestAddr: remoteAddr, - SrcConnectionID: srcConnID, - DestConnectionID: destConnID, - }) -} - -func (t *connectionTracer) NegotiatedVersion(chosen logging.Version, client, server []logging.Version) { - var clientVersions, serverVersions []version - if len(client) > 0 { - clientVersions = make([]version, len(client)) - for i, v := range client { - clientVersions[i] = version(v) - } - } - if len(server) > 0 { - serverVersions = make([]version, len(server)) - for i, v := range server { - serverVersions[i] = version(v) - } - } - t.recordEvent(time.Now(), &eventVersionNegotiated{ - clientVersions: clientVersions, - serverVersions: serverVersions, - chosenVersion: version(chosen), - }) -} - -func (t *connectionTracer) ClosedConnection(e error) { - t.recordEvent(time.Now(), &eventConnectionClosed{e: e}) -} - -func (t *connectionTracer) SentTransportParameters(tp *wire.TransportParameters) { - t.recordTransportParameters(t.perspective, tp) -} - -func (t *connectionTracer) ReceivedTransportParameters(tp *wire.TransportParameters) { - t.recordTransportParameters(t.perspective.Opposite(), tp) -} - -func (t *connectionTracer) RestoredTransportParameters(tp *wire.TransportParameters) { - ev := t.toTransportParameters(tp) - ev.Restore = true - - t.recordEvent(time.Now(), ev) -} - -func (t *connectionTracer) recordTransportParameters(sentBy protocol.Perspective, tp *wire.TransportParameters) { - ev := t.toTransportParameters(tp) - ev.Owner = ownerLocal - if sentBy != t.perspective { - ev.Owner = ownerRemote - } - ev.SentBy = sentBy - - t.recordEvent(time.Now(), ev) -} - -func (t *connectionTracer) toTransportParameters(tp *wire.TransportParameters) *eventTransportParameters { - var pa *preferredAddress - if tp.PreferredAddress != nil { - pa = &preferredAddress{ - IPv4: tp.PreferredAddress.IPv4, - IPv6: tp.PreferredAddress.IPv6, - ConnectionID: tp.PreferredAddress.ConnectionID, - StatelessResetToken: tp.PreferredAddress.StatelessResetToken, - } - } - return &eventTransportParameters{ - OriginalDestinationConnectionID: tp.OriginalDestinationConnectionID, - InitialSourceConnectionID: tp.InitialSourceConnectionID, - RetrySourceConnectionID: tp.RetrySourceConnectionID, - StatelessResetToken: tp.StatelessResetToken, - DisableActiveMigration: tp.DisableActiveMigration, - MaxIdleTimeout: tp.MaxIdleTimeout, - MaxUDPPayloadSize: tp.MaxUDPPayloadSize, - AckDelayExponent: tp.AckDelayExponent, - MaxAckDelay: tp.MaxAckDelay, - ActiveConnectionIDLimit: tp.ActiveConnectionIDLimit, - InitialMaxData: tp.InitialMaxData, - InitialMaxStreamDataBidiLocal: tp.InitialMaxStreamDataBidiLocal, - InitialMaxStreamDataBidiRemote: tp.InitialMaxStreamDataBidiRemote, - InitialMaxStreamDataUni: tp.InitialMaxStreamDataUni, - InitialMaxStreamsBidi: int64(tp.MaxBidiStreamNum), - InitialMaxStreamsUni: int64(tp.MaxUniStreamNum), - PreferredAddress: pa, - MaxDatagramFrameSize: tp.MaxDatagramFrameSize, - } -} - -func (t *connectionTracer) SentLongHeaderPacket( - hdr *logging.ExtendedHeader, - size logging.ByteCount, - ecn logging.ECN, - ack *logging.AckFrame, - frames []logging.Frame, -) { - t.sentPacket(*transformLongHeader(hdr), size, hdr.Length, ecn, ack, frames) -} - -func (t *connectionTracer) SentShortHeaderPacket( - hdr *logging.ShortHeader, - size logging.ByteCount, - ecn logging.ECN, - ack *logging.AckFrame, - frames []logging.Frame, -) { - t.sentPacket(*transformShortHeader(hdr), size, 0, ecn, ack, frames) -} - -func (t *connectionTracer) sentPacket( - hdr gojay.MarshalerJSONObject, - size, payloadLen logging.ByteCount, - ecn logging.ECN, - ack *logging.AckFrame, - frames []logging.Frame, -) { - numFrames := len(frames) - if ack != nil { - numFrames++ - } - fs := make([]frame, 0, numFrames) - if ack != nil { - fs = append(fs, frame{Frame: ack}) - } - for _, f := range frames { - fs = append(fs, frame{Frame: f}) - } - t.recordEvent(time.Now(), &eventPacketSent{ - Header: hdr, - Length: size, - PayloadLength: payloadLen, - ECN: ecn, - Frames: fs, - }) -} - -func (t *connectionTracer) ReceivedLongHeaderPacket(hdr *logging.ExtendedHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { - fs := make([]frame, len(frames)) - for i, f := range frames { - fs[i] = frame{Frame: f} - } - header := *transformLongHeader(hdr) - t.recordEvent(time.Now(), &eventPacketReceived{ - Header: header, - Length: size, - PayloadLength: hdr.Length, - ECN: ecn, - Frames: fs, - }) -} - -func (t *connectionTracer) ReceivedShortHeaderPacket(hdr *logging.ShortHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { - fs := make([]frame, len(frames)) - for i, f := range frames { - fs[i] = frame{Frame: f} - } - header := *transformShortHeader(hdr) - t.recordEvent(time.Now(), &eventPacketReceived{ - Header: header, - Length: size, - PayloadLength: size - wire.ShortHeaderLen(hdr.DestConnectionID, hdr.PacketNumberLen), - ECN: ecn, - Frames: fs, - }) -} - -func (t *connectionTracer) ReceivedRetry(hdr *wire.Header) { - t.recordEvent(time.Now(), &eventRetryReceived{ - Header: *transformHeader(hdr), - }) -} - -func (t *connectionTracer) ReceivedVersionNegotiationPacket(dest, src logging.ArbitraryLenConnectionID, versions []logging.Version) { - ver := make([]version, len(versions)) - for i, v := range versions { - ver[i] = version(v) - } - t.recordEvent(time.Now(), &eventVersionNegotiationReceived{ - Header: packetHeaderVersionNegotiation{ - SrcConnectionID: src, - DestConnectionID: dest, - }, - SupportedVersions: ver, - }) -} - -func (t *connectionTracer) BufferedPacket(pt logging.PacketType, size protocol.ByteCount) { - t.recordEvent(time.Now(), &eventPacketBuffered{ - PacketType: pt, - PacketSize: size, - }) -} - -func (t *connectionTracer) DroppedPacket(pt logging.PacketType, pn logging.PacketNumber, size protocol.ByteCount, reason logging.PacketDropReason) { - t.recordEvent(time.Now(), &eventPacketDropped{ - PacketType: pt, - PacketNumber: pn, - PacketSize: size, - Trigger: packetDropReason(reason), - }) -} - -func (t *connectionTracer) UpdatedMetrics(rttStats *utils.RTTStats, cwnd, bytesInFlight protocol.ByteCount, packetsInFlight int) { - m := &metrics{ - MinRTT: rttStats.MinRTT(), - SmoothedRTT: rttStats.SmoothedRTT(), - LatestRTT: rttStats.LatestRTT(), - RTTVariance: rttStats.MeanDeviation(), - CongestionWindow: cwnd, - BytesInFlight: bytesInFlight, - PacketsInFlight: packetsInFlight, - } - t.recordEvent(time.Now(), &eventMetricsUpdated{ - Last: t.lastMetrics, - Current: m, - }) - t.lastMetrics = m -} - -func (t *connectionTracer) AcknowledgedPacket(protocol.EncryptionLevel, protocol.PacketNumber) {} - -func (t *connectionTracer) LostPacket(encLevel protocol.EncryptionLevel, pn protocol.PacketNumber, lossReason logging.PacketLossReason) { - t.recordEvent(time.Now(), &eventPacketLost{ - PacketType: getPacketTypeFromEncryptionLevel(encLevel), - PacketNumber: pn, - Trigger: packetLossReason(lossReason), - }) -} - -func (t *connectionTracer) UpdatedMTU(mtu protocol.ByteCount, done bool) { - t.recordEvent(time.Now(), &eventMTUUpdated{mtu: mtu, done: done}) -} - -func (t *connectionTracer) UpdatedCongestionState(state logging.CongestionState) { - t.recordEvent(time.Now(), &eventCongestionStateUpdated{state: congestionState(state)}) -} - -func (t *connectionTracer) UpdatedPTOCount(value uint32) { - t.recordEvent(time.Now(), &eventUpdatedPTO{Value: value}) -} - -func (t *connectionTracer) UpdatedKeyFromTLS(encLevel protocol.EncryptionLevel, pers protocol.Perspective) { - t.recordEvent(time.Now(), &eventKeyUpdated{ - Trigger: keyUpdateTLS, - KeyType: encLevelToKeyType(encLevel, pers), - }) -} - -func (t *connectionTracer) UpdatedKey(generation protocol.KeyPhase, remote bool) { - trigger := keyUpdateLocal - if remote { - trigger = keyUpdateRemote - } - now := time.Now() - t.recordEvent(now, &eventKeyUpdated{ - Trigger: trigger, - KeyType: keyTypeClient1RTT, - KeyPhase: generation, - }) - t.recordEvent(now, &eventKeyUpdated{ - Trigger: trigger, - KeyType: keyTypeServer1RTT, - KeyPhase: generation, - }) -} - -func (t *connectionTracer) DroppedEncryptionLevel(encLevel protocol.EncryptionLevel) { - now := time.Now() - if encLevel == protocol.Encryption0RTT { - t.recordEvent(now, &eventKeyDiscarded{KeyType: encLevelToKeyType(encLevel, t.perspective)}) - } else { - t.recordEvent(now, &eventKeyDiscarded{KeyType: encLevelToKeyType(encLevel, protocol.PerspectiveServer)}) - t.recordEvent(now, &eventKeyDiscarded{KeyType: encLevelToKeyType(encLevel, protocol.PerspectiveClient)}) - } -} - -func (t *connectionTracer) DroppedKey(generation protocol.KeyPhase) { - now := time.Now() - t.recordEvent(now, &eventKeyDiscarded{ - KeyType: encLevelToKeyType(protocol.Encryption1RTT, protocol.PerspectiveServer), - KeyPhase: generation, - }) - t.recordEvent(now, &eventKeyDiscarded{ - KeyType: encLevelToKeyType(protocol.Encryption1RTT, protocol.PerspectiveClient), - KeyPhase: generation, - }) -} - -func (t *connectionTracer) SetLossTimer(tt logging.TimerType, encLevel protocol.EncryptionLevel, timeout time.Time) { - now := time.Now() - t.recordEvent(now, &eventLossTimerSet{ - TimerType: timerType(tt), - EncLevel: encLevel, - Delta: timeout.Sub(now), - }) -} - -func (t *connectionTracer) LossTimerExpired(tt logging.TimerType, encLevel protocol.EncryptionLevel) { - t.recordEvent(time.Now(), &eventLossTimerExpired{ - TimerType: timerType(tt), - EncLevel: encLevel, - }) -} - -func (t *connectionTracer) LossTimerCanceled() { - t.recordEvent(time.Now(), &eventLossTimerCanceled{}) -} - -func (t *connectionTracer) ECNStateUpdated(state logging.ECNState, trigger logging.ECNStateTrigger) { - t.recordEvent(time.Now(), &eventECNStateUpdated{state: state, trigger: trigger}) -} - -func (t *connectionTracer) Debug(name, msg string) { - t.recordEvent(time.Now(), &eventGeneric{ - name: name, - msg: msg, - }) -} diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/event.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/event.go index e97b057bb..77cdff232 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/event.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/event.go @@ -1,594 +1,807 @@ package qlog import ( - "errors" "fmt" - "net" "net/netip" "time" - "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/protocol" - "github.com/quic-go/quic-go/logging" - - "github.com/francoispqt/gojay" + "github.com/quic-go/quic-go/internal/qerr" + "github.com/quic-go/quic-go/qlogwriter/jsontext" ) func milliseconds(dur time.Duration) float64 { return float64(dur.Nanoseconds()) / 1e6 } -type eventDetails interface { - Category() category - Name() string - gojay.MarshalerJSONObject -} - -type event struct { - RelativeTime time.Duration - eventDetails +type encoderHelper struct { + enc *jsontext.Encoder + err error } -var _ gojay.MarshalerJSONObject = event{} - -func (e event) IsNil() bool { return false } -func (e event) MarshalJSONObject(enc *gojay.Encoder) { - enc.Float64Key("time", milliseconds(e.RelativeTime)) - enc.StringKey("name", e.Category().String()+":"+e.Name()) - enc.ObjectKey("data", e.eventDetails) +func (h *encoderHelper) WriteToken(t jsontext.Token) { + if h.err != nil { + return + } + h.err = h.enc.WriteToken(t) } -type versions []version +type versions []Version -func (v versions) IsNil() bool { return false } -func (v versions) MarshalJSONArray(enc *gojay.Encoder) { +func (v versions) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginArray) for _, e := range v { - enc.AddString(e.String()) + h.WriteToken(jsontext.String(fmt.Sprintf("%x", uint32(e)))) } + h.WriteToken(jsontext.EndArray) + return h.err } -type rawInfo struct { - Length logging.ByteCount // full packet length, including header and AEAD authentication tag - PayloadLength logging.ByteCount // length of the packet payload, excluding AEAD tag +type RawInfo struct { + Length int // full packet length, including header and AEAD authentication tag + PayloadLength int // length of the packet payload, excluding AEAD tag } -func (i rawInfo) IsNil() bool { return false } -func (i rawInfo) MarshalJSONObject(enc *gojay.Encoder) { - enc.Uint64Key("length", uint64(i.Length)) - enc.Uint64KeyOmitEmpty("payload_length", uint64(i.PayloadLength)) +func (i RawInfo) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("length")) + h.WriteToken(jsontext.Uint(uint64(i.Length))) + if i.PayloadLength != 0 { + h.WriteToken(jsontext.String("payload_length")) + h.WriteToken(jsontext.Uint(uint64(i.PayloadLength))) + } + h.WriteToken(jsontext.EndObject) + return h.err } -type eventConnectionStarted struct { - SrcAddr *net.UDPAddr - DestAddr *net.UDPAddr - - SrcConnectionID protocol.ConnectionID - DestConnectionID protocol.ConnectionID +type PathEndpointInfo struct { + IPv4 netip.AddrPort + IPv6 netip.AddrPort } -var _ eventDetails = &eventConnectionStarted{} - -func (e eventConnectionStarted) Category() category { return categoryTransport } -func (e eventConnectionStarted) Name() string { return "connection_started" } -func (e eventConnectionStarted) IsNil() bool { return false } - -func (e eventConnectionStarted) MarshalJSONObject(enc *gojay.Encoder) { - if e.SrcAddr.IP.To4() != nil { - enc.StringKey("ip_version", "ipv4") - } else { - enc.StringKey("ip_version", "ipv6") +func (p PathEndpointInfo) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + if p.IPv4.IsValid() { + h.WriteToken(jsontext.String("ip_v4")) + h.WriteToken(jsontext.String(p.IPv4.Addr().String())) + h.WriteToken(jsontext.String("port_v4")) + h.WriteToken(jsontext.Int(int64(p.IPv4.Port()))) + } + if p.IPv6.IsValid() { + h.WriteToken(jsontext.String("ip_v6")) + h.WriteToken(jsontext.String(p.IPv6.Addr().String())) + h.WriteToken(jsontext.String("port_v6")) + h.WriteToken(jsontext.Int(int64(p.IPv6.Port()))) } - enc.StringKey("src_ip", e.SrcAddr.IP.String()) - enc.IntKey("src_port", e.SrcAddr.Port) - enc.StringKey("dst_ip", e.DestAddr.IP.String()) - enc.IntKey("dst_port", e.DestAddr.Port) - enc.StringKey("src_cid", e.SrcConnectionID.String()) - enc.StringKey("dst_cid", e.DestConnectionID.String()) + h.WriteToken(jsontext.EndObject) + return h.err } -type eventVersionNegotiated struct { - clientVersions, serverVersions []version - chosenVersion version +type StartedConnection struct { + Local PathEndpointInfo + Remote PathEndpointInfo } -func (e eventVersionNegotiated) Category() category { return categoryTransport } -func (e eventVersionNegotiated) Name() string { return "version_information" } -func (e eventVersionNegotiated) IsNil() bool { return false } +func (e StartedConnection) Name() string { return "transport:connection_started" } -func (e eventVersionNegotiated) MarshalJSONObject(enc *gojay.Encoder) { - if len(e.clientVersions) > 0 { - enc.ArrayKey("client_versions", versions(e.clientVersions)) +func (e StartedConnection) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("local")) + if err := e.Local.encode(enc); err != nil { + return err } - if len(e.serverVersions) > 0 { - enc.ArrayKey("server_versions", versions(e.serverVersions)) + h.WriteToken(jsontext.String("remote")) + if err := e.Remote.encode(enc); err != nil { + return err } - enc.StringKey("chosen_version", e.chosenVersion.String()) + h.WriteToken(jsontext.EndObject) + return h.err } -type eventConnectionClosed struct { - e error +type VersionInformation struct { + ClientVersions, ServerVersions []Version + ChosenVersion Version } -func (e eventConnectionClosed) Category() category { return categoryTransport } -func (e eventConnectionClosed) Name() string { return "connection_closed" } -func (e eventConnectionClosed) IsNil() bool { return false } +func (e VersionInformation) Name() string { return "transport:version_information" } -func (e eventConnectionClosed) MarshalJSONObject(enc *gojay.Encoder) { - var ( - statelessResetErr *quic.StatelessResetError - handshakeTimeoutErr *quic.HandshakeTimeoutError - idleTimeoutErr *quic.IdleTimeoutError - applicationErr *quic.ApplicationError - transportErr *quic.TransportError - versionNegotiationErr *quic.VersionNegotiationError - ) - switch { - case errors.As(e.e, &statelessResetErr): - enc.StringKey("owner", ownerRemote.String()) - enc.StringKey("trigger", "stateless_reset") - case errors.As(e.e, &handshakeTimeoutErr): - enc.StringKey("owner", ownerLocal.String()) - enc.StringKey("trigger", "handshake_timeout") - case errors.As(e.e, &idleTimeoutErr): - enc.StringKey("owner", ownerLocal.String()) - enc.StringKey("trigger", "idle_timeout") - case errors.As(e.e, &applicationErr): - owner := ownerLocal - if applicationErr.Remote { - owner = ownerRemote +func (e VersionInformation) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + if len(e.ClientVersions) > 0 { + h.WriteToken(jsontext.String("client_versions")) + if err := versions(e.ClientVersions).encode(enc); err != nil { + return err } - enc.StringKey("owner", owner.String()) - enc.Uint64Key("application_code", uint64(applicationErr.ErrorCode)) - enc.StringKey("reason", applicationErr.ErrorMessage) - case errors.As(e.e, &transportErr): - owner := ownerLocal - if transportErr.Remote { - owner = ownerRemote + } + if len(e.ServerVersions) > 0 { + h.WriteToken(jsontext.String("server_versions")) + if err := versions(e.ServerVersions).encode(enc); err != nil { + return err } - enc.StringKey("owner", owner.String()) - enc.StringKey("connection_code", transportError(transportErr.ErrorCode).String()) - enc.StringKey("reason", transportErr.ErrorMessage) - case errors.As(e.e, &versionNegotiationErr): - enc.StringKey("trigger", "version_mismatch") } -} - -type eventPacketSent struct { - Header gojay.MarshalerJSONObject // either a shortHeader or a packetHeader - Length logging.ByteCount - PayloadLength logging.ByteCount - Frames frames - IsCoalesced bool - ECN logging.ECN - Trigger string -} - -var _ eventDetails = eventPacketSent{} - -func (e eventPacketSent) Category() category { return categoryTransport } -func (e eventPacketSent) Name() string { return "packet_sent" } -func (e eventPacketSent) IsNil() bool { return false } - -func (e eventPacketSent) MarshalJSONObject(enc *gojay.Encoder) { - enc.ObjectKey("header", e.Header) - enc.ObjectKey("raw", rawInfo{Length: e.Length, PayloadLength: e.PayloadLength}) - enc.ArrayKeyOmitEmpty("frames", e.Frames) - enc.BoolKeyOmitEmpty("is_coalesced", e.IsCoalesced) - if e.ECN != logging.ECNUnsupported { - enc.StringKey("ecn", ecn(e.ECN).String()) + h.WriteToken(jsontext.String("chosen_version")) + h.WriteToken(jsontext.String(fmt.Sprintf("%x", uint32(e.ChosenVersion)))) + h.WriteToken(jsontext.EndObject) + return h.err +} + +type ConnectionClosed struct { + Initiator Initiator + + ConnectionError *TransportErrorCode + ApplicationError *ApplicationErrorCode + + Reason string + + Trigger ConnectionCloseTrigger +} + +func (e ConnectionClosed) Name() string { return "transport:connection_closed" } + +func (e ConnectionClosed) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("initiator")) + h.WriteToken(jsontext.String(string(e.Initiator))) + if e.ConnectionError != nil { + h.WriteToken(jsontext.String("connection_error")) + if e.ConnectionError.IsCryptoError() { + h.WriteToken(jsontext.String(fmt.Sprintf("crypto_error_%#x", uint16(*e.ConnectionError)))) + } else { + switch *e.ConnectionError { + case qerr.NoError: + h.WriteToken(jsontext.String("no_error")) + case qerr.InternalError: + h.WriteToken(jsontext.String("internal_error")) + case qerr.ConnectionRefused: + h.WriteToken(jsontext.String("connection_refused")) + case qerr.FlowControlError: + h.WriteToken(jsontext.String("flow_control_error")) + case qerr.StreamLimitError: + h.WriteToken(jsontext.String("stream_limit_error")) + case qerr.StreamStateError: + h.WriteToken(jsontext.String("stream_state_error")) + case qerr.FinalSizeError: + h.WriteToken(jsontext.String("final_size_error")) + case qerr.FrameEncodingError: + h.WriteToken(jsontext.String("frame_encoding_error")) + case qerr.TransportParameterError: + h.WriteToken(jsontext.String("transport_parameter_error")) + case qerr.ConnectionIDLimitError: + h.WriteToken(jsontext.String("connection_id_limit_error")) + case qerr.ProtocolViolation: + h.WriteToken(jsontext.String("protocol_violation")) + case qerr.InvalidToken: + h.WriteToken(jsontext.String("invalid_token")) + case qerr.ApplicationErrorErrorCode: + h.WriteToken(jsontext.String("application_error")) + case qerr.CryptoBufferExceeded: + h.WriteToken(jsontext.String("crypto_buffer_exceeded")) + case qerr.KeyUpdateError: + h.WriteToken(jsontext.String("key_update_error")) + case qerr.AEADLimitReached: + h.WriteToken(jsontext.String("aead_limit_reached")) + case qerr.NoViablePathError: + h.WriteToken(jsontext.String("no_viable_path")) + default: + h.WriteToken(jsontext.String("unknown")) + h.WriteToken(jsontext.String("error_code")) + h.WriteToken(jsontext.Uint(uint64(*e.ConnectionError))) + } + } } - enc.StringKeyOmitEmpty("trigger", e.Trigger) + if e.ApplicationError != nil { + h.WriteToken(jsontext.String("application_error")) + h.WriteToken(jsontext.String("unknown")) + h.WriteToken(jsontext.String("error_code")) + h.WriteToken(jsontext.Uint(uint64(*e.ApplicationError))) + } + if e.ConnectionError != nil || e.ApplicationError != nil { + h.WriteToken(jsontext.String("reason")) + h.WriteToken(jsontext.String(e.Reason)) + } + if e.Trigger != "" { + h.WriteToken(jsontext.String("trigger")) + h.WriteToken(jsontext.String(string(e.Trigger))) + } + h.WriteToken(jsontext.EndObject) + return h.err } -type eventPacketReceived struct { - Header gojay.MarshalerJSONObject // either a shortHeader or a packetHeader - Length logging.ByteCount - PayloadLength logging.ByteCount - Frames frames - ECN logging.ECN - IsCoalesced bool - Trigger string +type PacketSent struct { + Header PacketHeader + Raw RawInfo + Frames []Frame + ECN ECN + IsCoalesced bool + Trigger string + SupportedVersions []Version } -var _ eventDetails = eventPacketReceived{} +func (e PacketSent) Name() string { return "transport:packet_sent" } -func (e eventPacketReceived) Category() category { return categoryTransport } -func (e eventPacketReceived) Name() string { return "packet_received" } -func (e eventPacketReceived) IsNil() bool { return false } - -func (e eventPacketReceived) MarshalJSONObject(enc *gojay.Encoder) { - enc.ObjectKey("header", e.Header) - enc.ObjectKey("raw", rawInfo{Length: e.Length, PayloadLength: e.PayloadLength}) - enc.ArrayKeyOmitEmpty("frames", e.Frames) - enc.BoolKeyOmitEmpty("is_coalesced", e.IsCoalesced) - if e.ECN != logging.ECNUnsupported { - enc.StringKey("ecn", ecn(e.ECN).String()) +func (e PacketSent) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("header")) + if err := e.Header.encode(enc); err != nil { + return err + } + h.WriteToken(jsontext.String("raw")) + if err := e.Raw.encode(enc); err != nil { + return err + } + if len(e.Frames) > 0 { + h.WriteToken(jsontext.String("frames")) + if err := frames(e.Frames).encode(enc); err != nil { + return err + } + } + if e.IsCoalesced { + h.WriteToken(jsontext.String("is_coalesced")) + h.WriteToken(jsontext.True) + } + if e.ECN != ECNUnsupported { + h.WriteToken(jsontext.String("ecn")) + h.WriteToken(jsontext.String(string(e.ECN))) + } + if e.Trigger != "" { + h.WriteToken(jsontext.String("trigger")) + h.WriteToken(jsontext.String(e.Trigger)) } - enc.StringKeyOmitEmpty("trigger", e.Trigger) + h.WriteToken(jsontext.EndObject) + return h.err } -type eventRetryReceived struct { - Header packetHeader +type PacketReceived struct { + Header PacketHeader + Raw RawInfo + Frames []Frame + ECN ECN + IsCoalesced bool + Trigger string } -func (e eventRetryReceived) Category() category { return categoryTransport } -func (e eventRetryReceived) Name() string { return "packet_received" } -func (e eventRetryReceived) IsNil() bool { return false } +func (e PacketReceived) Name() string { return "transport:packet_received" } -func (e eventRetryReceived) MarshalJSONObject(enc *gojay.Encoder) { - enc.ObjectKey("header", e.Header) +func (e PacketReceived) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("header")) + if err := e.Header.encode(enc); err != nil { + return err + } + h.WriteToken(jsontext.String("raw")) + if err := e.Raw.encode(enc); err != nil { + return err + } + if len(e.Frames) > 0 { + h.WriteToken(jsontext.String("frames")) + if err := frames(e.Frames).encode(enc); err != nil { + return err + } + } + if e.IsCoalesced { + h.WriteToken(jsontext.String("is_coalesced")) + h.WriteToken(jsontext.True) + } + if e.ECN != ECNUnsupported { + h.WriteToken(jsontext.String("ecn")) + h.WriteToken(jsontext.String(string(e.ECN))) + } + if e.Trigger != "" { + h.WriteToken(jsontext.String("trigger")) + h.WriteToken(jsontext.String(e.Trigger)) + } + h.WriteToken(jsontext.EndObject) + return h.err } -type eventVersionNegotiationReceived struct { - Header packetHeaderVersionNegotiation - SupportedVersions []version +type VersionNegotiationReceived struct { + Header PacketHeaderVersionNegotiation + SupportedVersions []Version } -func (e eventVersionNegotiationReceived) Category() category { return categoryTransport } -func (e eventVersionNegotiationReceived) Name() string { return "packet_received" } -func (e eventVersionNegotiationReceived) IsNil() bool { return false } +func (e VersionNegotiationReceived) Name() string { return "transport:packet_received" } -func (e eventVersionNegotiationReceived) MarshalJSONObject(enc *gojay.Encoder) { - enc.ObjectKey("header", e.Header) - enc.ArrayKey("supported_versions", versions(e.SupportedVersions)) +func (e VersionNegotiationReceived) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("header")) + if err := e.Header.encode(enc); err != nil { + return err + } + h.WriteToken(jsontext.String("supported_versions")) + if err := versions(e.SupportedVersions).encode(enc); err != nil { + return err + } + h.WriteToken(jsontext.EndObject) + return h.err } -type eventVersionNegotiationSent struct { - Header packetHeaderVersionNegotiation - SupportedVersions []version +type VersionNegotiationSent struct { + Header PacketHeaderVersionNegotiation + SupportedVersions []Version } -func (e eventVersionNegotiationSent) Category() category { return categoryTransport } -func (e eventVersionNegotiationSent) Name() string { return "packet_sent" } -func (e eventVersionNegotiationSent) IsNil() bool { return false } +func (e VersionNegotiationSent) Name() string { return "transport:packet_sent" } -func (e eventVersionNegotiationSent) MarshalJSONObject(enc *gojay.Encoder) { - enc.ObjectKey("header", e.Header) - enc.ArrayKey("supported_versions", versions(e.SupportedVersions)) +func (e VersionNegotiationSent) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("header")) + if err := e.Header.encode(enc); err != nil { + return err + } + h.WriteToken(jsontext.String("supported_versions")) + if err := versions(e.SupportedVersions).encode(enc); err != nil { + return err + } + h.WriteToken(jsontext.EndObject) + return h.err } -type eventPacketBuffered struct { - PacketType logging.PacketType - PacketSize protocol.ByteCount +type PacketBuffered struct { + Header PacketHeader + Raw RawInfo } -func (e eventPacketBuffered) Category() category { return categoryTransport } -func (e eventPacketBuffered) Name() string { return "packet_buffered" } -func (e eventPacketBuffered) IsNil() bool { return false } - -func (e eventPacketBuffered) MarshalJSONObject(enc *gojay.Encoder) { - //nolint:gosimple - enc.ObjectKey("header", packetHeaderWithType{PacketType: e.PacketType, PacketNumber: protocol.InvalidPacketNumber}) - enc.ObjectKey("raw", rawInfo{Length: e.PacketSize}) - enc.StringKey("trigger", "keys_unavailable") -} +func (e PacketBuffered) Name() string { return "transport:packet_buffered" } -type eventPacketDropped struct { - PacketType logging.PacketType - PacketSize protocol.ByteCount - PacketNumber logging.PacketNumber - Trigger packetDropReason +func (e PacketBuffered) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("header")) + if err := e.Header.encode(enc); err != nil { + return err + } + h.WriteToken(jsontext.String("raw")) + if err := e.Raw.encode(enc); err != nil { + return err + } + h.WriteToken(jsontext.String("trigger")) + h.WriteToken(jsontext.String("keys_unavailable")) + h.WriteToken(jsontext.EndObject) + return h.err } -func (e eventPacketDropped) Category() category { return categoryTransport } -func (e eventPacketDropped) Name() string { return "packet_dropped" } -func (e eventPacketDropped) IsNil() bool { return false } - -func (e eventPacketDropped) MarshalJSONObject(enc *gojay.Encoder) { - enc.ObjectKey("header", packetHeaderWithType{ - PacketType: e.PacketType, - PacketNumber: e.PacketNumber, - }) - enc.ObjectKey("raw", rawInfo{Length: e.PacketSize}) - enc.StringKey("trigger", e.Trigger.String()) +// PacketDropped is the transport:packet_dropped event. +type PacketDropped struct { + Header PacketHeader + Raw RawInfo + Trigger PacketDropReason } -type metrics struct { - MinRTT time.Duration - SmoothedRTT time.Duration - LatestRTT time.Duration - RTTVariance time.Duration +func (e PacketDropped) Name() string { return "transport:packet_dropped" } - CongestionWindow protocol.ByteCount - BytesInFlight protocol.ByteCount +func (e PacketDropped) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("header")) + if err := e.Header.encode(enc); err != nil { + return err + } + h.WriteToken(jsontext.String("raw")) + if err := e.Raw.encode(enc); err != nil { + return err + } + h.WriteToken(jsontext.String("trigger")) + h.WriteToken(jsontext.String(string(e.Trigger))) + h.WriteToken(jsontext.EndObject) + return h.err +} + +type MTUUpdated struct { + Value int + Done bool +} + +func (e MTUUpdated) Name() string { return "recovery:mtu_updated" } + +func (e MTUUpdated) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("mtu")) + h.WriteToken(jsontext.Uint(uint64(e.Value))) + h.WriteToken(jsontext.String("done")) + h.WriteToken(jsontext.Bool(e.Done)) + h.WriteToken(jsontext.EndObject) + return h.err +} + +// MetricsUpdated logs RTT and congestion metrics as defined in the +// recovery:metrics_updated event. +// The PTO count is logged via PTOCountUpdated. +type MetricsUpdated struct { + MinRTT time.Duration + SmoothedRTT time.Duration + LatestRTT time.Duration + RTTVariance time.Duration + CongestionWindow int + BytesInFlight int PacketsInFlight int } -type eventMTUUpdated struct { - mtu protocol.ByteCount - done bool -} - -func (e eventMTUUpdated) Category() category { return categoryRecovery } -func (e eventMTUUpdated) Name() string { return "mtu_updated" } -func (e eventMTUUpdated) IsNil() bool { return false } - -func (e eventMTUUpdated) MarshalJSONObject(enc *gojay.Encoder) { - enc.Uint64Key("mtu", uint64(e.mtu)) - enc.BoolKey("done", e.done) -} - -type eventMetricsUpdated struct { - Last *metrics - Current *metrics -} - -func (e eventMetricsUpdated) Category() category { return categoryRecovery } -func (e eventMetricsUpdated) Name() string { return "metrics_updated" } -func (e eventMetricsUpdated) IsNil() bool { return false } +func (e MetricsUpdated) Name() string { return "recovery:metrics_updated" } -func (e eventMetricsUpdated) MarshalJSONObject(enc *gojay.Encoder) { - if e.Last == nil || e.Last.MinRTT != e.Current.MinRTT { - enc.FloatKey("min_rtt", milliseconds(e.Current.MinRTT)) +func (e MetricsUpdated) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + if e.MinRTT != 0 { + h.WriteToken(jsontext.String("min_rtt")) + h.WriteToken(jsontext.Float(milliseconds(e.MinRTT))) } - if e.Last == nil || e.Last.SmoothedRTT != e.Current.SmoothedRTT { - enc.FloatKey("smoothed_rtt", milliseconds(e.Current.SmoothedRTT)) + if e.SmoothedRTT != 0 { + h.WriteToken(jsontext.String("smoothed_rtt")) + h.WriteToken(jsontext.Float(milliseconds(e.SmoothedRTT))) } - if e.Last == nil || e.Last.LatestRTT != e.Current.LatestRTT { - enc.FloatKey("latest_rtt", milliseconds(e.Current.LatestRTT)) + if e.LatestRTT != 0 { + h.WriteToken(jsontext.String("latest_rtt")) + h.WriteToken(jsontext.Float(milliseconds(e.LatestRTT))) } - if e.Last == nil || e.Last.RTTVariance != e.Current.RTTVariance { - enc.FloatKey("rtt_variance", milliseconds(e.Current.RTTVariance)) + if e.RTTVariance != 0 { + h.WriteToken(jsontext.String("rtt_variance")) + h.WriteToken(jsontext.Float(milliseconds(e.RTTVariance))) } - - if e.Last == nil || e.Last.CongestionWindow != e.Current.CongestionWindow { - enc.Uint64Key("congestion_window", uint64(e.Current.CongestionWindow)) + if e.CongestionWindow != 0 { + h.WriteToken(jsontext.String("congestion_window")) + h.WriteToken(jsontext.Uint(uint64(e.CongestionWindow))) } - if e.Last == nil || e.Last.BytesInFlight != e.Current.BytesInFlight { - enc.Uint64Key("bytes_in_flight", uint64(e.Current.BytesInFlight)) + if e.BytesInFlight != 0 { + h.WriteToken(jsontext.String("bytes_in_flight")) + h.WriteToken(jsontext.Uint(uint64(e.BytesInFlight))) } - if e.Last == nil || e.Last.PacketsInFlight != e.Current.PacketsInFlight { - enc.Uint64Key("packets_in_flight", uint64(e.Current.PacketsInFlight)) + if e.PacketsInFlight != 0 { + h.WriteToken(jsontext.String("packets_in_flight")) + h.WriteToken(jsontext.Uint(uint64(e.PacketsInFlight))) } + h.WriteToken(jsontext.EndObject) + return h.err } -type eventUpdatedPTO struct { - Value uint32 +// PTOCountUpdated logs the pto_count value of the +// recovery:metrics_updated event. +type PTOCountUpdated struct { + PTOCount uint32 } -func (e eventUpdatedPTO) Category() category { return categoryRecovery } -func (e eventUpdatedPTO) Name() string { return "metrics_updated" } -func (e eventUpdatedPTO) IsNil() bool { return false } +func (e PTOCountUpdated) Name() string { return "recovery:metrics_updated" } -func (e eventUpdatedPTO) MarshalJSONObject(enc *gojay.Encoder) { - enc.Uint32Key("pto_count", e.Value) +func (e PTOCountUpdated) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("pto_count")) + h.WriteToken(jsontext.Uint(uint64(e.PTOCount))) + h.WriteToken(jsontext.EndObject) + return h.err } -type eventPacketLost struct { - PacketType logging.PacketType - PacketNumber protocol.PacketNumber - Trigger packetLossReason +type PacketLost struct { + Header PacketHeader + Trigger PacketLossReason } -func (e eventPacketLost) Category() category { return categoryRecovery } -func (e eventPacketLost) Name() string { return "packet_lost" } -func (e eventPacketLost) IsNil() bool { return false } - -func (e eventPacketLost) MarshalJSONObject(enc *gojay.Encoder) { - enc.ObjectKey("header", packetHeaderWithTypeAndPacketNumber{ - PacketType: e.PacketType, - PacketNumber: e.PacketNumber, - }) - enc.StringKey("trigger", e.Trigger.String()) -} +func (e PacketLost) Name() string { return "recovery:packet_lost" } -type eventKeyUpdated struct { - Trigger keyUpdateTrigger - KeyType keyType - KeyPhase protocol.KeyPhase +func (e PacketLost) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("header")) + if err := e.Header.encode(enc); err != nil { + return err + } + h.WriteToken(jsontext.String("trigger")) + h.WriteToken(jsontext.String(string(e.Trigger))) + h.WriteToken(jsontext.EndObject) + return h.err +} + +type SpuriousLoss struct { + EncryptionLevel protocol.EncryptionLevel + PacketNumber protocol.PacketNumber + PacketReordering uint64 + TimeReordering time.Duration +} + +func (e SpuriousLoss) Name() string { return "recovery:spurious_loss" } + +func (e SpuriousLoss) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("packet_number_space")) + h.WriteToken(jsontext.String(encLevelToPacketNumberSpace(e.EncryptionLevel))) + h.WriteToken(jsontext.String("packet_number")) + h.WriteToken(jsontext.Uint(uint64(e.PacketNumber))) + h.WriteToken(jsontext.String("reordering_packets")) + h.WriteToken(jsontext.Uint(e.PacketReordering)) + h.WriteToken(jsontext.String("reordering_time")) + h.WriteToken(jsontext.Float(milliseconds(e.TimeReordering))) + h.WriteToken(jsontext.EndObject) + return h.err +} + +type KeyUpdated struct { + Trigger KeyUpdateTrigger + KeyType KeyType + KeyPhase KeyPhase // only set for 1-RTT keys // we don't log the keys here, so we don't need `old` and `new`. } -func (e eventKeyUpdated) Category() category { return categorySecurity } -func (e eventKeyUpdated) Name() string { return "key_updated" } -func (e eventKeyUpdated) IsNil() bool { return false } +func (e KeyUpdated) Name() string { return "security:key_updated" } -func (e eventKeyUpdated) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("trigger", e.Trigger.String()) - enc.StringKey("key_type", e.KeyType.String()) - if e.KeyType == keyTypeClient1RTT || e.KeyType == keyTypeServer1RTT { - enc.Uint64Key("key_phase", uint64(e.KeyPhase)) +func (e KeyUpdated) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("trigger")) + h.WriteToken(jsontext.String(string(e.Trigger))) + h.WriteToken(jsontext.String("key_type")) + h.WriteToken(jsontext.String(string(e.KeyType))) + if e.KeyType == KeyTypeClient1RTT || e.KeyType == KeyTypeServer1RTT { + h.WriteToken(jsontext.String("key_phase")) + h.WriteToken(jsontext.Uint(uint64(e.KeyPhase))) } + h.WriteToken(jsontext.EndObject) + return h.err } -type eventKeyDiscarded struct { - KeyType keyType - KeyPhase protocol.KeyPhase +type KeyDiscarded struct { + KeyType KeyType + KeyPhase KeyPhase // only set for 1-RTT keys } -func (e eventKeyDiscarded) Category() category { return categorySecurity } -func (e eventKeyDiscarded) Name() string { return "key_discarded" } -func (e eventKeyDiscarded) IsNil() bool { return false } +func (e KeyDiscarded) Name() string { return "security:key_discarded" } -func (e eventKeyDiscarded) MarshalJSONObject(enc *gojay.Encoder) { - if e.KeyType != keyTypeClient1RTT && e.KeyType != keyTypeServer1RTT { - enc.StringKey("trigger", "tls") +func (e KeyDiscarded) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + if e.KeyType != KeyTypeClient1RTT && e.KeyType != KeyTypeServer1RTT { + h.WriteToken(jsontext.String("trigger")) + h.WriteToken(jsontext.String("tls")) } - enc.StringKey("key_type", e.KeyType.String()) - if e.KeyType == keyTypeClient1RTT || e.KeyType == keyTypeServer1RTT { - enc.Uint64Key("key_phase", uint64(e.KeyPhase)) + h.WriteToken(jsontext.String("key_type")) + h.WriteToken(jsontext.String(string(e.KeyType))) + if e.KeyType == KeyTypeClient1RTT || e.KeyType == KeyTypeServer1RTT { + h.WriteToken(jsontext.String("key_phase")) + h.WriteToken(jsontext.Uint(uint64(e.KeyPhase))) } + h.WriteToken(jsontext.EndObject) + return h.err } -type eventTransportParameters struct { - Restore bool - Owner owner - SentBy protocol.Perspective - +type ParametersSet struct { + Restore bool + Initiator Initiator + SentBy protocol.Perspective OriginalDestinationConnectionID protocol.ConnectionID InitialSourceConnectionID protocol.ConnectionID RetrySourceConnectionID *protocol.ConnectionID - - StatelessResetToken *protocol.StatelessResetToken - DisableActiveMigration bool - MaxIdleTimeout time.Duration - MaxUDPPayloadSize protocol.ByteCount - AckDelayExponent uint8 - MaxAckDelay time.Duration - ActiveConnectionIDLimit uint64 - - InitialMaxData protocol.ByteCount - InitialMaxStreamDataBidiLocal protocol.ByteCount - InitialMaxStreamDataBidiRemote protocol.ByteCount - InitialMaxStreamDataUni protocol.ByteCount - InitialMaxStreamsBidi int64 - InitialMaxStreamsUni int64 - - PreferredAddress *preferredAddress - - MaxDatagramFrameSize protocol.ByteCount -} - -func (e eventTransportParameters) Category() category { return categoryTransport } -func (e eventTransportParameters) Name() string { + StatelessResetToken *protocol.StatelessResetToken + DisableActiveMigration bool + MaxIdleTimeout time.Duration + MaxUDPPayloadSize protocol.ByteCount + AckDelayExponent uint8 + MaxAckDelay time.Duration + ActiveConnectionIDLimit uint64 + InitialMaxData protocol.ByteCount + InitialMaxStreamDataBidiLocal protocol.ByteCount + InitialMaxStreamDataBidiRemote protocol.ByteCount + InitialMaxStreamDataUni protocol.ByteCount + InitialMaxStreamsBidi int64 + InitialMaxStreamsUni int64 + PreferredAddress *PreferredAddress + MaxDatagramFrameSize protocol.ByteCount + EnableResetStreamAt bool +} + +func (e ParametersSet) Name() string { if e.Restore { - return "parameters_restored" + return "transport:parameters_restored" } - return "parameters_set" + return "transport:parameters_set" } -func (e eventTransportParameters) IsNil() bool { return false } -func (e eventTransportParameters) MarshalJSONObject(enc *gojay.Encoder) { +func (e ParametersSet) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) if !e.Restore { - enc.StringKey("owner", e.Owner.String()) + h.WriteToken(jsontext.String("initiator")) + h.WriteToken(jsontext.String(string(e.Initiator))) if e.SentBy == protocol.PerspectiveServer { - enc.StringKey("original_destination_connection_id", e.OriginalDestinationConnectionID.String()) + h.WriteToken(jsontext.String("original_destination_connection_id")) + h.WriteToken(jsontext.String(e.OriginalDestinationConnectionID.String())) if e.StatelessResetToken != nil { - enc.StringKey("stateless_reset_token", fmt.Sprintf("%x", e.StatelessResetToken[:])) + h.WriteToken(jsontext.String("stateless_reset_token")) + h.WriteToken(jsontext.String(fmt.Sprintf("%x", e.StatelessResetToken[:]))) } if e.RetrySourceConnectionID != nil { - enc.StringKey("retry_source_connection_id", (*e.RetrySourceConnectionID).String()) + h.WriteToken(jsontext.String("retry_source_connection_id")) + h.WriteToken(jsontext.String((*e.RetrySourceConnectionID).String())) } } - enc.StringKey("initial_source_connection_id", e.InitialSourceConnectionID.String()) - } - enc.BoolKey("disable_active_migration", e.DisableActiveMigration) - enc.FloatKeyOmitEmpty("max_idle_timeout", milliseconds(e.MaxIdleTimeout)) - enc.Int64KeyNullEmpty("max_udp_payload_size", int64(e.MaxUDPPayloadSize)) - enc.Uint8KeyOmitEmpty("ack_delay_exponent", e.AckDelayExponent) - enc.FloatKeyOmitEmpty("max_ack_delay", milliseconds(e.MaxAckDelay)) - enc.Uint64KeyOmitEmpty("active_connection_id_limit", e.ActiveConnectionIDLimit) - - enc.Int64KeyOmitEmpty("initial_max_data", int64(e.InitialMaxData)) - enc.Int64KeyOmitEmpty("initial_max_stream_data_bidi_local", int64(e.InitialMaxStreamDataBidiLocal)) - enc.Int64KeyOmitEmpty("initial_max_stream_data_bidi_remote", int64(e.InitialMaxStreamDataBidiRemote)) - enc.Int64KeyOmitEmpty("initial_max_stream_data_uni", int64(e.InitialMaxStreamDataUni)) - enc.Int64KeyOmitEmpty("initial_max_streams_bidi", e.InitialMaxStreamsBidi) - enc.Int64KeyOmitEmpty("initial_max_streams_uni", e.InitialMaxStreamsUni) - + h.WriteToken(jsontext.String("initial_source_connection_id")) + h.WriteToken(jsontext.String(e.InitialSourceConnectionID.String())) + } + h.WriteToken(jsontext.String("disable_active_migration")) + h.WriteToken(jsontext.Bool(e.DisableActiveMigration)) + if e.MaxIdleTimeout != 0 { + h.WriteToken(jsontext.String("max_idle_timeout")) + h.WriteToken(jsontext.Float(milliseconds(e.MaxIdleTimeout))) + } + if e.MaxUDPPayloadSize != 0 { + h.WriteToken(jsontext.String("max_udp_payload_size")) + h.WriteToken(jsontext.Int(int64(e.MaxUDPPayloadSize))) + } + if e.AckDelayExponent != 0 { + h.WriteToken(jsontext.String("ack_delay_exponent")) + h.WriteToken(jsontext.Uint(uint64(e.AckDelayExponent))) + } + if e.MaxAckDelay != 0 { + h.WriteToken(jsontext.String("max_ack_delay")) + h.WriteToken(jsontext.Float(milliseconds(e.MaxAckDelay))) + } + if e.ActiveConnectionIDLimit != 0 { + h.WriteToken(jsontext.String("active_connection_id_limit")) + h.WriteToken(jsontext.Uint(e.ActiveConnectionIDLimit)) + } + if e.InitialMaxData != 0 { + h.WriteToken(jsontext.String("initial_max_data")) + h.WriteToken(jsontext.Int(int64(e.InitialMaxData))) + } + if e.InitialMaxStreamDataBidiLocal != 0 { + h.WriteToken(jsontext.String("initial_max_stream_data_bidi_local")) + h.WriteToken(jsontext.Int(int64(e.InitialMaxStreamDataBidiLocal))) + } + if e.InitialMaxStreamDataBidiRemote != 0 { + h.WriteToken(jsontext.String("initial_max_stream_data_bidi_remote")) + h.WriteToken(jsontext.Int(int64(e.InitialMaxStreamDataBidiRemote))) + } + if e.InitialMaxStreamDataUni != 0 { + h.WriteToken(jsontext.String("initial_max_stream_data_uni")) + h.WriteToken(jsontext.Int(int64(e.InitialMaxStreamDataUni))) + } + if e.InitialMaxStreamsBidi != 0 { + h.WriteToken(jsontext.String("initial_max_streams_bidi")) + h.WriteToken(jsontext.Int(e.InitialMaxStreamsBidi)) + } + if e.InitialMaxStreamsUni != 0 { + h.WriteToken(jsontext.String("initial_max_streams_uni")) + h.WriteToken(jsontext.Int(e.InitialMaxStreamsUni)) + } if e.PreferredAddress != nil { - enc.ObjectKey("preferred_address", e.PreferredAddress) + h.WriteToken(jsontext.String("preferred_address")) + if err := e.PreferredAddress.encode(enc); err != nil { + return err + } } if e.MaxDatagramFrameSize != protocol.InvalidByteCount { - enc.Int64Key("max_datagram_frame_size", int64(e.MaxDatagramFrameSize)) + h.WriteToken(jsontext.String("max_datagram_frame_size")) + h.WriteToken(jsontext.Int(int64(e.MaxDatagramFrameSize))) } + if e.EnableResetStreamAt { + h.WriteToken(jsontext.String("reset_stream_at")) + h.WriteToken(jsontext.True) + } + h.WriteToken(jsontext.EndObject) + return h.err } -type preferredAddress struct { +type PreferredAddress struct { IPv4, IPv6 netip.AddrPort ConnectionID protocol.ConnectionID StatelessResetToken protocol.StatelessResetToken } -var _ gojay.MarshalerJSONObject = &preferredAddress{} - -func (a preferredAddress) IsNil() bool { return false } -func (a preferredAddress) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("ip_v4", a.IPv4.Addr().String()) - enc.Uint16Key("port_v4", a.IPv4.Port()) - enc.StringKey("ip_v6", a.IPv6.Addr().String()) - enc.Uint16Key("port_v6", a.IPv6.Port()) - enc.StringKey("connection_id", a.ConnectionID.String()) - enc.StringKey("stateless_reset_token", fmt.Sprintf("%x", a.StatelessResetToken)) -} - -type eventLossTimerSet struct { - TimerType timerType - EncLevel protocol.EncryptionLevel - Delta time.Duration -} - -func (e eventLossTimerSet) Category() category { return categoryRecovery } -func (e eventLossTimerSet) Name() string { return "loss_timer_updated" } -func (e eventLossTimerSet) IsNil() bool { return false } - -func (e eventLossTimerSet) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("event_type", "set") - enc.StringKey("timer_type", e.TimerType.String()) - enc.StringKey("packet_number_space", encLevelToPacketNumberSpace(e.EncLevel)) - enc.Float64Key("delta", milliseconds(e.Delta)) -} - -type eventLossTimerExpired struct { - TimerType timerType - EncLevel protocol.EncryptionLevel -} - -func (e eventLossTimerExpired) Category() category { return categoryRecovery } -func (e eventLossTimerExpired) Name() string { return "loss_timer_updated" } -func (e eventLossTimerExpired) IsNil() bool { return false } - -func (e eventLossTimerExpired) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("event_type", "expired") - enc.StringKey("timer_type", e.TimerType.String()) - enc.StringKey("packet_number_space", encLevelToPacketNumberSpace(e.EncLevel)) +func (a PreferredAddress) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + if a.IPv4.IsValid() { + h.WriteToken(jsontext.String("ip_v4")) + h.WriteToken(jsontext.String(a.IPv4.Addr().String())) + h.WriteToken(jsontext.String("port_v4")) + h.WriteToken(jsontext.Uint(uint64(a.IPv4.Port()))) + } + if a.IPv6.IsValid() { + h.WriteToken(jsontext.String("ip_v6")) + h.WriteToken(jsontext.String(a.IPv6.Addr().String())) + h.WriteToken(jsontext.String("port_v6")) + h.WriteToken(jsontext.Uint(uint64(a.IPv6.Port()))) + } + h.WriteToken(jsontext.String("connection_id")) + h.WriteToken(jsontext.String(a.ConnectionID.String())) + h.WriteToken(jsontext.String("stateless_reset_token")) + h.WriteToken(jsontext.String(fmt.Sprintf("%x", a.StatelessResetToken))) + h.WriteToken(jsontext.EndObject) + return h.err +} + +type LossTimerUpdated struct { + Type LossTimerUpdateType + TimerType TimerType + EncLevel EncryptionLevel + Time time.Time +} + +func (e LossTimerUpdated) Name() string { return "recovery:loss_timer_updated" } + +func (e LossTimerUpdated) Encode(enc *jsontext.Encoder, t time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("event_type")) + h.WriteToken(jsontext.String(string(e.Type))) + h.WriteToken(jsontext.String("timer_type")) + h.WriteToken(jsontext.String(string(e.TimerType))) + h.WriteToken(jsontext.String("packet_number_space")) + h.WriteToken(jsontext.String(encLevelToPacketNumberSpace(e.EncLevel))) + if e.Type == LossTimerUpdateTypeSet { + h.WriteToken(jsontext.String("delta")) + h.WriteToken(jsontext.Float(milliseconds(e.Time.Sub(t)))) + } + h.WriteToken(jsontext.EndObject) + return h.err } type eventLossTimerCanceled struct{} -func (e eventLossTimerCanceled) Category() category { return categoryRecovery } -func (e eventLossTimerCanceled) Name() string { return "loss_timer_updated" } -func (e eventLossTimerCanceled) IsNil() bool { return false } +func (e eventLossTimerCanceled) Name() string { return "recovery:loss_timer_updated" } -func (e eventLossTimerCanceled) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("event_type", "cancelled") +func (e eventLossTimerCanceled) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("event_type")) + h.WriteToken(jsontext.String("cancelled")) + h.WriteToken(jsontext.EndObject) + return h.err } -type eventCongestionStateUpdated struct { - state congestionState +type CongestionStateUpdated struct { + State CongestionState } -func (e eventCongestionStateUpdated) Category() category { return categoryRecovery } -func (e eventCongestionStateUpdated) Name() string { return "congestion_state_updated" } -func (e eventCongestionStateUpdated) IsNil() bool { return false } +func (e CongestionStateUpdated) Name() string { return "recovery:congestion_state_updated" } -func (e eventCongestionStateUpdated) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("new", e.state.String()) +func (e CongestionStateUpdated) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("new")) + h.WriteToken(jsontext.String(e.State.String())) + h.WriteToken(jsontext.EndObject) + return h.err } -type eventECNStateUpdated struct { - state logging.ECNState - trigger logging.ECNStateTrigger +type ECNStateUpdated struct { + State ECNState + Trigger string } -func (e eventECNStateUpdated) Category() category { return categoryRecovery } -func (e eventECNStateUpdated) Name() string { return "ecn_state_updated" } -func (e eventECNStateUpdated) IsNil() bool { return false } +func (e ECNStateUpdated) Name() string { return "recovery:ecn_state_updated" } -func (e eventECNStateUpdated) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("new", ecnState(e.state).String()) - enc.StringKeyOmitEmpty("trigger", ecnStateTrigger(e.trigger).String()) -} - -type eventGeneric struct { - name string - msg string -} - -func (e eventGeneric) Category() category { return categoryTransport } -func (e eventGeneric) Name() string { return e.name } -func (e eventGeneric) IsNil() bool { return false } - -func (e eventGeneric) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("details", e.msg) +func (e ECNStateUpdated) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("new")) + h.WriteToken(jsontext.String(string(e.State))) + if e.Trigger != "" { + h.WriteToken(jsontext.String("trigger")) + h.WriteToken(jsontext.String(e.Trigger)) + } + h.WriteToken(jsontext.EndObject) + return h.err } -type eventALPNInformation struct { - chosenALPN string +type ALPNInformation struct { + ChosenALPN string } -func (e eventALPNInformation) Category() category { return categoryTransport } -func (e eventALPNInformation) Name() string { return "alpn_information" } -func (e eventALPNInformation) IsNil() bool { return false } +func (e ALPNInformation) Name() string { return "transport:alpn_information" } -func (e eventALPNInformation) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("chosen_alpn", e.chosenALPN) +func (e ALPNInformation) Encode(enc *jsontext.Encoder, _ time.Time) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("chosen_alpn")) + h.WriteToken(jsontext.String(e.ChosenALPN)) + h.WriteToken(jsontext.EndObject) + return h.err } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/frame.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/frame.go index 0d44f073b..b66fedc95 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/frame.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/frame.go @@ -1,227 +1,481 @@ package qlog import ( - "fmt" + "encoding/hex" "github.com/quic-go/quic-go/internal/wire" - "github.com/quic-go/quic-go/logging" - - "github.com/francoispqt/gojay" + "github.com/quic-go/quic-go/qlogwriter/jsontext" ) -type frame struct { - Frame logging.Frame +type Frame struct { + Frame any } -var _ gojay.MarshalerJSONObject = frame{} +type frames []Frame + +type ( + // An AckFrame is an ACK frame. + AckFrame = wire.AckFrame + // A ConnectionCloseFrame is a CONNECTION_CLOSE frame. + ConnectionCloseFrame = wire.ConnectionCloseFrame + // A DataBlockedFrame is a DATA_BLOCKED frame. + DataBlockedFrame = wire.DataBlockedFrame + // A HandshakeDoneFrame is a HANDSHAKE_DONE frame. + HandshakeDoneFrame = wire.HandshakeDoneFrame + // A MaxDataFrame is a MAX_DATA frame. + MaxDataFrame = wire.MaxDataFrame + // A MaxStreamDataFrame is a MAX_STREAM_DATA frame. + MaxStreamDataFrame = wire.MaxStreamDataFrame + // A MaxStreamsFrame is a MAX_STREAMS_FRAME. + MaxStreamsFrame = wire.MaxStreamsFrame + // A NewConnectionIDFrame is a NEW_CONNECTION_ID frame. + NewConnectionIDFrame = wire.NewConnectionIDFrame + // A NewTokenFrame is a NEW_TOKEN frame. + NewTokenFrame = wire.NewTokenFrame + // A PathChallengeFrame is a PATH_CHALLENGE frame. + PathChallengeFrame = wire.PathChallengeFrame + // A PathResponseFrame is a PATH_RESPONSE frame. + PathResponseFrame = wire.PathResponseFrame + // A PingFrame is a PING frame. + PingFrame = wire.PingFrame + // A ResetStreamFrame is a RESET_STREAM frame. + ResetStreamFrame = wire.ResetStreamFrame + // A RetireConnectionIDFrame is a RETIRE_CONNECTION_ID frame. + RetireConnectionIDFrame = wire.RetireConnectionIDFrame + // A StopSendingFrame is a STOP_SENDING frame. + StopSendingFrame = wire.StopSendingFrame + // A StreamsBlockedFrame is a STREAMS_BLOCKED frame. + StreamsBlockedFrame = wire.StreamsBlockedFrame + // A StreamDataBlockedFrame is a STREAM_DATA_BLOCKED frame. + StreamDataBlockedFrame = wire.StreamDataBlockedFrame + // An AckFrequencyFrame is an ACK_FREQUENCY frame. + AckFrequencyFrame = wire.AckFrequencyFrame + // An ImmediateAckFrame is an IMMEDIATE_ACK frame. + ImmediateAckFrame = wire.ImmediateAckFrame +) -var _ gojay.MarshalerJSONArray = frames{} +type AckRange = wire.AckRange -func (f frame) MarshalJSONObject(enc *gojay.Encoder) { - switch frame := f.Frame.(type) { - case *logging.PingFrame: - marshalPingFrame(enc, frame) - case *logging.AckFrame: - marshalAckFrame(enc, frame) - case *logging.ResetStreamFrame: - marshalResetStreamFrame(enc, frame) - case *logging.StopSendingFrame: - marshalStopSendingFrame(enc, frame) - case *logging.CryptoFrame: - marshalCryptoFrame(enc, frame) - case *logging.NewTokenFrame: - marshalNewTokenFrame(enc, frame) - case *logging.StreamFrame: - marshalStreamFrame(enc, frame) - case *logging.MaxDataFrame: - marshalMaxDataFrame(enc, frame) - case *logging.MaxStreamDataFrame: - marshalMaxStreamDataFrame(enc, frame) - case *logging.MaxStreamsFrame: - marshalMaxStreamsFrame(enc, frame) - case *logging.DataBlockedFrame: - marshalDataBlockedFrame(enc, frame) - case *logging.StreamDataBlockedFrame: - marshalStreamDataBlockedFrame(enc, frame) - case *logging.StreamsBlockedFrame: - marshalStreamsBlockedFrame(enc, frame) - case *logging.NewConnectionIDFrame: - marshalNewConnectionIDFrame(enc, frame) - case *logging.RetireConnectionIDFrame: - marshalRetireConnectionIDFrame(enc, frame) - case *logging.PathChallengeFrame: - marshalPathChallengeFrame(enc, frame) - case *logging.PathResponseFrame: - marshalPathResponseFrame(enc, frame) - case *logging.ConnectionCloseFrame: - marshalConnectionCloseFrame(enc, frame) - case *logging.HandshakeDoneFrame: - marshalHandshakeDoneFrame(enc, frame) - case *logging.DatagramFrame: - marshalDatagramFrame(enc, frame) - default: - panic("unknown frame type") - } +// A CryptoFrame is a CRYPTO frame. +type CryptoFrame struct { + Offset int64 + Length int64 } -func (f frame) IsNil() bool { return false } +// A StreamFrame is a STREAM frame. +type StreamFrame struct { + StreamID StreamID + Offset int64 + Length int64 + Fin bool +} -type frames []frame +// A DatagramFrame is a DATAGRAM frame. +type DatagramFrame struct { + Length int64 +} -func (fs frames) IsNil() bool { return fs == nil } -func (fs frames) MarshalJSONArray(enc *gojay.Encoder) { +func (fs frames) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginArray) for _, f := range fs { - enc.Object(f) + if err := f.Encode(enc); err != nil { + return err + } + } + h.WriteToken(jsontext.EndArray) + return h.err +} + +func (f Frame) Encode(enc *jsontext.Encoder) error { + switch frame := f.Frame.(type) { + case *PingFrame: + return encodePingFrame(enc, frame) + case *AckFrame: + return encodeAckFrame(enc, frame) + case *ResetStreamFrame: + return encodeResetStreamFrame(enc, frame) + case *StopSendingFrame: + return encodeStopSendingFrame(enc, frame) + case *CryptoFrame: + return encodeCryptoFrame(enc, frame) + case *NewTokenFrame: + return encodeNewTokenFrame(enc, frame) + case *StreamFrame: + return encodeStreamFrame(enc, frame) + case *MaxDataFrame: + return encodeMaxDataFrame(enc, frame) + case *MaxStreamDataFrame: + return encodeMaxStreamDataFrame(enc, frame) + case *MaxStreamsFrame: + return encodeMaxStreamsFrame(enc, frame) + case *DataBlockedFrame: + return encodeDataBlockedFrame(enc, frame) + case *StreamDataBlockedFrame: + return encodeStreamDataBlockedFrame(enc, frame) + case *StreamsBlockedFrame: + return encodeStreamsBlockedFrame(enc, frame) + case *NewConnectionIDFrame: + return encodeNewConnectionIDFrame(enc, frame) + case *RetireConnectionIDFrame: + return encodeRetireConnectionIDFrame(enc, frame) + case *PathChallengeFrame: + return encodePathChallengeFrame(enc, frame) + case *PathResponseFrame: + return encodePathResponseFrame(enc, frame) + case *ConnectionCloseFrame: + return encodeConnectionCloseFrame(enc, frame) + case *HandshakeDoneFrame: + return encodeHandshakeDoneFrame(enc, frame) + case *DatagramFrame: + return encodeDatagramFrame(enc, frame) + case *AckFrequencyFrame: + return encodeAckFrequencyFrame(enc, frame) + case *ImmediateAckFrame: + return encodeImmediateAckFrame(enc, frame) + default: + panic("unknown frame type") } } -func marshalPingFrame(enc *gojay.Encoder, _ *wire.PingFrame) { - enc.StringKey("frame_type", "ping") +func encodePingFrame(enc *jsontext.Encoder, _ *PingFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("ping")) + h.WriteToken(jsontext.EndObject) + return h.err } type ackRanges []wire.AckRange -func (ars ackRanges) MarshalJSONArray(enc *gojay.Encoder) { +func (ars ackRanges) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginArray) for _, r := range ars { - enc.Array(ackRange(r)) + if err := ackRange(r).encode(enc); err != nil { + return err + } } + h.WriteToken(jsontext.EndArray) + return h.err } -func (ars ackRanges) IsNil() bool { return false } - type ackRange wire.AckRange -func (ar ackRange) MarshalJSONArray(enc *gojay.Encoder) { - enc.AddInt64(int64(ar.Smallest)) +func (ar ackRange) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginArray) + h.WriteToken(jsontext.Int(int64(ar.Smallest))) if ar.Smallest != ar.Largest { - enc.AddInt64(int64(ar.Largest)) + h.WriteToken(jsontext.Int(int64(ar.Largest))) } + h.WriteToken(jsontext.EndArray) + return h.err } -func (ar ackRange) IsNil() bool { return false } - -func marshalAckFrame(enc *gojay.Encoder, f *logging.AckFrame) { - enc.StringKey("frame_type", "ack") - enc.FloatKeyOmitEmpty("ack_delay", milliseconds(f.DelayTime)) - enc.ArrayKey("acked_ranges", ackRanges(f.AckRanges)) - if hasECN := f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0; hasECN { - enc.Uint64Key("ect0", f.ECT0) - enc.Uint64Key("ect1", f.ECT1) - enc.Uint64Key("ce", f.ECNCE) +func encodeAckFrame(enc *jsontext.Encoder, f *AckFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("ack")) + if f.DelayTime > 0 { + h.WriteToken(jsontext.String("ack_delay")) + h.WriteToken(jsontext.Float(milliseconds(f.DelayTime))) + } + h.WriteToken(jsontext.String("acked_ranges")) + if err := ackRanges(f.AckRanges).encode(enc); err != nil { + return err + } + hasECN := f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0 + if hasECN { + h.WriteToken(jsontext.String("ect0")) + h.WriteToken(jsontext.Uint(f.ECT0)) + h.WriteToken(jsontext.String("ect1")) + h.WriteToken(jsontext.Uint(f.ECT1)) + h.WriteToken(jsontext.String("ce")) + h.WriteToken(jsontext.Uint(f.ECNCE)) } + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalResetStreamFrame(enc *gojay.Encoder, f *logging.ResetStreamFrame) { - enc.StringKey("frame_type", "reset_stream") - enc.Int64Key("stream_id", int64(f.StreamID)) - enc.Int64Key("error_code", int64(f.ErrorCode)) - enc.Int64Key("final_size", int64(f.FinalSize)) +func encodeResetStreamFrame(enc *jsontext.Encoder, f *ResetStreamFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + if f.ReliableSize > 0 { + h.WriteToken(jsontext.String("reset_stream_at")) + } else { + h.WriteToken(jsontext.String("reset_stream")) + } + h.WriteToken(jsontext.String("stream_id")) + h.WriteToken(jsontext.Int(int64(f.StreamID))) + h.WriteToken(jsontext.String("error_code")) + h.WriteToken(jsontext.Int(int64(f.ErrorCode))) + h.WriteToken(jsontext.String("final_size")) + h.WriteToken(jsontext.Int(int64(f.FinalSize))) + if f.ReliableSize > 0 { + h.WriteToken(jsontext.String("reliable_size")) + h.WriteToken(jsontext.Int(int64(f.ReliableSize))) + } + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalStopSendingFrame(enc *gojay.Encoder, f *logging.StopSendingFrame) { - enc.StringKey("frame_type", "stop_sending") - enc.Int64Key("stream_id", int64(f.StreamID)) - enc.Int64Key("error_code", int64(f.ErrorCode)) +func encodeStopSendingFrame(enc *jsontext.Encoder, f *StopSendingFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("stop_sending")) + h.WriteToken(jsontext.String("stream_id")) + h.WriteToken(jsontext.Int(int64(f.StreamID))) + h.WriteToken(jsontext.String("error_code")) + h.WriteToken(jsontext.Int(int64(f.ErrorCode))) + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalCryptoFrame(enc *gojay.Encoder, f *logging.CryptoFrame) { - enc.StringKey("frame_type", "crypto") - enc.Int64Key("offset", int64(f.Offset)) - enc.Int64Key("length", int64(f.Length)) +func encodeCryptoFrame(enc *jsontext.Encoder, f *CryptoFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("crypto")) + h.WriteToken(jsontext.String("offset")) + h.WriteToken(jsontext.Int(f.Offset)) + h.WriteToken(jsontext.String("length")) + h.WriteToken(jsontext.Int(f.Length)) + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalNewTokenFrame(enc *gojay.Encoder, f *logging.NewTokenFrame) { - enc.StringKey("frame_type", "new_token") - enc.ObjectKey("token", &token{Raw: f.Token}) +func encodeNewTokenFrame(enc *jsontext.Encoder, f *NewTokenFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("new_token")) + h.WriteToken(jsontext.String("token")) + if err := (Token{Raw: f.Token}).encode(enc); err != nil { + return err + } + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalStreamFrame(enc *gojay.Encoder, f *logging.StreamFrame) { - enc.StringKey("frame_type", "stream") - enc.Int64Key("stream_id", int64(f.StreamID)) - enc.Int64Key("offset", int64(f.Offset)) - enc.IntKey("length", int(f.Length)) - enc.BoolKeyOmitEmpty("fin", f.Fin) +func encodeStreamFrame(enc *jsontext.Encoder, f *StreamFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("stream")) + h.WriteToken(jsontext.String("stream_id")) + h.WriteToken(jsontext.Int(int64(f.StreamID))) + h.WriteToken(jsontext.String("offset")) + h.WriteToken(jsontext.Int(f.Offset)) + h.WriteToken(jsontext.String("length")) + h.WriteToken(jsontext.Int(f.Length)) + if f.Fin { + h.WriteToken(jsontext.String("fin")) + h.WriteToken(jsontext.True) + } + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalMaxDataFrame(enc *gojay.Encoder, f *logging.MaxDataFrame) { - enc.StringKey("frame_type", "max_data") - enc.Int64Key("maximum", int64(f.MaximumData)) +func encodeMaxDataFrame(enc *jsontext.Encoder, f *MaxDataFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("max_data")) + h.WriteToken(jsontext.String("maximum")) + h.WriteToken(jsontext.Int(int64(f.MaximumData))) + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalMaxStreamDataFrame(enc *gojay.Encoder, f *logging.MaxStreamDataFrame) { - enc.StringKey("frame_type", "max_stream_data") - enc.Int64Key("stream_id", int64(f.StreamID)) - enc.Int64Key("maximum", int64(f.MaximumStreamData)) +func encodeMaxStreamDataFrame(enc *jsontext.Encoder, f *MaxStreamDataFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("max_stream_data")) + h.WriteToken(jsontext.String("stream_id")) + h.WriteToken(jsontext.Int(int64(f.StreamID))) + h.WriteToken(jsontext.String("maximum")) + h.WriteToken(jsontext.Int(int64(f.MaximumStreamData))) + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalMaxStreamsFrame(enc *gojay.Encoder, f *logging.MaxStreamsFrame) { - enc.StringKey("frame_type", "max_streams") - enc.StringKey("stream_type", streamType(f.Type).String()) - enc.Int64Key("maximum", int64(f.MaxStreamNum)) +func encodeMaxStreamsFrame(enc *jsontext.Encoder, f *MaxStreamsFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("max_streams")) + h.WriteToken(jsontext.String("stream_type")) + h.WriteToken(jsontext.String(streamType(f.Type).String())) + h.WriteToken(jsontext.String("maximum")) + h.WriteToken(jsontext.Int(int64(f.MaxStreamNum))) + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalDataBlockedFrame(enc *gojay.Encoder, f *logging.DataBlockedFrame) { - enc.StringKey("frame_type", "data_blocked") - enc.Int64Key("limit", int64(f.MaximumData)) +func encodeDataBlockedFrame(enc *jsontext.Encoder, f *DataBlockedFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("data_blocked")) + h.WriteToken(jsontext.String("limit")) + h.WriteToken(jsontext.Int(int64(f.MaximumData))) + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalStreamDataBlockedFrame(enc *gojay.Encoder, f *logging.StreamDataBlockedFrame) { - enc.StringKey("frame_type", "stream_data_blocked") - enc.Int64Key("stream_id", int64(f.StreamID)) - enc.Int64Key("limit", int64(f.MaximumStreamData)) +func encodeStreamDataBlockedFrame(enc *jsontext.Encoder, f *StreamDataBlockedFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("stream_data_blocked")) + h.WriteToken(jsontext.String("stream_id")) + h.WriteToken(jsontext.Int(int64(f.StreamID))) + h.WriteToken(jsontext.String("limit")) + h.WriteToken(jsontext.Int(int64(f.MaximumStreamData))) + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalStreamsBlockedFrame(enc *gojay.Encoder, f *logging.StreamsBlockedFrame) { - enc.StringKey("frame_type", "streams_blocked") - enc.StringKey("stream_type", streamType(f.Type).String()) - enc.Int64Key("limit", int64(f.StreamLimit)) +func encodeStreamsBlockedFrame(enc *jsontext.Encoder, f *StreamsBlockedFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("streams_blocked")) + h.WriteToken(jsontext.String("stream_type")) + h.WriteToken(jsontext.String(streamType(f.Type).String())) + h.WriteToken(jsontext.String("limit")) + h.WriteToken(jsontext.Int(int64(f.StreamLimit))) + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalNewConnectionIDFrame(enc *gojay.Encoder, f *logging.NewConnectionIDFrame) { - enc.StringKey("frame_type", "new_connection_id") - enc.Int64Key("sequence_number", int64(f.SequenceNumber)) - enc.Int64Key("retire_prior_to", int64(f.RetirePriorTo)) - enc.IntKey("length", f.ConnectionID.Len()) - enc.StringKey("connection_id", f.ConnectionID.String()) - enc.StringKey("stateless_reset_token", fmt.Sprintf("%x", f.StatelessResetToken)) +func encodeNewConnectionIDFrame(enc *jsontext.Encoder, f *NewConnectionIDFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("new_connection_id")) + h.WriteToken(jsontext.String("sequence_number")) + h.WriteToken(jsontext.Uint(f.SequenceNumber)) + h.WriteToken(jsontext.String("retire_prior_to")) + h.WriteToken(jsontext.Uint(f.RetirePriorTo)) + h.WriteToken(jsontext.String("length")) + h.WriteToken(jsontext.Int(int64(f.ConnectionID.Len()))) + h.WriteToken(jsontext.String("connection_id")) + h.WriteToken(jsontext.String(f.ConnectionID.String())) + h.WriteToken(jsontext.String("stateless_reset_token")) + h.WriteToken(jsontext.String(hex.EncodeToString(f.StatelessResetToken[:]))) + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalRetireConnectionIDFrame(enc *gojay.Encoder, f *logging.RetireConnectionIDFrame) { - enc.StringKey("frame_type", "retire_connection_id") - enc.Int64Key("sequence_number", int64(f.SequenceNumber)) +func encodeRetireConnectionIDFrame(enc *jsontext.Encoder, f *RetireConnectionIDFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("retire_connection_id")) + h.WriteToken(jsontext.String("sequence_number")) + h.WriteToken(jsontext.Uint(f.SequenceNumber)) + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalPathChallengeFrame(enc *gojay.Encoder, f *logging.PathChallengeFrame) { - enc.StringKey("frame_type", "path_challenge") - enc.StringKey("data", fmt.Sprintf("%x", f.Data[:])) +func encodePathChallengeFrame(enc *jsontext.Encoder, f *PathChallengeFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("path_challenge")) + h.WriteToken(jsontext.String("data")) + h.WriteToken(jsontext.String(hex.EncodeToString(f.Data[:]))) + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalPathResponseFrame(enc *gojay.Encoder, f *logging.PathResponseFrame) { - enc.StringKey("frame_type", "path_response") - enc.StringKey("data", fmt.Sprintf("%x", f.Data[:])) +func encodePathResponseFrame(enc *jsontext.Encoder, f *PathResponseFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("path_response")) + h.WriteToken(jsontext.String("data")) + h.WriteToken(jsontext.String(hex.EncodeToString(f.Data[:]))) + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalConnectionCloseFrame(enc *gojay.Encoder, f *logging.ConnectionCloseFrame) { +func encodeConnectionCloseFrame(enc *jsontext.Encoder, f *ConnectionCloseFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("connection_close")) + h.WriteToken(jsontext.String("error_space")) errorSpace := "transport" if f.IsApplicationError { errorSpace = "application" } - enc.StringKey("frame_type", "connection_close") - enc.StringKey("error_space", errorSpace) - if errName := transportError(f.ErrorCode).String(); len(errName) > 0 { - enc.StringKey("error_code", errName) + h.WriteToken(jsontext.String(errorSpace)) + errName := transportError(f.ErrorCode).String() + if len(errName) > 0 { + h.WriteToken(jsontext.String("error_code")) + h.WriteToken(jsontext.String(errName)) } else { - enc.Uint64Key("error_code", f.ErrorCode) + h.WriteToken(jsontext.String("error_code")) + h.WriteToken(jsontext.Uint(f.ErrorCode)) } - enc.Uint64Key("raw_error_code", f.ErrorCode) - enc.StringKey("reason", f.ReasonPhrase) + h.WriteToken(jsontext.String("raw_error_code")) + h.WriteToken(jsontext.Uint(f.ErrorCode)) + h.WriteToken(jsontext.String("reason")) + h.WriteToken(jsontext.String(f.ReasonPhrase)) + h.WriteToken(jsontext.EndObject) + return h.err +} + +func encodeHandshakeDoneFrame(enc *jsontext.Encoder, _ *HandshakeDoneFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("handshake_done")) + h.WriteToken(jsontext.EndObject) + return h.err +} + +func encodeDatagramFrame(enc *jsontext.Encoder, f *DatagramFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("datagram")) + h.WriteToken(jsontext.String("length")) + h.WriteToken(jsontext.Int(f.Length)) + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalHandshakeDoneFrame(enc *gojay.Encoder, _ *logging.HandshakeDoneFrame) { - enc.StringKey("frame_type", "handshake_done") +func encodeAckFrequencyFrame(enc *jsontext.Encoder, f *AckFrequencyFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("ack_frequency")) + h.WriteToken(jsontext.String("sequence_number")) + h.WriteToken(jsontext.Uint(f.SequenceNumber)) + h.WriteToken(jsontext.String("ack_eliciting_threshold")) + h.WriteToken(jsontext.Uint(f.AckElicitingThreshold)) + h.WriteToken(jsontext.String("request_max_ack_delay")) + h.WriteToken(jsontext.Float(milliseconds(f.RequestMaxAckDelay))) + h.WriteToken(jsontext.String("reordering_threshold")) + h.WriteToken(jsontext.Int(int64(f.ReorderingThreshold))) + h.WriteToken(jsontext.EndObject) + return h.err } -func marshalDatagramFrame(enc *gojay.Encoder, f *logging.DatagramFrame) { - enc.StringKey("frame_type", "datagram") - enc.Int64Key("length", int64(f.Length)) +func encodeImmediateAckFrame(enc *jsontext.Encoder, _ *ImmediateAckFrame) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("frame_type")) + h.WriteToken(jsontext.String("immediate_ack")) + h.WriteToken(jsontext.EndObject) + return h.err } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/packet_header.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/packet_header.go index d2c57ba60..149ebcb6a 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/packet_header.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/packet_header.go @@ -1,161 +1,96 @@ package qlog import ( - "fmt" + "encoding/hex" "github.com/quic-go/quic-go/internal/protocol" - "github.com/quic-go/quic-go/logging" - - "github.com/francoispqt/gojay" + "github.com/quic-go/quic-go/qlogwriter/jsontext" ) -func getPacketTypeFromEncryptionLevel(encLevel protocol.EncryptionLevel) logging.PacketType { - switch encLevel { - case protocol.EncryptionInitial: - return logging.PacketTypeInitial - case protocol.EncryptionHandshake: - return logging.PacketTypeHandshake - case protocol.Encryption0RTT: - return logging.PacketType0RTT - case protocol.Encryption1RTT: - return logging.PacketType1RTT - default: - panic("unknown encryption level") - } -} - -type token struct { +type Token struct { Raw []byte } -var _ gojay.MarshalerJSONObject = &token{} - -func (t token) IsNil() bool { return false } -func (t token) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("data", fmt.Sprintf("%x", t.Raw)) +func (t Token) encode(enc *jsontext.Encoder) error { + h := encoderHelper{enc: enc} + h.WriteToken(jsontext.BeginObject) + h.WriteToken(jsontext.String("data")) + h.WriteToken(jsontext.String(hex.EncodeToString(t.Raw))) + h.WriteToken(jsontext.EndObject) + return h.err } // PacketHeader is a QUIC packet header. -// TODO: make this a long header -type packetHeader struct { - PacketType logging.PacketType - - KeyPhaseBit logging.KeyPhaseBit - PacketNumber logging.PacketNumber - - Version logging.Version - SrcConnectionID logging.ConnectionID - DestConnectionID logging.ConnectionID - - Token *token -} - -func transformHeader(hdr *logging.Header) *packetHeader { - h := &packetHeader{ - PacketType: logging.PacketTypeFromHeader(hdr), - SrcConnectionID: hdr.SrcConnectionID, - DestConnectionID: hdr.DestConnectionID, - Version: hdr.Version, - } - if len(hdr.Token) > 0 { - h.Token = &token{Raw: hdr.Token} - } - return h -} - -func transformLongHeader(hdr *logging.ExtendedHeader) *packetHeader { - h := transformHeader(&hdr.Header) - h.PacketNumber = hdr.PacketNumber - h.KeyPhaseBit = hdr.KeyPhase - return h -} - -func (h packetHeader) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("packet_type", packetType(h.PacketType).String()) - if h.PacketType != logging.PacketTypeRetry { - enc.Int64Key("packet_number", int64(h.PacketNumber)) +type PacketHeader struct { + PacketType PacketType + KeyPhaseBit KeyPhaseBit + PacketNumber PacketNumber + Version Version + SrcConnectionID ConnectionID + DestConnectionID ConnectionID + Token *Token +} + +func (h PacketHeader) encode(enc *jsontext.Encoder) error { + helper := encoderHelper{enc: enc} + helper.WriteToken(jsontext.BeginObject) + helper.WriteToken(jsontext.String("packet_type")) + helper.WriteToken(jsontext.String(string(h.PacketType))) + if h.PacketType != PacketTypeRetry && h.PacketType != PacketTypeVersionNegotiation && h.PacketType != "" && + h.PacketNumber != protocol.InvalidPacketNumber { + helper.WriteToken(jsontext.String("packet_number")) + helper.WriteToken(jsontext.Int(int64(h.PacketNumber))) } if h.Version != 0 { - enc.StringKey("version", version(h.Version).String()) + helper.WriteToken(jsontext.String("version")) + helper.WriteToken(jsontext.String(version(h.Version).String())) } - if h.PacketType != logging.PacketType1RTT { - enc.IntKey("scil", h.SrcConnectionID.Len()) + if h.PacketType != PacketType1RTT { + helper.WriteToken(jsontext.String("scil")) + helper.WriteToken(jsontext.Int(int64(h.SrcConnectionID.Len()))) if h.SrcConnectionID.Len() > 0 { - enc.StringKey("scid", h.SrcConnectionID.String()) + helper.WriteToken(jsontext.String("scid")) + helper.WriteToken(jsontext.String(h.SrcConnectionID.String())) } } - enc.IntKey("dcil", h.DestConnectionID.Len()) + helper.WriteToken(jsontext.String("dcil")) + helper.WriteToken(jsontext.Int(int64(h.DestConnectionID.Len()))) if h.DestConnectionID.Len() > 0 { - enc.StringKey("dcid", h.DestConnectionID.String()) + helper.WriteToken(jsontext.String("dcid")) + helper.WriteToken(jsontext.String(h.DestConnectionID.String())) } - if h.KeyPhaseBit == logging.KeyPhaseZero || h.KeyPhaseBit == logging.KeyPhaseOne { - enc.StringKey("key_phase_bit", h.KeyPhaseBit.String()) + if h.KeyPhaseBit == KeyPhaseZero || h.KeyPhaseBit == KeyPhaseOne { + helper.WriteToken(jsontext.String("key_phase_bit")) + helper.WriteToken(jsontext.String(h.KeyPhaseBit.String())) } if h.Token != nil { - enc.ObjectKey("token", h.Token) - } -} - -type packetHeaderVersionNegotiation struct { - SrcConnectionID logging.ArbitraryLenConnectionID - DestConnectionID logging.ArbitraryLenConnectionID -} - -func (h packetHeaderVersionNegotiation) IsNil() bool { return false } -func (h packetHeaderVersionNegotiation) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("packet_type", "version_negotiation") - enc.IntKey("scil", h.SrcConnectionID.Len()) - enc.StringKey("scid", h.SrcConnectionID.String()) - enc.IntKey("dcil", h.DestConnectionID.Len()) - enc.StringKey("dcid", h.DestConnectionID.String()) -} - -// a minimal header that only outputs the packet type, and potentially a packet number -type packetHeaderWithType struct { - PacketType logging.PacketType - PacketNumber logging.PacketNumber -} - -func (h packetHeaderWithType) IsNil() bool { return false } -func (h packetHeaderWithType) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("packet_type", packetType(h.PacketType).String()) - if h.PacketNumber != protocol.InvalidPacketNumber { - enc.Int64Key("packet_number", int64(h.PacketNumber)) - } -} - -// a minimal header that only outputs the packet type -type packetHeaderWithTypeAndPacketNumber struct { - PacketType logging.PacketType - PacketNumber logging.PacketNumber -} - -func (h packetHeaderWithTypeAndPacketNumber) IsNil() bool { return false } -func (h packetHeaderWithTypeAndPacketNumber) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("packet_type", packetType(h.PacketType).String()) - enc.Int64Key("packet_number", int64(h.PacketNumber)) -} - -type shortHeader struct { - DestConnectionID logging.ConnectionID - PacketNumber logging.PacketNumber - KeyPhaseBit logging.KeyPhaseBit -} - -func transformShortHeader(hdr *logging.ShortHeader) *shortHeader { - return &shortHeader{ - DestConnectionID: hdr.DestConnectionID, - PacketNumber: hdr.PacketNumber, - KeyPhaseBit: hdr.KeyPhase, - } -} - -func (h shortHeader) IsNil() bool { return false } -func (h shortHeader) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("packet_type", packetType(logging.PacketType1RTT).String()) - if h.DestConnectionID.Len() > 0 { - enc.StringKey("dcid", h.DestConnectionID.String()) + helper.WriteToken(jsontext.String("token")) + if err := h.Token.encode(enc); err != nil { + return err + } } - enc.Int64Key("packet_number", int64(h.PacketNumber)) - enc.StringKey("key_phase_bit", h.KeyPhaseBit.String()) + helper.WriteToken(jsontext.EndObject) + return helper.err +} + +type PacketHeaderVersionNegotiation struct { + SrcConnectionID ArbitraryLenConnectionID + DestConnectionID ArbitraryLenConnectionID +} + +func (h PacketHeaderVersionNegotiation) encode(enc *jsontext.Encoder) error { + helper := encoderHelper{enc: enc} + helper.WriteToken(jsontext.BeginObject) + helper.WriteToken(jsontext.String("packet_type")) + helper.WriteToken(jsontext.String("version_negotiation")) + helper.WriteToken(jsontext.String("scil")) + helper.WriteToken(jsontext.Int(int64(h.SrcConnectionID.Len()))) + helper.WriteToken(jsontext.String("scid")) + helper.WriteToken(jsontext.String(h.SrcConnectionID.String())) + helper.WriteToken(jsontext.String("dcil")) + helper.WriteToken(jsontext.Int(int64(h.DestConnectionID.Len()))) + helper.WriteToken(jsontext.String("dcid")) + helper.WriteToken(jsontext.String(h.DestConnectionID.String())) + helper.WriteToken(jsontext.EndObject) + return helper.err } diff --git a/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/qlog_dir.go b/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/qlog_dir.go index 162581c59..83bb72b3f 100644 --- a/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/qlog_dir.go +++ b/plugins/traefik/vendor/github.com/quic-go/quic-go/qlog/qlog_dir.go @@ -6,30 +6,31 @@ import ( "fmt" "log" "os" + "slices" "strings" "github.com/quic-go/quic-go/internal/utils" - "github.com/quic-go/quic-go/logging" + "github.com/quic-go/quic-go/qlogwriter" ) +// EventSchema is the qlog event schema for QUIC +const EventSchema = "urn:ietf:params:qlog:events:quic-12" + // DefaultConnectionTracer creates a qlog file in the qlog directory specified by the QLOGDIR environment variable. // File names are _.sqlog. // Returns nil if QLOGDIR is not set. -func DefaultConnectionTracer(_ context.Context, p logging.Perspective, connID logging.ConnectionID) *logging.ConnectionTracer { - var label string - switch p { - case logging.PerspectiveClient: - label = "client" - case logging.PerspectiveServer: - label = "server" +func DefaultConnectionTracer(_ context.Context, isClient bool, connID ConnectionID) qlogwriter.Trace { + return defaultConnectionTracerWithSchemas(isClient, connID, []string{EventSchema}) +} + +func DefaultConnectionTracerWithSchemas(_ context.Context, isClient bool, connID ConnectionID, eventSchemas []string) qlogwriter.Trace { + if !slices.Contains(eventSchemas, EventSchema) { + eventSchemas = append([]string{EventSchema}, eventSchemas...) } - return qlogDirTracer(p, connID, label) + return defaultConnectionTracerWithSchemas(isClient, connID, eventSchemas) } -// qlogDirTracer creates a qlog file in the qlog directory specified by the QLOGDIR environment variable. -// File names are _