feat(pr3): canonical wire-order header capture for h1/h2 + H3App for SETTINGS
- Renames caddy.listeners.decnet_h2fp → decnet_fp; adds h1 raw-byte header capture (plainTappingConn) and h2 continuous HPACK decode loop (parseH2HeadersLoop) so headers_ordered reflects actual wire order, not Go map iteration order. - Adds H3App Caddy module (decnet_h3) that owns UDP/443 via quic-go, wraps accepted QUIC connections with h3SettingsTappingConn to intercept the h3 control stream and extract RFC 9114 SETTINGS in wire order. - Wires access_log emission from FPHandler.ServeHTTP via responseCapture. - Updates syslog_bridge.py (canonical + per-service copies) with inline _compute_ja4h and new fp socket record branches: http_request_headers, h3_settings, access_log. - Fixes ingester proto field alias (bridge emits 'proto', ingester expected 'protocol') and exposes _process_fingerprint_bounties test alias. - Go tests: h1/h2/h3 golden-byte tests all green; h3_tracer_test covers varint parser, GREASE detection, truncated-stream safety. - Python tests: 15/15 green across bridge JA4H hash parity, ingester compat (old + new event shapes), and Caddyfile h3 template assertions.
This commit is contained in:
116
decnet/templates/https/_caddy_modules/decnetfp/go.mod
Normal file
116
decnet/templates/https/_caddy_modules/decnetfp/go.mod
Normal file
@@ -0,0 +1,116 @@
|
||||
module github.com/decnet/caddy-fp
|
||||
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/caddyserver/caddy/v2 v2.8.4
|
||||
github.com/quic-go/quic-go v0.44.0
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/net v0.27.0
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/caddyserver/certmagic v0.21.3 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/dgraph-io/badger v1.6.2 // indirect
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.0 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 // 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/golang/glog v1.2.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/cel-go v0.20.1 // indirect
|
||||
github.com/google/pprof v0.0.0-20231212022811-ec68065c825e // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/huandu/xstrings v1.3.3 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.14.3 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgtype v1.14.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.18.3 // indirect
|
||||
github.com/klauspost/compress v1.17.8 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/libdns/libdns v0.2.2 // indirect
|
||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mholt/acmez/v2 v2.0.1 // indirect
|
||||
github.com/miekg/dns v1.1.59 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.13.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.19.1 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/rs/xid v1.5.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/slackhq/nebula v1.6.1 // indirect
|
||||
github.com/smallstep/certificates v0.26.1 // indirect
|
||||
github.com/smallstep/nosql v0.6.1 // indirect
|
||||
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81 // indirect
|
||||
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d // indirect
|
||||
github.com/smallstep/truststore v0.13.0 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/cobra v1.8.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stoewer/go-strcase v1.2.0 // indirect
|
||||
github.com/tailscale/tscert v0.0.0-20240517230440-bbccfbf48933 // indirect
|
||||
github.com/urfave/cli v1.22.14 // indirect
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
go.etcd.io/bbolt v1.3.9 // indirect
|
||||
go.step.sm/cli-utils v0.9.0 // indirect
|
||||
go.step.sm/crypto v0.45.0 // indirect
|
||||
go.step.sm/linkedca v0.20.1 // indirect
|
||||
go.uber.org/automaxprocs v1.5.3 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap/exp v0.2.0 // indirect
|
||||
golang.org/x/crypto v0.25.0 // indirect
|
||||
golang.org/x/crypto/x509roots/fallback v0.0.0-20240507223354-67b13616a595 // indirect
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/term v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6 // indirect
|
||||
google.golang.org/grpc v1.63.2 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
howett.net/plist v1.0.0 // indirect
|
||||
)
|
||||
592
decnet/templates/https/_caddy_modules/decnetfp/go.sum
Normal file
592
decnet/templates/https/_caddy_modules/decnetfp/go.sum
Normal file
@@ -0,0 +1,592 @@
|
||||
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=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
|
||||
cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg=
|
||||
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
cloud.google.com/go/iam v1.1.8 h1:r7umDwhj+BQyz0ScZMp4QrGXjSTI3ZINnpgU2nlB/K0=
|
||||
cloud.google.com/go/iam v1.1.8/go.mod h1:GvE6lyMmfxXauzNq8NbgJbeVQNspG+tcdL/W8QO1+zE=
|
||||
cloud.google.com/go/kms v1.16.0 h1:1yZsRPhmargZOmY+fVAh8IKiR9HzCb0U1zsxb5g2nRY=
|
||||
cloud.google.com/go/kms v1.16.0/go.mod h1:olQUXy2Xud+1GzYfiBO9N0RhjsJk5IJLU6n/ethLXVc=
|
||||
cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU=
|
||||
cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
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=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
|
||||
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
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=
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw=
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA=
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.13 h1:WbKW8hOzrWoOA/+35S5okqO/2Ap8hkkFUzoW8Hzq24A=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.13/go.mod h1:XLiyiTMnguytjRER7u5RIkhIqS8Nyz41SwAWb4xEjxs=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.13 h1:XDCJDzk/u5cN7Aple7D/MiAhx1Rjo/0nueJ0La8mRuE=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.13/go.mod h1:FMNcjQrmuBYvOTZDtOLCIu0esmxjF7RuA/89iSXWzQI=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 h1:ogRAwT1/gxJBcSWDMZlgyFUM962F51A5CRhDLbxLdmo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7/go.mod h1:YCsIZhXfRPLFFCl5xxY+1T9RKzOKjCut+28JSX2DnAk=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.31.1 h1:5wtyAwuUiJiM3DHYeGZmP5iMonM7DFBWAEaaVPHYZA0=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.31.1/go.mod h1:2snWQJQUKsbN66vAawJuOGX7dr37pfOq9hb0tZDGIqQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.6 h1:o5cTaeunSpfXiLTIBx5xo2enQmiChtu1IBbzXnfU9Hs=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.6/go.mod h1:qGzynb/msuZIE8I75DVRCUXw3o3ZyBmUvMwQ2t/BrGM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.0 h1:Qe0r0lVURDDeBQJ4yP+BOrJkvkiCo/3FH/t+wY11dmw=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.0/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.7 h1:et3Ta53gotFR4ERLXXHIHl/Uuk1qYpP5uU7cvNql8ns=
|
||||
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 v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/caddyserver/caddy/v2 v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=
|
||||
github.com/caddyserver/caddy/v2 v2.8.4/go.mod h1:vmDAHp3d05JIvuhc24LmnxVlsZmWnUwbP5WMjzcMPWw=
|
||||
github.com/caddyserver/certmagic v0.21.3 h1:pqRRry3yuB4CWBVq9+cUqu+Y6E2z8TswbhNx1AZeYm0=
|
||||
github.com/caddyserver/certmagic v0.21.3/go.mod h1:Zq6pklO9nVRl3DIFUw9gVUfXKdpc/0qwTUAQMBlfgtI=
|
||||
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
|
||||
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
|
||||
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
|
||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
||||
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/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
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-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=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
|
||||
github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
|
||||
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
|
||||
github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||
github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU=
|
||||
github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
|
||||
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
|
||||
github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
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/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 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.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84=
|
||||
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg=
|
||||
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.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/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/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=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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 v2.12.4 h1:9gWcmF85Wvq4ryPFvGFaOgPIs1AQX0d0bcbGw4Z96qg=
|
||||
github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
|
||||
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||
github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
|
||||
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
|
||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
|
||||
github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||
github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw=
|
||||
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||
github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA=
|
||||
github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
|
||||
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/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
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.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
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 v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
|
||||
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
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=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
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/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/v2 v2.0.1 h1:3/3N0u1pLjMK4sNEAFSI+bcvzbPhRpY383sy1kLHJ6k=
|
||||
github.com/mholt/acmez/v2 v2.0.1/go.mod h1:fX4c9r5jYwMyMsC+7tkYRxHibkOTgta5DIFGoe67e1U=
|
||||
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
|
||||
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs=
|
||||
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
|
||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
||||
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU=
|
||||
github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
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 v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/quic-go/quic-go v0.44.0 h1:So5wOr7jyO4vzL2sd8/pD9Kesciv91zSk8BoFngItQ0=
|
||||
github.com/quic-go/quic-go v0.44.0/go.mod h1:z4cx/9Ny9UtGITIPzmPTXh1ULfOyWh4qGQlpnPcWmek=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
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/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
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/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=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/slackhq/nebula v1.6.1 h1:/OCTR3abj0Sbf2nGoLUrdDXImrCv0ZVFpVPP5qa0DsM=
|
||||
github.com/slackhq/nebula v1.6.1/go.mod h1:UmkqnXe4O53QwToSl/gG7sM4BroQwAB7dd4hUaT6MlI=
|
||||
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY=
|
||||
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc=
|
||||
github.com/smallstep/certificates v0.26.1 h1:FIUliEBcExSfJJDhRFA/s8aZgMIFuorexnRSKQd884o=
|
||||
github.com/smallstep/certificates v0.26.1/go.mod h1:OQMrW39IrGKDViKSHrKcgSQArMZ8c7EcjhYKK7mYqis=
|
||||
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 h1:kjYvkvS/Wdy0PVRDUAA0gGJIVSEZYhiAJtfwYgOYoGA=
|
||||
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4=
|
||||
github.com/smallstep/nosql v0.6.1 h1:X8IBZFTRIp1gmuf23ne/jlD/BWKJtDQbtatxEn7Et1Y=
|
||||
github.com/smallstep/nosql v0.6.1/go.mod h1:vrN+CftYYNnDM+DQqd863ATynvYFm/6FuY9D4TeAm2Y=
|
||||
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81 h1:B6cED3iLJTgxpdh4tuqByDjRRKan2EvtnOfHr2zHJVg=
|
||||
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81/go.mod h1:SoUAr/4M46rZ3WaLstHxGhLEgoYIDRqxQEXLOmOEB0Y=
|
||||
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d h1:06LUHn4Ia2X6syjIaCMNaXXDNdU+1N/oOHynJbWgpXw=
|
||||
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/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=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
|
||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tailscale/tscert v0.0.0-20240517230440-bbccfbf48933 h1:pV0H+XIvFoP7pl1MRtyPXh5hqoxB5I7snOtTHgrn6HU=
|
||||
github.com/tailscale/tscert v0.0.0-20240517230440-bbccfbf48933/go.mod h1:kNGUQ3VESx3VZwRwA9MSCUegIl6+saPL8Noq82ozCaU=
|
||||
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/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=
|
||||
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
|
||||
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
|
||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
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.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=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.step.sm/cli-utils v0.9.0 h1:55jYcsQbnArNqepZyAwcato6Zy2MoZDRkWW+jF+aPfQ=
|
||||
go.step.sm/cli-utils v0.9.0/go.mod h1:Y/CRoWl1FVR9j+7PnAewufAwKmBOTzR6l9+7EYGAnp8=
|
||||
go.step.sm/crypto v0.45.0 h1:Z0WYAaaOYrJmKP9sJkPW+6wy3pgN3Ija8ek/D4serjc=
|
||||
go.step.sm/crypto v0.45.0/go.mod h1:6IYlT0L2jfj81nVyCPpvA5cORy0EVHPhieSgQyuwHIY=
|
||||
go.step.sm/linkedca v0.20.1 h1:bHDn1+UG1NgRrERkWbbCiAIvv4lD5NOFaswPDTyO5vU=
|
||||
go.step.sm/linkedca v0.20.1/go.mod h1:Vaq4+Umtjh7DLFI1KuIxeo598vfBzgSYZUjgVJ7Syxw=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.uber.org/zap/exp v0.2.0 h1:FtGenNNeCATRB3CmB/yEUnjEFeJWpB/pMcy7e2bKPYs=
|
||||
go.uber.org/zap/exp v0.2.0/go.mod h1:t0gqAIdh1MfKv9EwN/dLwfZnJxe9ITAZN78HEWPFWDQ=
|
||||
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-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=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/crypto/x509roots/fallback v0.0.0-20240507223354-67b13616a595 h1:TgSqweA595vD0Zt86JzLv3Pb/syKg8gd5KMGGbJPYFw=
|
||||
golang.org/x/crypto/x509roots/fallback v0.0.0-20240507223354-67b13616a595/go.mod h1:kNa9WdvYnzFwC79zRpLRMJbdEFlhyM5RPFBBZp/wWH8=
|
||||
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-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.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-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=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
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.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
|
||||
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
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.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
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.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-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=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
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.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
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.180.0 h1:M2D87Yo0rGBPWpo1orwfCLehUUL6E7/TYe5gvMQWDh4=
|
||||
google.golang.org/api v0.180.0/go.mod h1:51AiyoEg1MJPSZ9zvklA8VnRILPXxn1iVen9v25XHAE=
|
||||
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-20240506185236-b8a5c65736ae h1:AH34z6WAGVNkllnKs5raNq3yRq93VnjBG6rpfub/jYk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae/go.mod h1:FfiGhwUm6CJviekPrc0oJ+7h29e+DmWU6UtjX0ZvI7Y=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6 h1:DujSIu+2tC9Ht0aPNA7jgj23Iq8Ewi5sgkQ++wdvonE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
||||
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
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=
|
||||
150
decnet/templates/https/_caddy_modules/decnetfp/h1_test.go
Normal file
150
decnet/templates/https/_caddy_modules/decnetfp/h1_test.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package decnetfp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// bindSock creates a unix datagram socket at path and returns it.
|
||||
// The caller must close it (or register a t.Cleanup).
|
||||
func bindSock(t *testing.T, path string) *net.UnixConn {
|
||||
t.Helper()
|
||||
sock, err := net.ListenUnixgram("unixgram", &net.UnixAddr{Name: path, Net: "unixgram"})
|
||||
if err != nil {
|
||||
t.Fatalf("listen %s: %v", path, err)
|
||||
}
|
||||
return sock
|
||||
}
|
||||
|
||||
// drainSock reads up to n records from an already-bound unix datagram socket.
|
||||
func drainSock(t *testing.T, sock *net.UnixConn, count int, timeout time.Duration) []map[string]interface{} {
|
||||
t.Helper()
|
||||
sock.SetDeadline(time.Now().Add(timeout)) //nolint:errcheck
|
||||
var records []map[string]interface{}
|
||||
buf := make([]byte, 65536)
|
||||
for len(records) < count {
|
||||
n, err := sock.Read(buf)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
var m map[string]interface{}
|
||||
if err := json.Unmarshal(buf[:n], &m); err != nil {
|
||||
t.Logf("unmarshal: %v", err)
|
||||
continue
|
||||
}
|
||||
records = append(records, m)
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func TestParseAndSendH1Headers(t *testing.T) {
|
||||
path := t.TempDir() + "/fp_h1.sock"
|
||||
t.Setenv("DECNET_FP_SOCK", path)
|
||||
// Reset the global socket so it reconnects to the test socket.
|
||||
sockMu.Lock()
|
||||
sockConn = nil
|
||||
sockMu.Unlock()
|
||||
t.Cleanup(func() {
|
||||
sockMu.Lock()
|
||||
if sockConn != nil {
|
||||
sockConn.Close()
|
||||
sockConn = nil
|
||||
}
|
||||
sockMu.Unlock()
|
||||
})
|
||||
|
||||
srv := bindSock(t, path)
|
||||
t.Cleanup(func() { srv.Close() })
|
||||
|
||||
done := make(chan []map[string]interface{}, 1)
|
||||
go func() {
|
||||
done <- drainSock(t, srv, 1, 2*time.Second)
|
||||
}()
|
||||
|
||||
// Typical curl HTTP/1.1 request bytes.
|
||||
raw := "GET /robots.txt HTTP/1.1\r\n" +
|
||||
"Host: example.com\r\n" +
|
||||
"User-Agent: curl/8.0.1\r\n" +
|
||||
"Accept: */*\r\n" +
|
||||
"X-Custom-A: alpha\r\n" +
|
||||
"X-Custom-B: beta\r\n" +
|
||||
"\r\n"
|
||||
|
||||
parseAndSendH1Headers("1.2.3.4:9999", []byte(raw))
|
||||
|
||||
records := <-done
|
||||
if len(records) != 1 {
|
||||
t.Fatalf("want 1 record, got %d", len(records))
|
||||
}
|
||||
rec := records[0]
|
||||
|
||||
if rec["kind"] != "http_request_headers" {
|
||||
t.Errorf("kind: got %v, want http_request_headers", rec["kind"])
|
||||
}
|
||||
if rec["proto_tag"] != "h1" {
|
||||
t.Errorf("proto_tag: got %v, want h1", rec["proto_tag"])
|
||||
}
|
||||
if rec["method"] != "GET" {
|
||||
t.Errorf("method: got %v, want GET", rec["method"])
|
||||
}
|
||||
if rec["path"] != "/robots.txt" {
|
||||
t.Errorf("path: got %v, want /robots.txt", rec["path"])
|
||||
}
|
||||
|
||||
rawOrdered, _ := json.Marshal(rec["headers_ordered"])
|
||||
var ordered [][]string
|
||||
if err := json.Unmarshal(rawOrdered, &ordered); err != nil {
|
||||
t.Fatalf("unmarshal headers_ordered: %v", err)
|
||||
}
|
||||
if len(ordered) != 5 {
|
||||
t.Fatalf("want 5 headers, got %d: %v", len(ordered), ordered)
|
||||
}
|
||||
// Wire order must be preserved exactly.
|
||||
want := []string{"host", "user-agent", "accept", "x-custom-a", "x-custom-b"}
|
||||
for i, pair := range ordered {
|
||||
if pair[0] != want[i] {
|
||||
t.Errorf("header[%d]: got %q, want %q", i, pair[0], want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAndSendH1Headers_StopsAtEmptyLine(t *testing.T) {
|
||||
// Headers should not include body bytes after \r\n\r\n.
|
||||
raw := "POST /login HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\nuser=bob&pass=secret"
|
||||
path := t.TempDir() + "/fp_h1b.sock"
|
||||
t.Setenv("DECNET_FP_SOCK", path)
|
||||
sockMu.Lock()
|
||||
sockConn = nil
|
||||
sockMu.Unlock()
|
||||
t.Cleanup(func() {
|
||||
sockMu.Lock()
|
||||
if sockConn != nil {
|
||||
sockConn.Close()
|
||||
sockConn = nil
|
||||
}
|
||||
sockMu.Unlock()
|
||||
})
|
||||
|
||||
srv := bindSock(t, path)
|
||||
t.Cleanup(func() { srv.Close() })
|
||||
|
||||
done := make(chan []map[string]interface{}, 1)
|
||||
go func() { done <- drainSock(t, srv, 1, 2*time.Second) }()
|
||||
parseAndSendH1Headers("10.0.0.1:1234", []byte(raw))
|
||||
|
||||
records := <-done
|
||||
if len(records) != 1 {
|
||||
t.Fatalf("want 1 record, got %d", len(records))
|
||||
}
|
||||
rawOrdered, _ := json.Marshal(records[0]["headers_ordered"])
|
||||
var ordered [][]string
|
||||
json.Unmarshal(rawOrdered, &ordered) //nolint:errcheck
|
||||
if len(ordered) != 1 {
|
||||
t.Fatalf("want 1 header (Content-Type), got %d: %v", len(ordered), ordered)
|
||||
}
|
||||
if ordered[0][0] != "content-type" {
|
||||
t.Errorf("header[0]: got %q, want content-type", ordered[0][0])
|
||||
}
|
||||
}
|
||||
206
decnet/templates/https/_caddy_modules/decnetfp/h2_test.go
Normal file
206
decnet/templates/https/_caddy_modules/decnetfp/h2_test.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package decnetfp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http2/hpack"
|
||||
)
|
||||
|
||||
// buildH2Preface returns the 24-byte h2 client preface.
|
||||
func buildH2Preface() []byte {
|
||||
return []byte(h2ClientPreface)
|
||||
}
|
||||
|
||||
// buildH2Settings builds a SETTINGS frame payload from id/val pairs.
|
||||
func buildH2Settings(pairs [][2]uint32) []byte {
|
||||
payload := make([]byte, len(pairs)*6)
|
||||
for i, p := range pairs {
|
||||
binary.BigEndian.PutUint16(payload[i*6:], uint16(p[0]))
|
||||
binary.BigEndian.PutUint32(payload[i*6+2:], p[1])
|
||||
}
|
||||
return buildH2Frame(0x4, 0, 0, payload)
|
||||
}
|
||||
|
||||
// buildH2Frame builds a raw h2 frame (9-byte header + payload).
|
||||
func buildH2Frame(typ, flags byte, streamID uint32, payload []byte) []byte {
|
||||
frame := make([]byte, 9+len(payload))
|
||||
frame[0] = byte(len(payload) >> 16)
|
||||
frame[1] = byte(len(payload) >> 8)
|
||||
frame[2] = byte(len(payload))
|
||||
frame[3] = typ
|
||||
frame[4] = flags
|
||||
binary.BigEndian.PutUint32(frame[5:], streamID)
|
||||
copy(frame[9:], payload)
|
||||
return frame
|
||||
}
|
||||
|
||||
// buildH2HeadersFrame encodes headers with HPACK and wraps in a HEADERS frame.
|
||||
func buildH2HeadersFrame(streamID uint32, headers [][2]string, endHeaders bool) []byte {
|
||||
var hbuf []byte
|
||||
enc := hpack.NewEncoder(writeCloser{&hbuf})
|
||||
for _, h := range headers {
|
||||
enc.WriteField(hpack.HeaderField{Name: h[0], Value: h[1]}) //nolint:errcheck
|
||||
}
|
||||
flags := byte(0)
|
||||
if endHeaders {
|
||||
flags |= 0x04
|
||||
}
|
||||
return buildH2Frame(0x1, flags, streamID, hbuf)
|
||||
}
|
||||
|
||||
type writeCloser struct{ b *[]byte }
|
||||
|
||||
func (w writeCloser) Write(p []byte) (int, error) {
|
||||
*w.b = append(*w.b, p...)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func TestParseH2HeadersLoop_DecodesHPACKOrder(t *testing.T) {
|
||||
sockPath := t.TempDir() + "/fp_h2.sock"
|
||||
t.Setenv("DECNET_FP_SOCK", sockPath)
|
||||
sockMu.Lock()
|
||||
sockConn = nil
|
||||
sockMu.Unlock()
|
||||
t.Cleanup(func() {
|
||||
sockMu.Lock()
|
||||
if sockConn != nil {
|
||||
sockConn.Close()
|
||||
sockConn = nil
|
||||
}
|
||||
sockMu.Unlock()
|
||||
})
|
||||
|
||||
srv := bindSock(t, sockPath)
|
||||
t.Cleanup(func() { srv.Close() })
|
||||
|
||||
done := make(chan []map[string]interface{}, 1)
|
||||
go func() { done <- drainSock(t, srv, 1, 3*time.Second) }()
|
||||
|
||||
// Build: preface + SETTINGS + HEADERS (stream 1, END_HEADERS).
|
||||
// Headers in a specific order to verify HPACK decode order is preserved.
|
||||
wantHeaders := [][2]string{
|
||||
{":method", "GET"},
|
||||
{":path", "/index.html"},
|
||||
{":scheme", "https"},
|
||||
{":authority", "example.com"},
|
||||
{"accept", "text/html"},
|
||||
{"user-agent", "Go-http-client/2.0"},
|
||||
{"accept-language", "en-US"},
|
||||
}
|
||||
|
||||
preface := buildH2Preface()
|
||||
settings := buildH2Settings([][2]uint32{{0x3, 100}}) // MAX_CONCURRENT_STREAMS=100
|
||||
headers := buildH2HeadersFrame(1, wantHeaders, true)
|
||||
|
||||
tap := make(chan []byte, 256)
|
||||
|
||||
// Feed the preface through the tap.
|
||||
chunks := [][]byte{preface, settings, headers}
|
||||
for _, c := range chunks {
|
||||
cp := make([]byte, len(c))
|
||||
copy(cp, c)
|
||||
tap <- cp
|
||||
}
|
||||
close(tap)
|
||||
|
||||
go parseH2HeadersLoop("5.6.7.8:443", tap)
|
||||
|
||||
records := <-done
|
||||
if len(records) == 0 {
|
||||
t.Fatal("no records received")
|
||||
}
|
||||
rec := records[0]
|
||||
if rec["kind"] != "http_request_headers" {
|
||||
t.Errorf("kind: got %v", rec["kind"])
|
||||
}
|
||||
if rec["proto_tag"] != "h2" {
|
||||
t.Errorf("proto_tag: got %v", rec["proto_tag"])
|
||||
}
|
||||
if rec["method"] != "GET" {
|
||||
t.Errorf("method: got %v", rec["method"])
|
||||
}
|
||||
if rec["path"] != "/index.html" {
|
||||
t.Errorf("path: got %v", rec["path"])
|
||||
}
|
||||
if rec["accept_language"] != "en-US" {
|
||||
t.Errorf("accept_language: got %v", rec["accept_language"])
|
||||
}
|
||||
|
||||
rawOrdered, _ := json.Marshal(rec["headers_ordered"])
|
||||
var ordered [][]string
|
||||
if err := json.Unmarshal(rawOrdered, &ordered); err != nil {
|
||||
t.Fatalf("unmarshal headers_ordered: %v", err)
|
||||
}
|
||||
if len(ordered) != len(wantHeaders) {
|
||||
t.Fatalf("want %d headers, got %d: %v", len(wantHeaders), len(ordered), ordered)
|
||||
}
|
||||
for i, pair := range ordered {
|
||||
if pair[0] != wantHeaders[i][0] {
|
||||
t.Errorf("header[%d]: got %q, want %q", i, pair[0], wantHeaders[i][0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseH2Settings_FrameOrder(t *testing.T) {
|
||||
sockPath := t.TempDir() + "/fp_h2s.sock"
|
||||
t.Setenv("DECNET_FP_SOCK", sockPath)
|
||||
sockMu.Lock()
|
||||
sockConn = nil
|
||||
sockMu.Unlock()
|
||||
t.Cleanup(func() {
|
||||
sockMu.Lock()
|
||||
if sockConn != nil {
|
||||
sockConn.Close()
|
||||
sockConn = nil
|
||||
}
|
||||
sockMu.Unlock()
|
||||
})
|
||||
|
||||
srv, err := net.ListenUnixgram("unixgram", &net.UnixAddr{Name: sockPath, Net: "unixgram"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer srv.Close()
|
||||
|
||||
// SETTINGS frame with 3 params in a specific order.
|
||||
settings := [][2]uint32{
|
||||
{0x4, 65535}, // INITIAL_WINDOW_SIZE
|
||||
{0x3, 1000}, // MAX_CONCURRENT_STREAMS
|
||||
{0x1, 65536}, // HEADER_TABLE_SIZE
|
||||
}
|
||||
payload := make([]byte, len(settings)*6)
|
||||
for i, s := range settings {
|
||||
binary.BigEndian.PutUint16(payload[i*6:], uint16(s[0]))
|
||||
binary.BigEndian.PutUint32(payload[i*6+2:], s[1])
|
||||
}
|
||||
|
||||
done := make(chan map[string]interface{}, 1)
|
||||
go func() {
|
||||
buf := make([]byte, 65536)
|
||||
srv.SetDeadline(time.Now().Add(2 * time.Second))
|
||||
n, _ := srv.Read(buf)
|
||||
var m map[string]interface{}
|
||||
json.Unmarshal(buf[:n], &m) //nolint:errcheck
|
||||
done <- m
|
||||
}()
|
||||
|
||||
parseAndSendH2Settings("1.2.3.4:1234", payload)
|
||||
|
||||
rec := <-done
|
||||
if rec["kind"] != "h2_settings" {
|
||||
t.Errorf("kind: got %v", rec["kind"])
|
||||
}
|
||||
rawOrder, _ := json.Marshal(rec["frame_order"])
|
||||
var order []float64 // JSON numbers decode as float64
|
||||
json.Unmarshal(rawOrder, &order) //nolint:errcheck
|
||||
if len(order) != 3 {
|
||||
t.Fatalf("want 3 frame_order entries, got %d", len(order))
|
||||
}
|
||||
if order[0] != 4 || order[1] != 3 || order[2] != 1 {
|
||||
t.Errorf("frame_order: got %v, want [4 3 1]", order)
|
||||
}
|
||||
}
|
||||
170
decnet/templates/https/_caddy_modules/decnetfp/h3_tracer_test.go
Normal file
170
decnet/templates/https/_caddy_modules/decnetfp/h3_tracer_test.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package decnetfp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// encodeVarint encodes a uint64 as an RFC 9000 variable-length integer.
|
||||
func encodeVarint(v uint64) []byte {
|
||||
switch {
|
||||
case v <= 0x3f:
|
||||
return []byte{byte(v)}
|
||||
case v <= 0x3fff:
|
||||
return []byte{0x40 | byte(v>>8), byte(v)}
|
||||
case v <= 0x3fffffff:
|
||||
b := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(b, uint32(v)|0x80000000)
|
||||
return b
|
||||
default:
|
||||
b := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(b, v|0xc000000000000000)
|
||||
return b
|
||||
}
|
||||
}
|
||||
|
||||
// buildH3ControlStream builds the opening bytes of an h3 control stream
|
||||
// with a SETTINGS frame containing the given id/val pairs.
|
||||
func buildH3ControlStream(settings [][2]uint64) []byte {
|
||||
// Stream type = 0x00 (control stream)
|
||||
var body []byte
|
||||
for _, s := range settings {
|
||||
body = append(body, encodeVarint(s[0])...)
|
||||
body = append(body, encodeVarint(s[1])...)
|
||||
}
|
||||
// h3 frame: type=0x04 (SETTINGS), length=len(body), body
|
||||
frame := append(encodeVarint(0x04), encodeVarint(uint64(len(body)))...)
|
||||
frame = append(frame, body...)
|
||||
|
||||
return append(encodeVarint(0x00), frame...)
|
||||
}
|
||||
|
||||
func TestTryParseH3ControlStream_ParsesSettings(t *testing.T) {
|
||||
sockPath := t.TempDir() + "/fp_h3.sock"
|
||||
t.Setenv("DECNET_FP_SOCK", sockPath)
|
||||
sockMu.Lock()
|
||||
sockConn = nil
|
||||
sockMu.Unlock()
|
||||
t.Cleanup(func() {
|
||||
sockMu.Lock()
|
||||
if sockConn != nil {
|
||||
sockConn.Close()
|
||||
sockConn = nil
|
||||
}
|
||||
sockMu.Unlock()
|
||||
})
|
||||
|
||||
srv, err := net.ListenUnixgram("unixgram", &net.UnixAddr{Name: sockPath, Net: "unixgram"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer srv.Close()
|
||||
|
||||
settings := [][2]uint64{
|
||||
{0x01, 0}, // QPACK_MAX_TABLE_CAPACITY
|
||||
{0x06, 0}, // MAX_FIELD_SECTION_SIZE (0 = unlimited)
|
||||
{0x07, 0}, // QPACK_BLOCKED_STREAMS
|
||||
{0x4242, 1}, // GREASE-like unknown value
|
||||
}
|
||||
data := buildH3ControlStream(settings)
|
||||
|
||||
done := make(chan map[string]interface{}, 1)
|
||||
go func() {
|
||||
buf := make([]byte, 65536)
|
||||
srv.SetDeadline(time.Now().Add(2 * time.Second))
|
||||
n, _ := srv.Read(buf)
|
||||
var m map[string]interface{}
|
||||
json.Unmarshal(buf[:n], &m) //nolint:errcheck
|
||||
done <- m
|
||||
}()
|
||||
|
||||
tryParseH3ControlStream("9.8.7.6:443", data)
|
||||
|
||||
rec := <-done
|
||||
if rec == nil {
|
||||
t.Fatal("no record received")
|
||||
}
|
||||
if rec["kind"] != "h3_settings" {
|
||||
t.Errorf("kind: got %v, want h3_settings", rec["kind"])
|
||||
}
|
||||
if rec["remote_addr"] != "9.8.7.6:443" {
|
||||
t.Errorf("remote_addr: got %v", rec["remote_addr"])
|
||||
}
|
||||
|
||||
rawSettings, _ := json.Marshal(rec["settings"])
|
||||
var gotSettings map[string]interface{}
|
||||
json.Unmarshal(rawSettings, &gotSettings) //nolint:errcheck
|
||||
if _, ok := gotSettings["QPACK_MAX_TABLE_CAPACITY"]; !ok {
|
||||
t.Errorf("missing QPACK_MAX_TABLE_CAPACITY in settings: %v", gotSettings)
|
||||
}
|
||||
if _, ok := gotSettings["MAX_FIELD_SECTION_SIZE"]; !ok {
|
||||
t.Errorf("missing MAX_FIELD_SECTION_SIZE in settings: %v", gotSettings)
|
||||
}
|
||||
|
||||
rawOrder, _ := json.Marshal(rec["frame_order"])
|
||||
var order []interface{}
|
||||
json.Unmarshal(rawOrder, &order) //nolint:errcheck
|
||||
if len(order) != 4 {
|
||||
t.Errorf("want 4 frame_order entries, got %d: %v", len(order), order)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTryParseH3ControlStream_WrongStreamType(t *testing.T) {
|
||||
// Stream type 0x02 = QPACK encoder stream — should be ignored.
|
||||
data := append(encodeVarint(0x02), []byte{0x00}...)
|
||||
// Should not panic or emit any record.
|
||||
tryParseH3ControlStream("1.2.3.4:443", data) // no socket — will silently drop
|
||||
}
|
||||
|
||||
func TestTryParseH3ControlStream_TruncatedData(t *testing.T) {
|
||||
// Only stream-type prefix, no SETTINGS frame yet.
|
||||
data := encodeVarint(0x00)
|
||||
tryParseH3ControlStream("1.2.3.4:443", data) // must not panic
|
||||
}
|
||||
|
||||
func TestQuicVarint(t *testing.T) {
|
||||
cases := []struct {
|
||||
input []byte
|
||||
want uint64
|
||||
wantN int
|
||||
}{
|
||||
{[]byte{0x00}, 0, 1},
|
||||
{[]byte{0x3f}, 63, 1},
|
||||
{[]byte{0x40, 0x00}, 0, 2},
|
||||
{[]byte{0x7f, 0xff}, 16383, 2},
|
||||
{[]byte{0x80, 0x00, 0x00, 0x00}, 0, 4},
|
||||
{[]byte{0xbf, 0xff, 0xff, 0xff}, 1073741823, 4},
|
||||
// Empty input
|
||||
{[]byte{}, 0, 0},
|
||||
// Truncated 2-byte
|
||||
{[]byte{0x40}, 0, 0},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, n := quicVarint(c.input)
|
||||
if v != c.want || n != c.wantN {
|
||||
t.Errorf("quicVarint(%x) = (%d, %d), want (%d, %d)", c.input, v, n, c.want, c.wantN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestH3SettingName(t *testing.T) {
|
||||
cases := []struct {
|
||||
id uint64
|
||||
want string
|
||||
}{
|
||||
{0x01, "QPACK_MAX_TABLE_CAPACITY"},
|
||||
{0x06, "MAX_FIELD_SECTION_SIZE"},
|
||||
{0x07, "QPACK_BLOCKED_STREAMS"},
|
||||
{0x08, "ENABLE_CONNECT_PROTOCOL"},
|
||||
{0x33, "H3_DATAGRAM"},
|
||||
{0x41, "UNKNOWN"}, // not a GREASE pattern (GREASE = 0x1f*N+0x21; 0x41-0x21=0x20, not div by 0x1f)
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := h3SettingName(c.id); got != c.want {
|
||||
t.Errorf("h3SettingName(0x%x) = %q, want %q", c.id, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
178
decnet/templates/https/_caddy_modules/decnetfp/h3app.go
Normal file
178
decnet/templates/https/_caddy_modules/decnetfp/h3app.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package decnetfp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterModule(H3App{})
|
||||
httpcaddyfile.RegisterGlobalOption("decnet_h3", parseH3AppOption)
|
||||
}
|
||||
|
||||
// parseH3AppOption maps the `decnet_h3` global Caddyfile block to the
|
||||
// decnet_h3 app config (empty JSON — all config comes from env).
|
||||
func parseH3AppOption(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) {
|
||||
for d.Next() {
|
||||
for d.NextBlock(0) {
|
||||
}
|
||||
}
|
||||
return json.RawMessage(`{}`), nil
|
||||
}
|
||||
|
||||
// H3App is a Caddy app that owns the QUIC/UDP listener on port 443, forwarding
|
||||
// accepted h3 connections to the Caddy HTTP app's handler chain. This is the
|
||||
// only way to inject a per-connection quic-go Tracer for h3 SETTINGS capture,
|
||||
// since Caddy does not expose its QUIC config.
|
||||
//
|
||||
// Activate with a `decnet_h3` global Caddyfile block AND omit `h3` from the
|
||||
// `:443` server's `protocols` list, otherwise both this app and Caddy's HTTP
|
||||
// server will fight over UDP/443.
|
||||
//
|
||||
// The app is a no-op when `HTTP_VERSIONS` env does not contain `"http/3"`.
|
||||
type H3App struct {
|
||||
caddyCtx caddy.Context
|
||||
logger *zap.Logger
|
||||
listener *quic.Listener
|
||||
transport *quic.Transport
|
||||
httpSrv *http3.Server
|
||||
cancelLoop context.CancelFunc
|
||||
wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
func (H3App) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: "decnet_h3",
|
||||
New: func() caddy.Module { return new(H3App) },
|
||||
}
|
||||
}
|
||||
|
||||
func (a *H3App) Provision(ctx caddy.Context) error {
|
||||
a.caddyCtx = ctx
|
||||
a.logger = ctx.Logger()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *H3App) Start() error {
|
||||
a.wg = &sync.WaitGroup{}
|
||||
if !strings.Contains(os.Getenv("HTTP_VERSIONS"), "http/3") {
|
||||
return nil
|
||||
}
|
||||
|
||||
cert, err := tls.LoadX509KeyPair("/opt/tls/cert.pem", "/opt/tls/key.pem")
|
||||
if err != nil {
|
||||
return fmt.Errorf("decnet_h3: load TLS cert: %w", err)
|
||||
}
|
||||
tlsCfg := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
NextProtos: []string{"h3"},
|
||||
}
|
||||
|
||||
udpConn, err := net.ListenPacket("udp", ":443")
|
||||
if err != nil {
|
||||
return fmt.Errorf("decnet_h3: bind UDP/443: %w", err)
|
||||
}
|
||||
tr := &quic.Transport{Conn: udpConn.(*net.UDPConn)}
|
||||
a.transport = tr
|
||||
|
||||
ln, err := tr.Listen(tlsCfg, &quic.Config{Tracer: newH3SettingsTracer})
|
||||
if err != nil {
|
||||
tr.Close()
|
||||
return fmt.Errorf("decnet_h3: quic listen: %w", err)
|
||||
}
|
||||
a.listener = ln
|
||||
|
||||
handler, err := a.findHTTPHandler()
|
||||
if err != nil {
|
||||
ln.Close()
|
||||
tr.Close()
|
||||
return fmt.Errorf("decnet_h3: find HTTP handler: %w", err)
|
||||
}
|
||||
|
||||
a.httpSrv = &http3.Server{Handler: handler}
|
||||
|
||||
loopCtx, cancel := context.WithCancel(context.Background())
|
||||
a.cancelLoop = cancel
|
||||
a.wg.Add(1)
|
||||
go func() {
|
||||
defer a.wg.Done()
|
||||
a.acceptLoop(loopCtx)
|
||||
}()
|
||||
|
||||
a.logger.Info("decnet_h3 listening on UDP/443")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *H3App) acceptLoop(ctx context.Context) {
|
||||
for {
|
||||
conn, err := a.listener.Accept(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
wrapped := &h3SettingsTappingConn{
|
||||
Connection: conn,
|
||||
remoteAddr: conn.RemoteAddr().String(),
|
||||
}
|
||||
go func() {
|
||||
a.httpSrv.ServeQUICConn(wrapped) //nolint:errcheck
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *H3App) Stop() error {
|
||||
if a.cancelLoop != nil {
|
||||
a.cancelLoop()
|
||||
}
|
||||
if a.listener != nil {
|
||||
a.listener.Close()
|
||||
}
|
||||
a.wg.Wait()
|
||||
if a.transport != nil {
|
||||
a.transport.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// findHTTPHandler returns the http.Handler for Caddy's :443 server.
|
||||
func (a *H3App) findHTTPHandler() (http.Handler, error) {
|
||||
appIface, err := a.caddyCtx.App("http")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get http app: %w", err)
|
||||
}
|
||||
httpApp, ok := appIface.(*caddyhttp.App)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected http app type %T", appIface)
|
||||
}
|
||||
for _, srv := range httpApp.Servers {
|
||||
for _, addr := range srv.Listen {
|
||||
if strings.Contains(addr, ":443") {
|
||||
return srv, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fall back to any available server.
|
||||
for _, srv := range httpApp.Servers {
|
||||
return srv, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no HTTP servers found in caddy http app")
|
||||
}
|
||||
|
||||
var (
|
||||
_ caddy.App = (*H3App)(nil)
|
||||
_ caddy.Provisioner = (*H3App)(nil)
|
||||
)
|
||||
184
decnet/templates/https/_caddy_modules/decnetfp/h3conn.go
Normal file
184
decnet/templates/https/_caddy_modules/decnetfp/h3conn.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package decnetfp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/logging"
|
||||
)
|
||||
|
||||
// newH3SettingsTracer is the quic.Config.Tracer factory. We don't use
|
||||
// quic-go's logging.ConnectionTracer for SETTINGS (its ReceivedStreamFrame
|
||||
// hook gives only metadata, not payload bytes). The actual h3 SETTINGS
|
||||
// capture happens in h3TappingUniStream by wrapping AcceptUniStream.
|
||||
// This function returns nil (no-op tracer) so quic-go uses its default path.
|
||||
func newH3SettingsTracer(_ context.Context, _ logging.Perspective, _ quic.ConnectionID) *logging.ConnectionTracer {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ── QUIC connection wrapper ───────────────────────────────────────────────────
|
||||
|
||||
// h3SettingsTappingConn wraps quic.Connection and intercepts AcceptUniStream
|
||||
// so the first bytes of each client-initiated unidirectional stream can be
|
||||
// inspected for h3 control stream SETTINGS before being replayed to the
|
||||
// http3.Server.
|
||||
type h3SettingsTappingConn struct {
|
||||
quic.Connection
|
||||
remoteAddr string
|
||||
}
|
||||
|
||||
func (c *h3SettingsTappingConn) AcceptUniStream(ctx context.Context) (quic.ReceiveStream, error) {
|
||||
stream, err := c.Connection.AcceptUniStream(ctx)
|
||||
if err != nil {
|
||||
return stream, err
|
||||
}
|
||||
return &h3TappingUniStream{ReceiveStream: stream, remoteAddr: c.remoteAddr}, nil
|
||||
}
|
||||
|
||||
// ── QUIC receive-stream wrapper ───────────────────────────────────────────────
|
||||
|
||||
// h3TappingUniStream peeks at the first bytes of a unidirectional stream to
|
||||
// identify the h3 control stream (stream type 0x00, RFC 9114 §6.2.1) and
|
||||
// extract its first SETTINGS frame, then replays all bytes to the caller.
|
||||
type h3TappingUniStream struct {
|
||||
quic.ReceiveStream
|
||||
once sync.Once
|
||||
buf bytes.Buffer
|
||||
reader io.Reader
|
||||
remoteAddr string
|
||||
}
|
||||
|
||||
// maxH3ControlPeek is enough to cover the stream-type varint + SETTINGS
|
||||
// frame type varint + frame-length varint + a typical SETTINGS frame body
|
||||
// (6 settings × 8 bytes each = 48 bytes, plus 3 varint headers ≈ 64 bytes).
|
||||
const maxH3ControlPeek = 256
|
||||
|
||||
func (s *h3TappingUniStream) Read(p []byte) (int, error) {
|
||||
s.once.Do(func() {
|
||||
scratch := make([]byte, maxH3ControlPeek)
|
||||
n, _ := s.ReceiveStream.Read(scratch)
|
||||
s.buf.Write(scratch[:n])
|
||||
go tryParseH3ControlStream(s.remoteAddr, s.buf.Bytes())
|
||||
s.reader = io.MultiReader(&s.buf, s.ReceiveStream)
|
||||
})
|
||||
if s.reader != nil {
|
||||
return s.reader.Read(p)
|
||||
}
|
||||
return s.ReceiveStream.Read(p)
|
||||
}
|
||||
|
||||
// tryParseH3ControlStream examines the peeked bytes. If the stream opens
|
||||
// with stream-type 0x00 (h3 control stream) and the first frame is SETTINGS
|
||||
// (type 0x04), it emits an h3_settings fp record. All errors are silent —
|
||||
// this is a best-effort tap.
|
||||
func tryParseH3ControlStream(remoteAddr string, data []byte) {
|
||||
streamType, c0 := quicVarint(data)
|
||||
if c0 == 0 || streamType != 0x00 {
|
||||
return // not the h3 control stream
|
||||
}
|
||||
data = data[c0:]
|
||||
|
||||
frameType, c1 := quicVarint(data)
|
||||
if c1 == 0 {
|
||||
return
|
||||
}
|
||||
data = data[c1:]
|
||||
|
||||
frameLen, c2 := quicVarint(data)
|
||||
if c2 == 0 {
|
||||
return
|
||||
}
|
||||
data = data[c2:]
|
||||
|
||||
// Per RFC 9114 §7.2.4: the first frame on the control stream MUST be SETTINGS.
|
||||
if frameType != 0x04 {
|
||||
return
|
||||
}
|
||||
if uint64(len(data)) < frameLen {
|
||||
return // need more bytes — we only peeked 256
|
||||
}
|
||||
body := data[:frameLen]
|
||||
|
||||
settings := make(map[string]uint64)
|
||||
frameOrder := make([]uint64, 0, 8)
|
||||
for len(body) > 0 {
|
||||
id, ci := quicVarint(body)
|
||||
if ci == 0 {
|
||||
break
|
||||
}
|
||||
body = body[ci:]
|
||||
val, cv := quicVarint(body)
|
||||
if cv == 0 {
|
||||
break
|
||||
}
|
||||
body = body[cv:]
|
||||
settings[h3SettingName(id)] = val
|
||||
frameOrder = append(frameOrder, id)
|
||||
}
|
||||
|
||||
sendFP(map[string]interface{}{
|
||||
"kind": "h3_settings",
|
||||
"remote_addr": remoteAddr,
|
||||
"settings": settings,
|
||||
"frame_order": frameOrder,
|
||||
"ts": time.Now().UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
// quicVarint decodes an RFC 9000 §16 variable-length integer.
|
||||
// Returns (value, bytes_consumed); bytes_consumed == 0 on failure.
|
||||
func quicVarint(b []byte) (uint64, int) {
|
||||
if len(b) == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
prefix := b[0] >> 6
|
||||
switch prefix {
|
||||
case 0:
|
||||
return uint64(b[0] & 0x3f), 1
|
||||
case 1:
|
||||
if len(b) < 2 {
|
||||
return 0, 0
|
||||
}
|
||||
return uint64(b[0]&0x3f)<<8 | uint64(b[1]), 2
|
||||
case 2:
|
||||
if len(b) < 4 {
|
||||
return 0, 0
|
||||
}
|
||||
return uint64(b[0]&0x3f)<<24 | uint64(b[1])<<16 | uint64(b[2])<<8 | uint64(b[3]), 4
|
||||
case 3:
|
||||
if len(b) < 8 {
|
||||
return 0, 0
|
||||
}
|
||||
return uint64(b[0]&0x3f)<<56 | uint64(b[1])<<48 | uint64(b[2])<<40 | uint64(b[3])<<32 |
|
||||
uint64(b[4])<<24 | uint64(b[5])<<16 | uint64(b[6])<<8 | uint64(b[7]), 8
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// h3SettingName maps RFC 9114 and extension SETTINGS IDs to human-readable names.
|
||||
func h3SettingName(id uint64) string {
|
||||
switch id {
|
||||
case 0x01:
|
||||
return "QPACK_MAX_TABLE_CAPACITY"
|
||||
case 0x06:
|
||||
return "MAX_FIELD_SECTION_SIZE"
|
||||
case 0x07:
|
||||
return "QPACK_BLOCKED_STREAMS"
|
||||
case 0x08:
|
||||
return "ENABLE_CONNECT_PROTOCOL"
|
||||
case 0x33:
|
||||
return "H3_DATAGRAM"
|
||||
case 0xc671706a:
|
||||
return "ENABLE_WEBTRANSPORT"
|
||||
default:
|
||||
// GREASE values per RFC 9114 §7.2.8 pattern (0x1f * N + 0x21)
|
||||
if id > 0x20 && (id-0x21)%0x1f == 0 {
|
||||
return "GREASE"
|
||||
}
|
||||
return "UNKNOWN"
|
||||
}
|
||||
}
|
||||
645
decnet/templates/https/_caddy_modules/decnetfp/module.go
Normal file
645
decnet/templates/https/_caddy_modules/decnetfp/module.go
Normal file
@@ -0,0 +1,645 @@
|
||||
// Package decnetfp provides Caddy modules for HTTP fingerprint capture.
|
||||
//
|
||||
// Registered modules:
|
||||
// - caddy.listeners.decnet_fp — post-TLS listener wrapper that taps
|
||||
// the h2 client preface (SETTINGS + HEADERS frames via persistent HPACK
|
||||
// decoder) and h1 request-line / header bytes, emitting ordered header
|
||||
// name lists to /run/decnet/fp.sock (unix datagram).
|
||||
// - http.handlers.decnet_fp — HTTP middleware that emits an
|
||||
// access_log record (status code, bytes, protocol) after each response.
|
||||
// - caddy.logging.encoders.decnet_jsonl — log encoder stub (registered
|
||||
// but not wired into Caddyfile; access_log comes from the handler above).
|
||||
//
|
||||
// All modules write JSON lines to a unix datagram socket whose path is
|
||||
// controlled by DECNET_FP_SOCK (default: /run/decnet/fp.sock).
|
||||
package decnetfp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterModule(FPListenerWrapper{})
|
||||
caddy.RegisterModule(FPHandler{})
|
||||
caddy.RegisterModule(DecnetJSONLEncoder{})
|
||||
httpcaddyfile.RegisterHandlerDirective("decnet_fp", parseFPHandler)
|
||||
}
|
||||
|
||||
func parseFPHandler(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
|
||||
var fp FPHandler
|
||||
return &fp, fp.UnmarshalCaddyfile(h.Dispenser)
|
||||
}
|
||||
|
||||
func sockPath() string {
|
||||
if p := os.Getenv("DECNET_FP_SOCK"); p != "" {
|
||||
return p
|
||||
}
|
||||
return "/run/decnet/fp.sock"
|
||||
}
|
||||
|
||||
// ── unix datagram sender ──────────────────────────────────────────────────────
|
||||
|
||||
var (
|
||||
sockMu sync.Mutex
|
||||
sockConn *net.UnixConn
|
||||
)
|
||||
|
||||
func sendFP(record map[string]interface{}) {
|
||||
b, err := json.Marshal(record)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sockMu.Lock()
|
||||
defer sockMu.Unlock()
|
||||
if sockConn == nil {
|
||||
conn, err := net.DialUnix("unixgram", nil, &net.UnixAddr{Name: sockPath(), Net: "unixgram"})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sockConn = conn
|
||||
}
|
||||
sockConn.SetWriteDeadline(time.Now().Add(50 * time.Millisecond)) //nolint:errcheck
|
||||
sockConn.Write(b) //nolint:errcheck
|
||||
}
|
||||
|
||||
// ── caddy.listeners.decnet_fp ─────────────────────────────────────────────────
|
||||
|
||||
// FPListenerWrapper is a post-TLS Caddy listener wrapper that:
|
||||
// - For h2 ALPN connections: taps the h2 client preface (SETTINGS frame)
|
||||
// and then continuously parses HEADERS/CONTINUATION frames via a
|
||||
// persistent HPACK decoder, emitting ordered header name lists.
|
||||
// - For h1 ALPN connections (or plain connections): taps the first request's
|
||||
// header bytes, emitting ordered header names from the wire.
|
||||
//
|
||||
// Place it AFTER the TLS listener wrapper so it sees post-TLS data:
|
||||
//
|
||||
// listener_wrappers {
|
||||
// tls
|
||||
// decnet_fp
|
||||
// }
|
||||
type FPListenerWrapper struct {
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (FPListenerWrapper) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: "caddy.listeners.decnet_fp",
|
||||
New: func() caddy.Module { return new(FPListenerWrapper) },
|
||||
}
|
||||
}
|
||||
|
||||
func (w *FPListenerWrapper) Provision(ctx caddy.Context) error {
|
||||
w.logger = ctx.Logger()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *FPListenerWrapper) WrapListener(ln net.Listener) net.Listener {
|
||||
return &fpListener{Listener: ln, logger: w.logger}
|
||||
}
|
||||
|
||||
func (w *FPListenerWrapper) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type fpListener struct {
|
||||
net.Listener
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (l *fpListener) Accept() (net.Conn, error) {
|
||||
conn, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
return conn, err
|
||||
}
|
||||
remote := conn.RemoteAddr().String()
|
||||
|
||||
tlsConn, ok := conn.(*tls.Conn)
|
||||
if !ok {
|
||||
// Plain (cleartext) connection — peek to distinguish h2c from h1.
|
||||
return &plainTappingConn{Conn: conn, remoteAddr: remote}, nil
|
||||
}
|
||||
state := tlsConn.ConnectionState()
|
||||
switch state.NegotiatedProtocol {
|
||||
case "h2":
|
||||
return &h2TappingConn{Conn: conn, remoteAddr: remote}, nil
|
||||
default:
|
||||
// http/1.1 ALPN or no ALPN — post-TLS plaintext is h1; safe to
|
||||
// use plainTappingConn (h2c preface won't appear after TLS).
|
||||
return &plainTappingConn{Conn: conn, remoteAddr: remote}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ── Plain connection tap (h1 and h2c) ────────────────────────────────────────
|
||||
|
||||
const h1MaxHeaderBuf = 8192
|
||||
|
||||
// plainTappingConn handles post-accept connections that are not TLS h2.
|
||||
// It peeks the first bytes to distinguish h2c prior-knowledge from h1,
|
||||
// then routes to the appropriate parser. Used for both cleartext (port 80)
|
||||
// and TLS-h1 (ALPN "" or "http/1.1") connections.
|
||||
type plainTappingConn struct {
|
||||
net.Conn
|
||||
once sync.Once
|
||||
buf bytes.Buffer
|
||||
reader io.Reader
|
||||
remoteAddr string
|
||||
}
|
||||
|
||||
// tapWriter is a non-blocking io.Writer that drops if the channel is full.
|
||||
type tapWriter struct {
|
||||
ch chan<- []byte
|
||||
}
|
||||
|
||||
func (t tapWriter) Write(p []byte) (int, error) {
|
||||
cp := make([]byte, len(p))
|
||||
copy(cp, p)
|
||||
select {
|
||||
case t.ch <- cp:
|
||||
default:
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (c *plainTappingConn) Read(b []byte) (int, error) {
|
||||
c.once.Do(func() {
|
||||
// Peek exactly len(h2ClientPreface) bytes to detect h2c prior knowledge.
|
||||
preface := make([]byte, len(h2ClientPreface))
|
||||
n, _ := io.ReadFull(c.Conn, preface)
|
||||
c.buf.Write(preface[:n])
|
||||
|
||||
if n == len(h2ClientPreface) && string(preface) == h2ClientPreface {
|
||||
// h2c prior-knowledge connection — run the same SETTINGS+HPACK tap.
|
||||
hdr9 := make([]byte, 9)
|
||||
if m, err := io.ReadFull(c.Conn, hdr9); m == 9 && err == nil {
|
||||
c.buf.Write(hdr9)
|
||||
frameLen := int(hdr9[0])<<16 | int(hdr9[1])<<8 | int(hdr9[2])
|
||||
frameType := hdr9[3]
|
||||
if frameType == 0x4 && frameLen > 0 && frameLen <= 16384 {
|
||||
payload := make([]byte, frameLen)
|
||||
if _, err := io.ReadFull(c.Conn, payload); err == nil {
|
||||
c.buf.Write(payload)
|
||||
go parseAndSendH2Settings(c.remoteAddr, payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
tap := make(chan []byte, 256)
|
||||
c.reader = io.TeeReader(io.MultiReader(&c.buf, c.Conn), tapWriter{ch: tap})
|
||||
go parseH2HeadersLoop(c.remoteAddr, tap)
|
||||
return
|
||||
}
|
||||
|
||||
// h1 — buffer up to h1MaxHeaderBuf or until \r\n\r\n.
|
||||
scratch := make([]byte, h1MaxHeaderBuf)
|
||||
for c.buf.Len() < h1MaxHeaderBuf {
|
||||
nn, err := c.Conn.Read(scratch[:h1MaxHeaderBuf-c.buf.Len()])
|
||||
c.buf.Write(scratch[:nn])
|
||||
if bytes.Contains(c.buf.Bytes(), []byte("\r\n\r\n")) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
go parseAndSendH1Headers(c.remoteAddr, c.buf.Bytes())
|
||||
c.reader = io.MultiReader(&c.buf, c.Conn)
|
||||
})
|
||||
if c.reader == nil {
|
||||
return c.Conn.Read(b)
|
||||
}
|
||||
return c.reader.Read(b)
|
||||
}
|
||||
|
||||
func parseAndSendH1Headers(remoteAddr string, raw []byte) {
|
||||
idx := bytes.Index(raw, []byte("\r\n\r\n"))
|
||||
if idx < 0 {
|
||||
idx = len(raw)
|
||||
}
|
||||
lines := bytes.Split(raw[:idx], []byte("\r\n"))
|
||||
if len(lines) == 0 {
|
||||
return
|
||||
}
|
||||
// First line: "GET /path HTTP/1.1"
|
||||
requestLine := string(lines[0])
|
||||
var method, path, proto string
|
||||
parts := bytes.Fields(lines[0])
|
||||
if len(parts) >= 3 {
|
||||
method = string(parts[0])
|
||||
path = string(parts[1])
|
||||
proto = string(parts[2])
|
||||
}
|
||||
|
||||
var ordered [][]string // [[name, value], ...]
|
||||
var cookie, acceptLang string
|
||||
for _, line := range lines[1:] {
|
||||
sep := bytes.IndexByte(line, ':')
|
||||
if sep < 0 {
|
||||
continue
|
||||
}
|
||||
name := string(bytes.ToLower(bytes.TrimSpace(line[:sep])))
|
||||
value := string(bytes.TrimSpace(line[sep+1:]))
|
||||
ordered = append(ordered, []string{name, value})
|
||||
switch name {
|
||||
case "cookie":
|
||||
cookie = value
|
||||
case "accept-language":
|
||||
acceptLang = value
|
||||
}
|
||||
}
|
||||
|
||||
sendFP(map[string]interface{}{
|
||||
"kind": "http_request_headers",
|
||||
"remote_addr": remoteAddr,
|
||||
"proto_tag": "h1",
|
||||
"request_line": requestLine,
|
||||
"method": method,
|
||||
"path": path,
|
||||
"proto": proto,
|
||||
"headers_ordered": ordered,
|
||||
"cookie": cookie,
|
||||
"accept_language": acceptLang,
|
||||
"ts": time.Now().UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
// ── H2 client preface + HPACK continuous tap ──────────────────────────────────
|
||||
|
||||
const h2ClientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
|
||||
|
||||
type h2TappingConn struct {
|
||||
net.Conn
|
||||
once sync.Once
|
||||
buf bytes.Buffer
|
||||
reader io.Reader
|
||||
remoteAddr string
|
||||
}
|
||||
|
||||
func (c *h2TappingConn) Read(b []byte) (int, error) {
|
||||
c.once.Do(func() {
|
||||
// Buffer client preface (24 B) + first frame header (9 B).
|
||||
hdr := make([]byte, len(h2ClientPreface)+9)
|
||||
if _, err := io.ReadFull(c.Conn, hdr); err != nil {
|
||||
c.buf.Write(hdr)
|
||||
c.reader = io.MultiReader(&c.buf, c.Conn)
|
||||
return
|
||||
}
|
||||
c.buf.Write(hdr)
|
||||
|
||||
frameLen := int(hdr[len(h2ClientPreface)])<<16 |
|
||||
int(hdr[len(h2ClientPreface)+1])<<8 |
|
||||
int(hdr[len(h2ClientPreface)+2])
|
||||
frameType := hdr[len(h2ClientPreface)+3]
|
||||
|
||||
if frameType == 0x4 && frameLen > 0 && frameLen <= 16384 {
|
||||
payload := make([]byte, frameLen)
|
||||
if _, err := io.ReadFull(c.Conn, payload); err == nil {
|
||||
c.buf.Write(payload)
|
||||
go parseAndSendH2Settings(c.remoteAddr, payload)
|
||||
}
|
||||
}
|
||||
|
||||
// Start HPACK continuous parse on a non-blocking channel tap.
|
||||
tap := make(chan []byte, 256)
|
||||
c.reader = io.TeeReader(io.MultiReader(&c.buf, c.Conn), tapWriter{ch: tap})
|
||||
go parseH2HeadersLoop(c.remoteAddr, tap)
|
||||
})
|
||||
if c.reader == nil {
|
||||
return c.Conn.Read(b)
|
||||
}
|
||||
return c.reader.Read(b)
|
||||
}
|
||||
|
||||
func parseAndSendH2Settings(remoteAddr string, payload []byte) {
|
||||
settings := make(map[string]uint32)
|
||||
frameOrder := make([]uint16, 0, len(payload)/6)
|
||||
for i := 0; i+6 <= len(payload); i += 6 {
|
||||
id := binary.BigEndian.Uint16(payload[i : i+2])
|
||||
val := binary.BigEndian.Uint32(payload[i+2 : i+6])
|
||||
settings[settingName(id)] = val
|
||||
frameOrder = append(frameOrder, id)
|
||||
}
|
||||
sendFP(map[string]interface{}{
|
||||
"kind": "h2_settings",
|
||||
"remote_addr": remoteAddr,
|
||||
"settings": settings,
|
||||
"frame_order": frameOrder,
|
||||
"ts": time.Now().UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
func settingName(id uint16) string {
|
||||
switch id {
|
||||
case 0x1:
|
||||
return "HEADER_TABLE_SIZE"
|
||||
case 0x2:
|
||||
return "ENABLE_PUSH"
|
||||
case 0x3:
|
||||
return "MAX_CONCURRENT_STREAMS"
|
||||
case 0x4:
|
||||
return "INITIAL_WINDOW_SIZE"
|
||||
case 0x5:
|
||||
return "MAX_FRAME_SIZE"
|
||||
case 0x6:
|
||||
return "MAX_HEADER_LIST_SIZE"
|
||||
case 0x8:
|
||||
return "ENABLE_CONNECT_PROTOCOL"
|
||||
default:
|
||||
if id >= 0xf000 {
|
||||
return "GREASE"
|
||||
}
|
||||
return "UNKNOWN"
|
||||
}
|
||||
}
|
||||
|
||||
// hpackConn holds per-connection HPACK decoder state.
|
||||
type hpackConn struct {
|
||||
decoder *hpack.Decoder
|
||||
currentFields []hpack.HeaderField
|
||||
}
|
||||
|
||||
func newHpackConn(maxTableSize uint32) *hpackConn {
|
||||
hc := &hpackConn{}
|
||||
hc.decoder = hpack.NewDecoder(maxTableSize, func(f hpack.HeaderField) {
|
||||
hc.currentFields = append(hc.currentFields, f)
|
||||
})
|
||||
return hc
|
||||
}
|
||||
|
||||
// decode decodes a header block fragment, appending to existing and returning combined.
|
||||
func (hc *hpackConn) decode(fragment []byte, existing []hpack.HeaderField) []hpack.HeaderField {
|
||||
hc.currentFields = existing
|
||||
hc.decoder.Write(fragment) //nolint:errcheck
|
||||
return hc.currentFields
|
||||
}
|
||||
|
||||
func parseH2HeadersLoop(remoteAddr string, tap <-chan []byte) {
|
||||
var buf []byte
|
||||
prefaceLen := len(h2ClientPreface)
|
||||
prefaceSkipped := false
|
||||
hc := newHpackConn(4096)
|
||||
|
||||
type streamState struct {
|
||||
fields []hpack.HeaderField
|
||||
}
|
||||
streams := make(map[uint32]*streamState)
|
||||
|
||||
for chunk := range tap {
|
||||
buf = append(buf, chunk...)
|
||||
|
||||
if !prefaceSkipped {
|
||||
if len(buf) < prefaceLen {
|
||||
continue
|
||||
}
|
||||
buf = buf[prefaceLen:]
|
||||
prefaceSkipped = true
|
||||
}
|
||||
|
||||
for len(buf) >= 9 {
|
||||
frameLen := int(buf[0])<<16 | int(buf[1])<<8 | int(buf[2])
|
||||
frameType := buf[3]
|
||||
flags := buf[4]
|
||||
streamID := binary.BigEndian.Uint32(buf[5:9]) & 0x7fffffff
|
||||
|
||||
total := 9 + frameLen
|
||||
if len(buf) < total {
|
||||
break
|
||||
}
|
||||
payload := buf[9:total]
|
||||
buf = buf[total:]
|
||||
|
||||
switch frameType {
|
||||
case 0x4: // SETTINGS from client
|
||||
if flags&0x1 != 0 { // ACK
|
||||
break
|
||||
}
|
||||
for i := 0; i+6 <= len(payload); i += 6 {
|
||||
id := binary.BigEndian.Uint16(payload[i : i+2])
|
||||
val := binary.BigEndian.Uint32(payload[i+2 : i+6])
|
||||
if id == 0x1 { // SETTINGS_HEADER_TABLE_SIZE
|
||||
hc.decoder.SetMaxDynamicTableSize(val)
|
||||
}
|
||||
}
|
||||
|
||||
case 0x1, 0x9: // HEADERS, CONTINUATION
|
||||
fragment := payload
|
||||
if frameType == 0x1 {
|
||||
off := 0
|
||||
var padLen byte
|
||||
if flags&0x08 != 0 { // PADDED
|
||||
if len(fragment) < 1 {
|
||||
continue
|
||||
}
|
||||
padLen = fragment[0]
|
||||
off = 1
|
||||
}
|
||||
if flags&0x20 != 0 { // PRIORITY
|
||||
off += 5
|
||||
}
|
||||
end := len(fragment) - int(padLen)
|
||||
if off > end || end < 0 {
|
||||
continue
|
||||
}
|
||||
fragment = fragment[off:end]
|
||||
}
|
||||
|
||||
ss, ok := streams[streamID]
|
||||
if !ok {
|
||||
ss = &streamState{}
|
||||
streams[streamID] = ss
|
||||
}
|
||||
ss.fields = hc.decode(fragment, ss.fields)
|
||||
|
||||
if flags&0x04 != 0 { // END_HEADERS
|
||||
emitH2RequestHeaders(remoteAddr, streamID, ss.fields)
|
||||
delete(streams, streamID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func emitH2RequestHeaders(remoteAddr string, streamID uint32, fields []hpack.HeaderField) {
|
||||
ordered := make([][]string, 0, len(fields))
|
||||
var cookie, acceptLang, method, path string
|
||||
for _, f := range fields {
|
||||
ordered = append(ordered, []string{f.Name, f.Value})
|
||||
switch f.Name {
|
||||
case "cookie":
|
||||
cookie = f.Value
|
||||
case "accept-language":
|
||||
acceptLang = f.Value
|
||||
case ":method":
|
||||
method = f.Value
|
||||
case ":path":
|
||||
path = f.Value
|
||||
}
|
||||
}
|
||||
sendFP(map[string]interface{}{
|
||||
"kind": "http_request_headers",
|
||||
"remote_addr": remoteAddr,
|
||||
"stream_id": streamID,
|
||||
"proto_tag": "h2",
|
||||
"method": method,
|
||||
"path": path,
|
||||
"headers_ordered": ordered,
|
||||
"cookie": cookie,
|
||||
"accept_language": acceptLang,
|
||||
"ts": time.Now().UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
// ── http.handlers.decnet_fp ───────────────────────────────────────────────────
|
||||
|
||||
// FPHandler is HTTP middleware that emits an access_log record (status code,
|
||||
// bytes, proto) after each response via the fp socket. For h3 requests it
|
||||
// also emits a best-effort http_request_headers record (header order degraded
|
||||
// — QPACK decode order is preserved by quic-go but the Go map randomises it
|
||||
// further; canonical h3 header order requires request-stream QPACK tapping
|
||||
// which is a follow-up task).
|
||||
type FPHandler struct {
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (FPHandler) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: "http.handlers.decnet_fp",
|
||||
New: func() caddy.Module { return new(FPHandler) },
|
||||
}
|
||||
}
|
||||
|
||||
func (h *FPHandler) Provision(ctx caddy.Context) error {
|
||||
h.logger = ctx.Logger()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *FPHandler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// responseCapture captures the status code and bytes written.
|
||||
type responseCapture struct {
|
||||
http.ResponseWriter
|
||||
status int
|
||||
bytes int
|
||||
}
|
||||
|
||||
func (rc *responseCapture) WriteHeader(status int) {
|
||||
rc.status = status
|
||||
rc.ResponseWriter.WriteHeader(status)
|
||||
}
|
||||
|
||||
func (rc *responseCapture) Write(b []byte) (int, error) {
|
||||
n, err := rc.ResponseWriter.Write(b)
|
||||
rc.bytes += n
|
||||
if rc.status == 0 {
|
||||
rc.status = 200
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (h *FPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
|
||||
rc := &responseCapture{ResponseWriter: w}
|
||||
err := next.ServeHTTP(rc, r)
|
||||
|
||||
protoTag := "h1"
|
||||
if r.ProtoMajor == 2 {
|
||||
protoTag = "h2"
|
||||
} else if r.ProtoMajor == 3 {
|
||||
protoTag = "h3"
|
||||
}
|
||||
|
||||
status := rc.status
|
||||
if status == 0 {
|
||||
status = 200
|
||||
}
|
||||
|
||||
go sendFP(map[string]interface{}{
|
||||
"kind": "access_log",
|
||||
"remote_addr": r.RemoteAddr,
|
||||
"method": r.Method,
|
||||
"path": r.URL.Path,
|
||||
"proto": r.Proto,
|
||||
"proto_tag": protoTag,
|
||||
"status": status,
|
||||
"bytes": rc.bytes,
|
||||
"ts": time.Now().UTC().Format(time.RFC3339),
|
||||
})
|
||||
|
||||
// For h3, emit best-effort http_request_headers (map order, degraded).
|
||||
if r.ProtoMajor == 3 {
|
||||
ordered := make([][]string, 0, len(r.Header))
|
||||
var cookie, acceptLang string
|
||||
for name, vals := range r.Header {
|
||||
v := ""
|
||||
if len(vals) > 0 {
|
||||
v = vals[0]
|
||||
}
|
||||
ordered = append(ordered, []string{name, v})
|
||||
switch http.CanonicalHeaderKey(name) {
|
||||
case "Cookie":
|
||||
cookie = v
|
||||
case "Accept-Language":
|
||||
acceptLang = v
|
||||
}
|
||||
}
|
||||
go sendFP(map[string]interface{}{
|
||||
"kind": "http_request_headers",
|
||||
"remote_addr": r.RemoteAddr,
|
||||
"proto_tag": "h3",
|
||||
"method": r.Method,
|
||||
"path": r.URL.Path,
|
||||
"headers_ordered": ordered,
|
||||
"cookie": cookie,
|
||||
"accept_language": acceptLang,
|
||||
"h3_order_note": "degraded_map_iteration",
|
||||
"ts": time.Now().UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
_ caddy.Provisioner = (*FPListenerWrapper)(nil)
|
||||
_ caddy.ListenerWrapper = (*FPListenerWrapper)(nil)
|
||||
_ caddyfile.Unmarshaler = (*FPListenerWrapper)(nil)
|
||||
_ caddy.Provisioner = (*FPHandler)(nil)
|
||||
_ caddyhttp.MiddlewareHandler = (*FPHandler)(nil)
|
||||
_ caddyfile.Unmarshaler = (*FPHandler)(nil)
|
||||
)
|
||||
|
||||
// ── caddy.logging.encoders.decnet_jsonl ──────────────────────────────────────
|
||||
|
||||
// DecnetJSONLEncoder is a registered Caddy module stub. A full zapcore.Encoder
|
||||
// implementation (required for `log { format decnet_jsonl }`) is deferred;
|
||||
// access_log records are emitted by FPHandler.ServeHTTP instead.
|
||||
type DecnetJSONLEncoder struct {
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (DecnetJSONLEncoder) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: "caddy.logging.encoders.decnet_jsonl",
|
||||
New: func() caddy.Module { return new(DecnetJSONLEncoder) },
|
||||
}
|
||||
}
|
||||
|
||||
func (e *DecnetJSONLEncoder) Provision(ctx caddy.Context) error {
|
||||
e.logger = ctx.Logger()
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user