Update dependencies

This commit is contained in:
Ingo Oppermann 2022-10-28 17:24:57 +02:00
parent 4334105f95
commit 4cc82dd333
No known key found for this signature in database
GPG Key ID: 2AB32426E9DD229E
273 changed files with 19686 additions and 3612 deletions

View File

@ -669,20 +669,6 @@ func (a *api) start() error {
}
certmagic.Default.DefaultServerName = cfg.Host.Name[0]
certmagic.Default.Logger = nil
certmagic.Default.OnEvent = func(event string, data interface{}) {
message := ""
switch data := data.(type) {
case string:
message = data
case fmt.Stringer:
message = data.String()
}
if len(message) != 0 {
a.log.logger.core.WithComponent("certmagic").Info().WithField("event", event).Log(message)
}
}
magic := certmagic.NewDefault()
acme := certmagic.NewACMEIssuer(magic, certmagic.DefaultACME)

View File

@ -3837,7 +3837,7 @@ const docTemplate = `{
"description": "The total number of received KM (Key Material) control packets",
"type": "integer"
},
"recv_loss__bytes": {
"recv_loss_bytes": {
"description": "Same as pktRcvLoss, but expressed in bytes, including payload and all the headers (IP, TCP, SRT), bytes for the presently missing (either reordered or lost) packets' payloads are estimated based on the average packet size",
"type": "integer"
},
@ -3945,7 +3945,7 @@ const docTemplate = `{
"description": "The total number of retransmitted packets sent by the SRT sender",
"type": "integer"
},
"sent_unique__bytes": {
"sent_unique_bytes": {
"description": "Same as pktSentUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)",
"type": "integer"
},

View File

@ -3829,7 +3829,7 @@
"description": "The total number of received KM (Key Material) control packets",
"type": "integer"
},
"recv_loss__bytes": {
"recv_loss_bytes": {
"description": "Same as pktRcvLoss, but expressed in bytes, including payload and all the headers (IP, TCP, SRT), bytes for the presently missing (either reordered or lost) packets' payloads are estimated based on the average packet size",
"type": "integer"
},
@ -3937,7 +3937,7 @@
"description": "The total number of retransmitted packets sent by the SRT sender",
"type": "integer"
},
"sent_unique__bytes": {
"sent_unique_bytes": {
"description": "Same as pktSentUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)",
"type": "integer"
},

View File

@ -979,7 +979,7 @@ definitions:
recv_km_pkt:
description: The total number of received KM (Key Material) control packets
type: integer
recv_loss__bytes:
recv_loss_bytes:
description: Same as pktRcvLoss, but expressed in bytes, including payload
and all the headers (IP, TCP, SRT), bytes for the presently missing (either
reordered or lost) packets' payloads are estimated based on the average
@ -1088,7 +1088,7 @@ definitions:
sent_retrans_pkt:
description: The total number of retransmitted packets sent by the SRT sender
type: integer
sent_unique__bytes:
sent_unique_bytes:
description: Same as pktSentUnique, but expressed in bytes, including payload
and all the headers (IP, TCP, SRT)
type: integer

54
go.mod
View File

@ -3,29 +3,29 @@ module github.com/datarhei/core/v16
go 1.18
require (
github.com/99designs/gqlgen v0.17.16
github.com/99designs/gqlgen v0.17.20
github.com/atrox/haikunatorgo/v2 v2.0.1
github.com/caddyserver/certmagic v0.16.2
github.com/datarhei/gosrt v0.2.1-0.20220817080252-d44df04a3845
github.com/caddyserver/certmagic v0.17.2
github.com/datarhei/gosrt v0.3.1
github.com/datarhei/joy4 v0.0.0-20220914170649-23c70d207759
github.com/go-playground/validator/v10 v10.11.0
github.com/go-playground/validator/v10 v10.11.1
github.com/gobwas/glob v0.2.3
github.com/golang-jwt/jwt/v4 v4.4.2
github.com/google/uuid v1.3.0
github.com/invopop/jsonschema v0.4.0
github.com/joho/godotenv v1.4.0
github.com/labstack/echo/v4 v4.9.0
github.com/labstack/echo/v4 v4.9.1
github.com/lithammer/shortuuid/v4 v4.0.0
github.com/mattn/go-isatty v0.0.16
github.com/prep/average v0.0.0-20200506183628-d26c465f48c3
github.com/prometheus/client_golang v1.13.0
github.com/shirou/gopsutil/v3 v3.22.8
github.com/stretchr/testify v1.8.0
github.com/swaggo/echo-swagger v1.3.4
github.com/swaggo/swag v1.8.5
github.com/vektah/gqlparser/v2 v2.5.0
github.com/shirou/gopsutil/v3 v3.22.9
github.com/stretchr/testify v1.8.1
github.com/swaggo/echo-swagger v1.3.5
github.com/swaggo/swag v1.8.7
github.com/vektah/gqlparser/v2 v2.5.1
github.com/xeipuuv/gojsonschema v1.2.0
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
golang.org/x/mod v0.6.0
)
require (
@ -49,20 +49,20 @@ require (
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/iancoleman/orderedmap v0.2.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/cpuid/v2 v2.0.11 // indirect
github.com/labstack/gommon v0.3.1 // indirect
github.com/klauspost/cpuid/v2 v2.1.2 // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/libdns/libdns v0.2.1 // indirect
github.com/lufia/plan9stats v0.0.0-20220517141722-cf486979b281 // indirect
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mholt/acmez v1.0.4 // indirect
github.com/miekg/dns v1.1.46 // indirect
github.com/miekg/dns v1.1.50 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
@ -71,20 +71,20 @@ require (
github.com/tklauser/numcpus v0.5.0 // indirect
github.com/urfave/cli/v2 v2.8.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.21.0 // indirect
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
golang.org/x/net v0.0.0-20220907135653-1e95f45603a7 // indirect
golang.org/x/sys v0.0.0-20220907062415-87db552b00fd // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
golang.org/x/tools v0.1.12 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.23.0 // indirect
golang.org/x/crypto v0.1.0 // indirect
golang.org/x/net v0.1.0 // indirect
golang.org/x/sys v0.1.0 // indirect
golang.org/x/text v0.4.0 // indirect
golang.org/x/time v0.1.0 // indirect
golang.org/x/tools v0.2.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

111
go.sum
View File

@ -31,8 +31,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/99designs/gqlgen v0.17.16 h1:tTIw/cQ/uvf3iXIb2I6YSkdaDkmHmH2W2eZkVe0IVLA=
github.com/99designs/gqlgen v0.17.16/go.mod h1:dnJdUkgfh8iw8CEx2hhTdgTQO/GvVWKLcm/kult5gwI=
github.com/99designs/gqlgen v0.17.20 h1:O7WzccIhKB1dm+7g6dhQcULINftfiLSBg2l/mwbpJMw=
github.com/99designs/gqlgen v0.17.20/go.mod h1:Mja2HI23kWT1VRH09hvWshFgOzKswpO20o4ScpJIES4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
@ -63,8 +63,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/caddyserver/certmagic v0.16.2 h1:k2n3LkkUG3aMUK/kckMuF9/0VFo+0FtMX3drPYESbmQ=
github.com/caddyserver/certmagic v0.16.2/go.mod h1:PgLIr/dSJa+WA7t7z6Je5xuS/e5A/GFCPHRuZ1QP+MQ=
github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE=
github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
@ -78,8 +78,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/datarhei/gosrt v0.2.1-0.20220817080252-d44df04a3845 h1:nlVb4EVMwdVUwH6e10WZrx4lW0n2utnlE+4ILMPyD5o=
github.com/datarhei/gosrt v0.2.1-0.20220817080252-d44df04a3845/go.mod h1:wyoTu+DG45XRuCgEq/y+R8nhZCrJbOyQKn+SwNrNVZ8=
github.com/datarhei/gosrt v0.3.1 h1:9A75hIvnY74IUFyeguqYXh1lsGF8Qt8fjxJS2Ewr12Q=
github.com/datarhei/gosrt v0.3.1/go.mod h1:M2nl2WPrawncUc1FtUBK6gZX4tpZRC7FqL8NjOdBZV0=
github.com/datarhei/joy4 v0.0.0-20220914170649-23c70d207759 h1:h8NyekuQSDvLIsZVTV172m5/RVArXkEM/cnHaUzszQU=
github.com/datarhei/joy4 v0.0.0-20220914170649-23c70d207759/go.mod h1:Jcw/6jZDQQmPx8A7INEkXmuEF7E9jjBbSTfVSLwmiQw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -124,8 +124,8 @@ github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
@ -174,8 +174,8 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@ -220,8 +220,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3roToPzKNM8dtdM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.0.11 h1:i2lw1Pm7Yi/4O6XCSyJWqEHI2MDw2FzUK6o/D21xn2A=
github.com/klauspost/cpuid/v2 v2.0.11/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.1.2 h1:XhdX4fqAJUA0yj+kUwMavO0hHrSPAecYdYf1ZmxHvak=
github.com/klauspost/cpuid/v2 v2.1.2/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
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.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@ -233,11 +233,12 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v4 v4.7.2/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
github.com/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITVY=
github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
github.com/labstack/echo/v4 v4.9.1 h1:GliPYSpzGKlyOhqIbG8nmHBo3i1saKWFOgh41AN3b+Y=
github.com/labstack/echo/v4 v4.9.1/go.mod h1:Pop5HLc+xoc4qhTZ1ip6C0RtP7Z+4VzRLWZZFKqbbjo=
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
@ -246,8 +247,8 @@ github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw
github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y=
github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lufia/plan9stats v0.0.0-20220517141722-cf486979b281 h1:aczX6NMOtt6L4YT0fQvKkDK6LZEtdOso9sUH89V1+P0=
github.com/lufia/plan9stats v0.0.0-20220517141722-cf486979b281/go.mod h1:lc+czkgO/8F7puNki5jk8QyujbfK1LOT7Wl0ON2hxyk=
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c h1:VtwQ41oftZwlMnOEbMWQtSEUgU64U4s+GHk7hZK+jtY=
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
@ -255,18 +256,18 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/VUBk=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
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.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80=
github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY=
github.com/miekg/dns v1.1.46 h1:uzwpxRtSVxtcIZmz/4Uz6/Rn7G11DvsaslXoy5LxQio=
github.com/miekg/dns v1.1.46/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
@ -306,8 +307,9 @@ github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
@ -330,8 +332,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shirou/gopsutil/v3 v3.22.8 h1:a4s3hXogo5mE2PfdfJIonDbstO/P+9JszdfhAHSzD9Y=
github.com/shirou/gopsutil/v3 v3.22.8/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI=
github.com/shirou/gopsutil/v3 v3.22.9 h1:yibtJhIVEMcdw+tCTbOPiF1VcsuDeTE4utJ8Dm4c5eA=
github.com/shirou/gopsutil/v3 v3.22.9/go.mod h1:bBYl1kjgEJpWpxeHmLI+dVHWtyAwfcmSBLDsp2TNT8A=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@ -341,6 +343,7 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9
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.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.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@ -348,16 +351,16 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
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.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/swaggo/echo-swagger v1.3.4 h1:8B+yVqjVm7cMy4QBLRUuRaOzrTVAqZahcrgrOSdpC5I=
github.com/swaggo/echo-swagger v1.3.4/go.mod h1:vh8QAdbHtTXwTSaWzc1Nby7zMYJd/g0FwQyArmrFHA8=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/swaggo/echo-swagger v1.3.5 h1:kCx1wvX5AKhjI6Ykt48l3PTsfL9UD40ZROOx/tYzWyY=
github.com/swaggo/echo-swagger v1.3.5/go.mod h1:3IMHd2Z8KftdWFEEjGmv6QpWj370LwMCOfovuh7vF34=
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a h1:kAe4YSu0O0UFn1DowNo2MY5p6xzqtJ/wQ7LZynSvGaY=
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ=
github.com/swaggo/swag v1.8.5 h1:7NgtfXsXE+jrcOwRyiftGKW7Ppydj7tZiVenuRf1fE4=
github.com/swaggo/swag v1.8.5/go.mod h1:jMLeXOOmYyjk8PvHTsXBdrubsNd9gUJTTCzL5iBnseg=
github.com/swaggo/swag v1.8.7 h1:2K9ivTD3teEO+2fXV6zrZKDqk5IuU2aJtBDo8U7omWU=
github.com/swaggo/swag v1.8.7/go.mod h1:ezQVUUhly8dludpVk+/PuwJWvLLanB13ygV5Pr9enSk=
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
@ -368,10 +371,11 @@ github.com/urfave/cli/v2 v2.8.1 h1:CGuYNZF9IKZY/rfBe3lJpccSoIY1ytfvmgQT90cNOl4=
github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vektah/gqlparser/v2 v2.5.0 h1:GwEwy7AJsqPWrey0bHnn+3JLaHLZVT66wY/+O+Tf9SU=
github.com/vektah/gqlparser/v2 v2.5.0/go.mod h1:mPgqFBu/woKTVYWyNk8cO3kh4S/f4aRFZrvOnp3hmCs=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vektah/gqlparser/v2 v2.5.1 h1:ZGu+bquAY23jsxDRcYpWjttRZrUz07LbiY77gUOHcr4=
github.com/vektah/gqlparser/v2 v2.5.1/go.mod h1:mPgqFBu/woKTVYWyNk8cO3kh4S/f4aRFZrvOnp3hmCs=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@ -387,6 +391,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@ -394,14 +399,17 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -412,9 +420,8 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -447,8 +454,9 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -489,8 +497,9 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220907135653-1e95f45603a7 h1:1WGATo9HAhkWMbfyuVU0tEFP88OIkUvwaHFveQPvzCQ=
golang.org/x/net v0.0.0-20220907135653-1e95f45603a7/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -509,6 +518,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -562,25 +572,29 @@ golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/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-20220704084225-05e143d24a9e/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.0.0-20220907062415-87db552b00fd h1:AZeIEzg+8RCELJYq8w+ODLVxFgLMMigSwO/ffKPEd9U=
golang.org/x/sys v0.0.0-20220907062415-87db552b00fd/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ=
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA=
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -626,8 +640,9 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -33,9 +33,9 @@ type SRTStatistics struct {
ByteSent uint64 `json:"sent_bytes"` // Same as pktSent, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecv uint64 `json:"recv_bytes"` // Same as pktRecv, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteSentUnique uint64 `json:"sent_unique__bytes"` // Same as pktSentUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteSentUnique uint64 `json:"sent_unique_bytes"` // Same as pktSentUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecvUnique uint64 `json:"recv_unique_bytes"` // Same as pktRecvUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRcvLoss uint64 `json:"recv_loss__bytes"` // Same as pktRcvLoss, but expressed in bytes, including payload and all the headers (IP, TCP, SRT), bytes for the presently missing (either reordered or lost) packets' payloads are estimated based on the average packet size
ByteRcvLoss uint64 `json:"recv_loss_bytes"` // Same as pktRcvLoss, but expressed in bytes, including payload and all the headers (IP, TCP, SRT), bytes for the presently missing (either reordered or lost) packets' payloads are estimated based on the average packet size
ByteRetrans uint64 `json:"sent_retrans_bytes"` // Same as pktRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteSndDrop uint64 `json:"send_drop_bytes"` // Same as pktSndDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRcvDrop uint64 `json:"recv_drop_bytes"` // Same as pktRcvDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
@ -68,54 +68,54 @@ type SRTStatistics struct {
func (s *SRTStatistics) Unmarshal(ss *gosrt.Statistics) {
s.MsTimeStamp = ss.MsTimeStamp
s.PktSent = ss.PktSent
s.PktRecv = ss.PktRecv
s.PktSentUnique = ss.PktSentUnique
s.PktRecvUnique = ss.PktRecvUnique
s.PktSndLoss = ss.PktSndLoss
s.PktRcvLoss = ss.PktRcvLoss
s.PktRetrans = ss.PktRetrans
s.PktRcvRetrans = ss.PktRcvRetrans
s.PktSentACK = ss.PktSentACK
s.PktRecvACK = ss.PktRecvACK
s.PktSentNAK = ss.PktSentNAK
s.PktRecvNAK = ss.PktRecvNAK
s.PktSentKM = ss.PktSentKM
s.PktRecvKM = ss.PktRecvKM
s.UsSndDuration = ss.UsSndDuration
s.PktSndDrop = ss.PktSndDrop
s.PktRcvDrop = ss.PktRcvDrop
s.PktRcvUndecrypt = ss.PktRcvUndecrypt
s.PktSent = ss.Accumulated.PktSent
s.PktRecv = ss.Accumulated.PktRecv
s.PktSentUnique = ss.Accumulated.PktSentUnique
s.PktRecvUnique = ss.Accumulated.PktRecvUnique
s.PktSndLoss = ss.Accumulated.PktSendLoss
s.PktRcvLoss = ss.Accumulated.PktRecvLoss
s.PktRetrans = ss.Accumulated.PktRetrans
s.PktRcvRetrans = ss.Accumulated.PktRecvRetrans
s.PktSentACK = ss.Accumulated.PktSentACK
s.PktRecvACK = ss.Accumulated.PktRecvACK
s.PktSentNAK = ss.Accumulated.PktSentNAK
s.PktRecvNAK = ss.Accumulated.PktRecvNAK
s.PktSentKM = ss.Accumulated.PktSentKM
s.PktRecvKM = ss.Accumulated.PktRecvKM
s.UsSndDuration = ss.Accumulated.UsSndDuration
s.PktSndDrop = ss.Accumulated.PktSendDrop
s.PktRcvDrop = ss.Accumulated.PktRecvDrop
s.PktRcvUndecrypt = ss.Accumulated.PktRecvUndecrypt
s.ByteSent = ss.ByteSent
s.ByteRecv = ss.ByteRecv
s.ByteSentUnique = ss.ByteSentUnique
s.ByteRecvUnique = ss.ByteRecvUnique
s.ByteRcvLoss = ss.ByteRcvLoss
s.ByteRetrans = ss.ByteRetrans
s.ByteSndDrop = ss.ByteSndDrop
s.ByteRcvDrop = ss.ByteRcvDrop
s.ByteRcvUndecrypt = ss.ByteRcvUndecrypt
s.ByteSent = ss.Accumulated.ByteSent
s.ByteRecv = ss.Accumulated.ByteRecv
s.ByteSentUnique = ss.Accumulated.ByteSentUnique
s.ByteRecvUnique = ss.Accumulated.ByteRecvUnique
s.ByteRcvLoss = ss.Accumulated.ByteRecvLoss
s.ByteRetrans = ss.Accumulated.ByteRetrans
s.ByteSndDrop = ss.Accumulated.ByteSendDrop
s.ByteRcvDrop = ss.Accumulated.ByteRecvDrop
s.ByteRcvUndecrypt = ss.Accumulated.ByteRecvUndecrypt
s.UsPktSndPeriod = ss.UsPktSndPeriod
s.PktFlowWindow = ss.PktFlowWindow
s.PktFlightSize = ss.PktFlightSize
s.MsRTT = ss.MsRTT
s.MbpsBandwidth = ss.MbpsBandwidth
s.ByteAvailSndBuf = ss.ByteAvailSndBuf
s.ByteAvailRcvBuf = ss.ByteAvailRcvBuf
s.MbpsMaxBW = ss.MbpsMaxBW
s.ByteMSS = ss.ByteMSS
s.PktSndBuf = ss.PktSndBuf
s.ByteSndBuf = ss.ByteSndBuf
s.MsSndBuf = ss.MsSndBuf
s.MsSndTsbPdDelay = ss.MsSndTsbPdDelay
s.PktRcvBuf = ss.PktRcvBuf
s.ByteRcvBuf = ss.ByteRcvBuf
s.MsRcvBuf = ss.MsRcvBuf
s.MsRcvTsbPdDelay = ss.MsRcvTsbPdDelay
s.PktReorderTolerance = ss.PktReorderTolerance
s.PktRcvAvgBelatedTime = ss.PktRcvAvgBelatedTime
s.UsPktSndPeriod = ss.Instantaneous.UsPktSendPeriod
s.PktFlowWindow = ss.Instantaneous.PktFlowWindow
s.PktFlightSize = ss.Instantaneous.PktFlightSize
s.MsRTT = ss.Instantaneous.MsRTT
s.MbpsBandwidth = ss.Instantaneous.MbpsLinkCapacity
s.ByteAvailSndBuf = ss.Instantaneous.ByteAvailSendBuf
s.ByteAvailRcvBuf = ss.Instantaneous.ByteAvailRecvBuf
s.MbpsMaxBW = ss.Instantaneous.MbpsMaxBW
s.ByteMSS = ss.Instantaneous.ByteMSS
s.PktSndBuf = ss.Instantaneous.PktSendBuf
s.ByteSndBuf = ss.Instantaneous.ByteSendBuf
s.MsSndBuf = ss.Instantaneous.MsSendBuf
s.MsSndTsbPdDelay = ss.Instantaneous.MsSendTsbPdDelay
s.PktRcvBuf = ss.Instantaneous.PktRecvBuf
s.ByteRcvBuf = ss.Instantaneous.ByteRecvBuf
s.MsRcvBuf = ss.Instantaneous.MsRecvBuf
s.MsRcvTsbPdDelay = ss.Instantaneous.MsRecvTsbPdDelay
s.PktReorderTolerance = ss.Instantaneous.PktReorderTolerance
s.PktRcvAvgBelatedTime = ss.Instantaneous.PktRecvAvgBelatedTime
}
type SRTLog struct {

View File

@ -53,15 +53,17 @@ func (c *client) ticker(ctx context.Context) {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
stats := &srt.Statistics{}
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
stats := c.conn.Stats()
c.conn.Stats(stats)
rxbytes := stats.ByteRecv
txbytes := stats.ByteSent
rxbytes := stats.Accumulated.ByteRecv
txbytes := stats.Accumulated.ByteSent
c.collector.Ingress(c.id, int64(rxbytes-c.rxbytes))
c.collector.Egress(c.id, int64(txbytes-c.txbytes))
@ -285,8 +287,11 @@ func (s *server) Channels() Channels {
socketId := ch.publisher.conn.SocketId()
st.Publisher[id] = socketId
stats := &srt.Statistics{}
ch.publisher.conn.Stats(stats)
st.Connections[socketId] = Connection{
Stats: ch.publisher.conn.Stats(),
Stats: *stats,
Log: map[string][]Log{},
}
@ -294,8 +299,11 @@ func (s *server) Channels() Channels {
socketId := c.conn.SocketId()
st.Subscriber[id] = append(st.Subscriber[id], socketId)
stats := &srt.Statistics{}
c.conn.Stats(stats)
st.Connections[socketId] = Connection{
Stats: c.conn.Stats(),
Stats: *stats,
Log: map[string][]Log{},
}
}

View File

@ -5,10 +5,138 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
<a name="unreleased"></a>
## [Unreleased](https://github.com/99designs/gqlgen/compare/v0.17.14...HEAD)
## [Unreleased](https://github.com/99designs/gqlgen/compare/v0.17.19...HEAD)
<!-- end of if -->
<!-- end of CommitGroups -->
<a name="v0.17.19"></a>
## [v0.17.19](https://github.com/99designs/gqlgen/compare/v0.17.18...v0.17.19) - 2022-09-15
- <a href="https://github.com/99designs/gqlgen/commit/588c6ac137b8ed7aea1bc7c009ea23cb9dec5caa"><tt>588c6ac1</tt></a> release v0.17.19
- <a href="https://github.com/99designs/gqlgen/commit/c671317056298db8073498c8db02120b6f737032"><tt>c6713170</tt></a> v0.17.18 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.18"></a>
## [v0.17.18](https://github.com/99designs/gqlgen/compare/v0.17.17...v0.17.18) - 2022-09-15
- <a href="https://github.com/99designs/gqlgen/commit/1d41c808a93446fca8ff867e957ef552e56f6ae3"><tt>1d41c808</tt></a> release v0.17.18
- <a href="https://github.com/99designs/gqlgen/commit/4dbe2e475f15ce77a498c841ea6c9149ef5ceaba"><tt>4dbe2e47</tt></a> update graphiql to 2.0.7 (<a href="https://github.com/99designs/gqlgen/pull/2375">#2375</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/b7cc094a49e3d348cfc457aa76f1640c86cdcae9"><tt>b7cc094a</tt></a> testfix: make apollo federated tracer test more consistent (<a href="https://github.com/99designs/gqlgen/pull/2374">#2374</a>)</summary>
* Update tracing_test.go
* add missing imports
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/d096fb9b08531b0dc389a786b6f44add045ea75e"><tt>d096fb9b</tt></a> Update directives (<a href="https://github.com/99designs/gqlgen/pull/2371">#2371</a>)
- <a href="https://github.com/99designs/gqlgen/commit/1acfea2fbdf3564df16f8023f4e736e90a05b909"><tt>1acfea2f</tt></a> Add v0.17.17 changelog
- <a href="https://github.com/99designs/gqlgen/commit/c273adc8ad45e15940bbb6fe211603670d9f3220"><tt>c273adc8</tt></a> v0.17.17 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.17"></a>
## [v0.17.17](https://github.com/99designs/gqlgen/compare/v0.17.16...v0.17.17) - 2022-09-13
- <a href="https://github.com/99designs/gqlgen/commit/d50bc5aca10c5a5dd6a1680b2288c35a61327ade"><tt>d50bc5ac</tt></a> release v0.17.17
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/462025b400e9b792a5afbe320cde4cc952f6b547"><tt>462025b4</tt></a> nil check error before type assertion follow-up from <a href="https://github.com/99designs/gqlgen/pull/2341">#2341</a> (<a href="https://github.com/99designs/gqlgen/pull/2368">#2368</a>)</summary>
* Improve errcode.Set safety
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/59493aff86020d170e58900654d334f5ebc2ceee"><tt>59493aff</tt></a> fix: apollo federation tracer was race prone (<a href="https://github.com/99designs/gqlgen/pull/2366">#2366</a>)</summary>
The tracer was using a global state across different goroutines
Added req headers to operation context to allow it to be fetched in InterceptOperation
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/fc0185567f2dfc37b38f11283efb9cc1db69e96d"><tt>fc018556</tt></a> Update gqlparser to v2.5.1 (<a href="https://github.com/99designs/gqlgen/pull/2363">#2363</a>)
- <a href="https://github.com/99designs/gqlgen/commit/56574a146bd16a13c9055128ec3c80e96a7c4b29"><tt>56574a14</tt></a> feat: make Playground HTML content compatible with UTF-8 charset (<a href="https://github.com/99designs/gqlgen/pull/2355">#2355</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/182b039d34cb730f432c486ebe763f246937dea4"><tt>182b039d</tt></a> Add `subscriptions.md` recipe to docs (<a href="https://github.com/99designs/gqlgen/pull/2346">#2346</a>)</summary>
* Add `subscriptions.md` recipe to docs
* Fix wrong request type
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/b66fff16de0b16edc317398a5574fcff2cb39e66"><tt>b66fff16</tt></a> Add omit_getters config option (<a href="https://github.com/99designs/gqlgen/pull/2348">#2348</a>)
- <a href="https://github.com/99designs/gqlgen/commit/2ba8040f20e32d06dc6d5bfacaadc5619a6e66ee"><tt>2ba8040f</tt></a> Update changelog for v0.17.16
- <a href="https://github.com/99designs/gqlgen/commit/8bef8c8061222071e6c814e45bbc33fcabcb3980"><tt>8bef8c80</tt></a> v0.17.16 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.16"></a>
## [v0.17.16](https://github.com/99designs/gqlgen/compare/v0.17.15...v0.17.16) - 2022-08-26
- <a href="https://github.com/99designs/gqlgen/commit/9593ceadd6e07c6fd0f0b0e0c55b9f1bf8ade762"><tt>9593cead</tt></a> release v0.17.16
- <a href="https://github.com/99designs/gqlgen/commit/2390af2db920dc632fe47bc778a24c30495b9efd"><tt>2390af2d</tt></a> Update gqlparser to v2.5.0 (<a href="https://github.com/99designs/gqlgen/pull/2341">#2341</a>)
- <a href="https://github.com/99designs/gqlgen/commit/2a87fe0645fd271e4e71d2b7bde34ecf31bf844c"><tt>2a87fe06</tt></a> feat: update Graphiql to version 2 (<a href="https://github.com/99designs/gqlgen/pull/2340">#2340</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/32e2ccd30e82fc566ca022a65dcc4a67c4b6125a"><tt>32e2ccd3</tt></a> Update yaml to v3 (<a href="https://github.com/99designs/gqlgen/pull/2339">#2339</a>)</summary>
* update yaml to v3
* add missing go entry for yaml on _example
* add missing sum file
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/7949117a524be7f8882a61e2d4ade1bedf105107"><tt>7949117a</tt></a> v0.17.15 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.15"></a>
## [v0.17.15](https://github.com/99designs/gqlgen/compare/v0.17.14...v0.17.15) - 2022-08-23
- <a href="https://github.com/99designs/gqlgen/commit/23cc749256b4e2edc4b11ce9e84c643a7bb3194f"><tt>23cc7492</tt></a> release v0.17.15
- <a href="https://github.com/99designs/gqlgen/commit/577a570cdb6b1b9185f24940690a14cdced37a36"><tt>577a570c</tt></a> Markdown formatting fixes (<a href="https://github.com/99designs/gqlgen/pull/2335">#2335</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/2b584011fc64a55cbda67f46637a280bf94d9cc1"><tt>2b584011</tt></a> Fix Interface Slice Getter Generation (<a href="https://github.com/99designs/gqlgen/pull/2332">#2332</a>)</summary>
* Make modelgen test fail if generated doesn't build
Added returning list of interface to modelgen test schema
* Implement slice copying when returning interface slices
* Re-generate to satisfy the linter
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/aee57b4c521e527ebc0538b8edfbe610973abf21"><tt>aee57b4c</tt></a> Correct boolean logic (<a href="https://github.com/99designs/gqlgen/pull/2330">#2330</a>)</summary>
Correcting boolean logic issue
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/da0610e11accf3afd34903f03bfc0abd045d07ed"><tt>da0610e1</tt></a> Update changelog for v0.17.14
- <a href="https://github.com/99designs/gqlgen/commit/ddcb524e3321d849505f6937307ef3dcbd3acace"><tt>ddcb524e</tt></a> v0.17.14 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.14"></a>
## [v0.17.14](https://github.com/99designs/gqlgen/compare/v0.17.13...v0.17.14) - 2022-08-18
- <a href="https://github.com/99designs/gqlgen/commit/581bf6eb063a0d6a3cec3b6bc7a16ca10e310a97"><tt>581bf6eb</tt></a> release v0.17.14

View File

@ -142,6 +142,16 @@ first model in this list is used as the default type and it will always be used
There isn't any way around this, gqlgen has no way to know what you want in a given context.
### Why do my interfaces have getters? Can I disable these?
These were added in v0.17.14 to allow accessing common interface fields without casting to a concrete type.
However, certain fields, like Relay-style Connections, cannot be implemented with simple getters.
If you'd prefer to not have getters generated in your interfaces, you can add the following in your `gqlgen.yml`:
```yaml
# gqlgen.yml
omit_getters: true
```
## Other Resources
- [Christopher Biscardi @ Gophercon UK 2018](https://youtu.be/FdURVezcdcw)

View File

@ -26,6 +26,7 @@ type Config struct {
StructTag string `yaml:"struct_tag,omitempty"`
Directives map[string]DirectiveConfig `yaml:"directives,omitempty"`
OmitSliceElementPointers bool `yaml:"omit_slice_element_pointers,omitempty"`
OmitGetters bool `yaml:"omit_getters,omitempty"`
StructFieldsAlwaysPointers bool `yaml:"struct_fields_always_pointers,omitempty"`
ResolversAlwaysReturnPointers bool `yaml:"resolvers_always_return_pointers,omitempty"`
SkipValidation bool `yaml:"skip_validation,omitempty"`

View File

@ -3,6 +3,7 @@ package graphql
import (
"context"
"errors"
"net/http"
"github.com/vektah/gqlparser/v2/ast"
)
@ -15,6 +16,7 @@ type OperationContext struct {
Variables map[string]interface{}
OperationName string
Doc *ast.QueryDocument
Headers http.Header
Operation *ast.OperationDefinition
DisableIntrospection bool

View File

@ -23,14 +23,22 @@ var codeType = map[string]ErrorKind{
ParseFailed: KindProtocol,
}
// RegisterErrorType should be called by extensions that want to customize the http status codes for errors they return
// RegisterErrorType should be called by extensions that want to customize the http status codes for
// errors they return
func RegisterErrorType(code string, kind ErrorKind) {
codeType[code] = kind
}
// Set the error code on a given graphql error extension
func Set(err error, value string) {
gqlErr, _ := err.(*gqlerror.Error)
if err == nil {
return
}
gqlErr, ok := err.(*gqlerror.Error)
if !ok {
return
}
if gqlErr.Extensions == nil {
gqlErr.Extensions = map[string]interface{}{}
}

View File

@ -118,6 +118,11 @@ func getOrCreateAndAppendField(c *[]CollectedField, name string, alias string, o
return &(*c)[i]
}
}
for _, ifc := range cf.ObjectDefinition.Interfaces {
if ifc == objectDefinition.Name {
return &(*c)[i]
}
}
}
}

View File

@ -37,7 +37,10 @@ func New(es graphql.ExecutableSchema) *Executor {
return e
}
func (e *Executor) CreateOperationContext(ctx context.Context, params *graphql.RawParams) (*graphql.OperationContext, gqlerror.List) {
func (e *Executor) CreateOperationContext(
ctx context.Context,
params *graphql.RawParams,
) (*graphql.OperationContext, gqlerror.List) {
rc := &graphql.OperationContext{
DisableIntrospection: true,
RecoverFunc: e.recoverFunc,
@ -58,6 +61,7 @@ func (e *Executor) CreateOperationContext(ctx context.Context, params *graphql.R
rc.RawQuery = params.Query
rc.OperationName = params.OperationName
rc.Headers = params.Headers
var listErr gqlerror.List
rc.Doc, listErr = e.parseQuery(ctx, &rc.Stats, params.Query)
@ -74,10 +78,13 @@ func (e *Executor) CreateOperationContext(ctx context.Context, params *graphql.R
var err error
rc.Variables, err = validator.VariableValues(e.es.Schema(), rc.Operation, params.Variables)
gqlErr, _ := err.(*gqlerror.Error)
if gqlErr != nil {
errcode.Set(gqlErr, errcode.ValidationFailed)
return rc, gqlerror.List{gqlErr}
if err != nil {
gqlErr, ok := err.(*gqlerror.Error)
if ok {
errcode.Set(gqlErr, errcode.ValidationFailed)
return rc, gqlerror.List{gqlErr}
}
}
rc.Stats.Validation.End = graphql.Now()
@ -90,7 +97,10 @@ func (e *Executor) CreateOperationContext(ctx context.Context, params *graphql.R
return rc, nil
}
func (e *Executor) DispatchOperation(ctx context.Context, rc *graphql.OperationContext) (graphql.ResponseHandler, context.Context) {
func (e *Executor) DispatchOperation(
ctx context.Context,
rc *graphql.OperationContext,
) (graphql.ResponseHandler, context.Context) {
ctx = graphql.WithOperationContext(ctx, rc)
var innerCtx context.Context
@ -160,9 +170,14 @@ func (e *Executor) SetRecoverFunc(f graphql.RecoverFunc) {
// parseQuery decodes the incoming query and validates it, pulling from cache if present.
//
// NOTE: This should NOT look at variables, they will change per request. It should only parse and validate
// NOTE: This should NOT look at variables, they will change per request. It should only parse and
// validate
// the raw query string.
func (e *Executor) parseQuery(ctx context.Context, stats *graphql.Stats, query string) (*ast.QueryDocument, gqlerror.List) {
func (e *Executor) parseQuery(
ctx context.Context,
stats *graphql.Stats,
query string,
) (*ast.QueryDocument, gqlerror.List) {
stats.Parsing.Start = graphql.Now()
if doc, ok := e.queryCache.Get(ctx, query); ok {
@ -174,10 +189,12 @@ func (e *Executor) parseQuery(ctx context.Context, stats *graphql.Stats, query s
}
doc, err := parser.ParseQuery(&ast.Source{Input: query})
gqlErr, _ := err.(*gqlerror.Error)
if gqlErr != nil {
errcode.Set(gqlErr, errcode.ParseFailed)
return nil, gqlerror.List{gqlErr}
if err != nil {
gqlErr, ok := err.(*gqlerror.Error)
if ok {
errcode.Set(gqlErr, errcode.ParseFailed)
return nil, gqlerror.List{gqlErr}
}
}
stats.Parsing.End = graphql.Now()

View File

@ -42,14 +42,14 @@ type (
// Its important to understand the lifecycle of a graphql request and the terminology we use in gqlgen
// before working with these
//
// +--- REQUEST POST /graphql --------------------------------------------+
// | +- OPERATION query OpName { viewer { name } } -----------------------+ |
// | | RESPONSE { "data": { "viewer": { "name": "bob" } } } | |
// | +- OPERATION subscription OpName2 { chat { message } } --------------+ |
// | | RESPONSE { "data": { "chat": { "message": "hello" } } } | |
// | | RESPONSE { "data": { "chat": { "message": "byee" } } } | |
// | +--------------------------------------------------------------------+ |
// +------------------------------------------------------------------------+
// +--- REQUEST POST /graphql --------------------------------------------+
// | +- OPERATION query OpName { viewer { name } } -----------------------+ |
// | | RESPONSE { "data": { "viewer": { "name": "bob" } } } | |
// | +- OPERATION subscription OpName2 { chat { message } } --------------+ |
// | | RESPONSE { "data": { "chat": { "message": "hello" } } } | |
// | | RESPONSE { "data": { "chat": { "message": "byee" } } } | |
// | +--------------------------------------------------------------------+ |
// +------------------------------------------------------------------------+
HandlerExtension interface {
// ExtensionName should be a CamelCase string version of the extension which may be shown in stats and logging.
ExtensionName() string

View File

@ -9,6 +9,7 @@ import (
var page = template.Must(template.New("graphiql").Parse(`<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{.title}}</title>
<style>
body {
@ -75,15 +76,15 @@ var page = template.Must(template.New("graphiql").Parse(`<!DOCTYPE html>
// Handler responsible for setting up the playground
func Handler(title string, endpoint string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "text/html")
w.Header().Add("Content-Type", "text/html; charset=UTF-8")
err := page.Execute(w, map[string]interface{}{
"title": title,
"endpoint": endpoint,
"endpointIsAbsolute": endpointHasScheme(endpoint),
"subscriptionEndpoint": getSubscriptionEndpoint(endpoint),
"version": "2.0.1",
"cssSRI": "sha256-hYUgpHapGug0ucdB5kG0zSipubcQOJcGjclIZke2rl8=",
"jsSRI": "sha256-jMXGO5+Y4OhcHPSR34jpzpzlz4OZTlxcvaDXSWmUMRo=",
"version": "2.0.7",
"cssSRI": "sha256-gQryfbGYeYFxnJYnfPStPYFt0+uv8RP8Dm++eh00G9c=",
"jsSRI": "sha256-qQ6pw7LwTLC+GfzN+cJsYXfVWRKH9O5o7+5H96gTJhQ=",
"reactSRI": "sha256-Ipu/TQ50iCCVZBUsZyNJfxrDk0E2yhaEIz0vqI+kFG8=",
"reactDOMSRI": "sha256-nbMykgB6tsOFJ7OdVmPpdqMFVk4ZsqWocT6issAPUF0=",
})

View File

@ -1,3 +1,3 @@
package graphql
const Version = "v0.17.16"
const Version = "v0.17.20"

View File

@ -102,10 +102,10 @@ func (f *federation) InjectSourceEarly() *ast.Source {
`
} else if f.Version == 2 {
input += `
directive @key(fields: _FieldSet!, resolvable: Boolean) repeatable on OBJECT | INTERFACE
directive @key(fields: _FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
directive @link(import: [String!], url: String!) repeatable on SCHEMA
directive @shareable on OBJECT | FIELD_DEFINITION
directive @tag repeatable on OBJECT | FIELD_DEFINITION | INTERFACE | UNION
directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
directive @override(from: String!) on FIELD_DEFINITION
directive @inaccessible on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
`

View File

@ -103,9 +103,13 @@ func (m *Plugin) MutateConfig(cfg *config.Config) error {
}
switch schemaType.Kind {
case ast.Interface, ast.Union:
fields, err := m.generateFields(cfg, schemaType)
if err != nil {
return err
var fields []*Field
var err error
if !cfg.OmitGetters {
fields, err = m.generateFields(cfg, schemaType)
if err != nil {
return err
}
}
it := &Interface{

View File

@ -458,6 +458,42 @@ Most applications will not need to interact with certificate caches directly. Us
Again, if you're needing to do this, you've probably over-complicated your application design.
## Events
(Events are new and still experimental, so they may change.)
CertMagic emits events when possible things of interest happen. Set the [`OnEvent` field of your `Config`](https://pkg.go.dev/github.com/caddyserver/certmagic#Config.OnEvent) to subscribe to events; ignore the ones you aren't interested in. Here are the events currently emitted along with their metadata you can use:
- **`cached_unmanaged_cert`** An unmanaged certificate was cached
- `sans`: The subject names on the certificate
- **`cert_obtaining`** A certificate is about to be obtained
- `renewal`: Whether this is a renewal
- `identifier`: The name on the certificate
- `forced`: Whether renewal is being forced (if renewal)
- `remaining`: Time left on the certificate (if renewal)
- `issuer`: The previous or current issuer
- **`cert_obtained`** A certificate was successfully obtained
- `renewal`: Whether this is a renewal
- `identifier`: The name on the certificate
- `remaining`: Time left on the certificate (if renewal)
- `issuer`: The previous or current issuer
- `storage_key`: The path to the cert resources within storage
- **`cert_failed`** An attempt to obtain a certificate failed
- `renewal`: Whether this is a renewal
- `identifier`: The name on the certificate
- `remaining`: Time left on the certificate (if renewal)
- `issuer`: The previous or current issuer
- `storage_key`: The path to the cert resources within storage
- `error`: The (final) error message
- **`tls_get_certificate`** The GetCertificate phase of a TLS handshake is under way
- `client_hello`: The tls.ClientHelloInfo struct
- **`cert_ocsp_revoked`** A certificate's OCSP indicates it has been revoked
- `subjects`: The subject names on the certificate
- `certificate`: The Certificate struct
- `reason`: The OCSP revocation reason
- `revoked_at`: When the certificate was revoked
`OnEvent` can return an error. Some events may be aborted by returning an error. For example, returning an error from `cert_obtained` can cancel obtaining the certificate. Only return an error from `OnEvent` if you want to abort program flow.
## FAQ

View File

@ -175,9 +175,7 @@ func (iss *ACMEIssuer) newACMEClient(useTestCA bool) (*acmez.Client, error) {
},
ChallengeSolvers: make(map[string]acmez.Solver),
}
if iss.Logger != nil {
client.Logger = iss.Logger.Named("acme_client")
}
client.Logger = iss.Logger.Named("acme_client")
// configure challenges (most of the time, DNS challenge is
// exclusive of other ones because it is usually only used
@ -260,24 +258,20 @@ func (c *acmeClient) throttle(ctx context.Context, names []string) error {
// TODO: stop rate limiter when it is garbage-collected...
}
rateLimitersMu.Unlock()
if c.iss.Logger != nil {
c.iss.Logger.Info("waiting on internal rate limiter",
zap.Strings("identifiers", names),
zap.String("ca", c.acmeClient.Directory),
zap.String("account", email),
)
}
c.iss.Logger.Info("waiting on internal rate limiter",
zap.Strings("identifiers", names),
zap.String("ca", c.acmeClient.Directory),
zap.String("account", email),
)
err := rl.Wait(ctx)
if err != nil {
return err
}
if c.iss.Logger != nil {
c.iss.Logger.Info("done waiting on internal rate limiter",
zap.Strings("identifiers", names),
zap.String("ca", c.acmeClient.Directory),
zap.String("account", email),
)
}
c.iss.Logger.Info("done waiting on internal rate limiter",
zap.Strings("identifiers", names),
zap.String("ca", c.acmeClient.Directory),
zap.String("account", email),
)
return nil
}

View File

@ -110,7 +110,9 @@ type ACMEIssuer struct {
// certificate chains
PreferredChains ChainPreference
// Set a logger to enable logging
// Set a logger to configure logging; a default
// logger must always be set; if no logging is
// desired, set this to zap.NewNop().
Logger *zap.Logger
config *Config
@ -197,6 +199,11 @@ func NewACMEIssuer(cfg *Config, template ACMEIssuer) *ACMEIssuer {
template.Logger = DefaultACME.Logger
}
// absolutely do not allow a nil logger; that would panic
if template.Logger == nil {
template.Logger = defaultLogger
}
template.config = cfg
template.mu = new(sync.Mutex)
@ -398,7 +405,7 @@ func (am *ACMEIssuer) doIssue(ctx context.Context, csr *x509.CertificateRequest,
// processing. If there are no matches, the first chain is returned.
func (am *ACMEIssuer) selectPreferredChain(certChains []acme.Certificate) acme.Certificate {
if len(certChains) == 1 {
if am.Logger != nil && (len(am.PreferredChains.AnyCommonName) > 0 || len(am.PreferredChains.RootCommonName) > 0) {
if len(am.PreferredChains.AnyCommonName) > 0 || len(am.PreferredChains.RootCommonName) > 0 {
am.Logger.Debug("there is only one chain offered; selecting it regardless of preferences",
zap.String("chain_url", certChains[0].URL))
}
@ -423,11 +430,9 @@ func (am *ACMEIssuer) selectPreferredChain(certChains []acme.Certificate) acme.C
for i, chain := range certChains {
certs, err := parseCertsFromPEMBundle(chain.ChainPEM)
if err != nil {
if am.Logger != nil {
am.Logger.Error("unable to parse PEM certificate chain",
zap.Int("chain", i),
zap.Error(err))
}
am.Logger.Error("unable to parse PEM certificate chain",
zap.Int("chain", i),
zap.Error(err))
continue
}
decodedChains[i] = certs
@ -438,11 +443,9 @@ func (am *ACMEIssuer) selectPreferredChain(certChains []acme.Certificate) acme.C
for i, chain := range decodedChains {
for _, cert := range chain {
if cert.Issuer.CommonName == prefAnyCN {
if am.Logger != nil {
am.Logger.Debug("found preferred certificate chain by issuer common name",
zap.String("preference", prefAnyCN),
zap.Int("chain", i))
}
am.Logger.Debug("found preferred certificate chain by issuer common name",
zap.String("preference", prefAnyCN),
zap.Int("chain", i))
return certChains[i]
}
}
@ -454,20 +457,16 @@ func (am *ACMEIssuer) selectPreferredChain(certChains []acme.Certificate) acme.C
for _, prefRootCN := range am.PreferredChains.RootCommonName {
for i, chain := range decodedChains {
if chain[len(chain)-1].Issuer.CommonName == prefRootCN {
if am.Logger != nil {
am.Logger.Debug("found preferred certificate chain by root common name",
zap.String("preference", prefRootCN),
zap.Int("chain", i))
}
am.Logger.Debug("found preferred certificate chain by root common name",
zap.String("preference", prefRootCN),
zap.Int("chain", i))
return certChains[i]
}
}
}
}
if am.Logger != nil {
am.Logger.Warn("did not find chain matching preferences; using first")
}
am.Logger.Warn("did not find chain matching preferences; using first")
}
return certChains[0]
@ -509,6 +508,7 @@ type ChainPreference struct {
var DefaultACME = ACMEIssuer{
CA: LetsEncryptProductionCA,
TestCA: LetsEncryptStagingCA,
Logger: defaultLogger,
}
// Some well-known CA endpoints available to use.

View File

@ -71,9 +71,7 @@ func (jm *jobManager) worker() {
jm.queue = jm.queue[1:]
jm.mu.Unlock()
if err := next.job(); err != nil {
if next.logger != nil {
next.logger.Error("job failed", zap.Error(err))
}
next.logger.Error("job failed", zap.Error(err))
}
if next.name != "" {
jm.mu.Lock()
@ -116,22 +114,19 @@ func doWithRetry(ctx context.Context, log *zap.Logger, f func(context.Context) e
intervalIndex++
}
if time.Since(start) < maxRetryDuration {
if log != nil {
log.Error("will retry",
zap.Error(err),
zap.Int("attempt", attempts),
zap.Duration("retrying_in", retryIntervals[intervalIndex]),
zap.Duration("elapsed", time.Since(start)),
zap.Duration("max_duration", maxRetryDuration))
}
log.Error("will retry",
zap.Error(err),
zap.Int("attempt", attempts),
zap.Duration("retrying_in", retryIntervals[intervalIndex]),
zap.Duration("elapsed", time.Since(start)),
zap.Duration("max_duration", maxRetryDuration))
} else {
if log != nil {
log.Error("final attempt; giving up",
zap.Error(err),
zap.Int("attempt", attempts),
zap.Duration("elapsed", time.Since(start)),
zap.Duration("max_duration", maxRetryDuration))
}
log.Error("final attempt; giving up",
zap.Error(err),
zap.Int("attempt", attempts),
zap.Duration("elapsed", time.Since(start)),
zap.Duration("max_duration", maxRetryDuration))
return nil
}
}

View File

@ -118,6 +118,11 @@ func NewCache(opts CacheOptions) *Cache {
logger: opts.Logger,
}
// absolutely do not allow a nil logger; panics galore
if c.logger == nil {
c.logger = defaultLogger
}
go c.maintainAssets(0)
return c
@ -194,14 +199,12 @@ func (certCache *Cache) cacheCertificate(cert Certificate) {
func (certCache *Cache) unsyncedCacheCertificate(cert Certificate) {
// no-op if this certificate already exists in the cache
if _, ok := certCache.cache[cert.hash]; ok {
if certCache.logger != nil {
certCache.logger.Debug("certificate already cached",
zap.Strings("subjects", cert.Names),
zap.Time("expiration", cert.Leaf.NotAfter),
zap.Bool("managed", cert.managed),
zap.String("issuer_key", cert.issuerKey),
zap.String("hash", cert.hash))
}
certCache.logger.Debug("certificate already cached",
zap.Strings("subjects", cert.Names),
zap.Time("expiration", expiresAt(cert.Leaf)),
zap.Bool("managed", cert.managed),
zap.String("issuer_key", cert.issuerKey),
zap.String("hash", cert.hash))
return
}
@ -217,13 +220,11 @@ func (certCache *Cache) unsyncedCacheCertificate(cert Certificate) {
i := 0
for _, randomCert := range certCache.cache {
if i == rnd {
if certCache.logger != nil {
certCache.logger.Debug("cache full; evicting random certificate",
zap.Strings("removing_subjects", randomCert.Names),
zap.String("removing_hash", randomCert.hash),
zap.Strings("inserting_subjects", cert.Names),
zap.String("inserting_hash", cert.hash))
}
certCache.logger.Debug("cache full; evicting random certificate",
zap.Strings("removing_subjects", randomCert.Names),
zap.String("removing_hash", randomCert.hash),
zap.Strings("inserting_subjects", cert.Names),
zap.String("inserting_hash", cert.hash))
certCache.removeCertificate(randomCert)
break
}
@ -239,16 +240,14 @@ func (certCache *Cache) unsyncedCacheCertificate(cert Certificate) {
certCache.cacheIndex[name] = append(certCache.cacheIndex[name], cert.hash)
}
if certCache.logger != nil {
certCache.logger.Debug("added certificate to cache",
zap.Strings("subjects", cert.Names),
zap.Time("expiration", cert.Leaf.NotAfter),
zap.Bool("managed", cert.managed),
zap.String("issuer_key", cert.issuerKey),
zap.String("hash", cert.hash),
zap.Int("cache_size", len(certCache.cache)),
zap.Int("cache_capacity", certCache.options.Capacity))
}
certCache.logger.Debug("added certificate to cache",
zap.Strings("subjects", cert.Names),
zap.Time("expiration", expiresAt(cert.Leaf)),
zap.Bool("managed", cert.managed),
zap.String("issuer_key", cert.issuerKey),
zap.String("hash", cert.hash),
zap.Int("cache_size", len(certCache.cache)),
zap.Int("cache_capacity", certCache.options.Capacity))
}
// removeCertificate removes cert from the cache.
@ -275,16 +274,14 @@ func (certCache *Cache) removeCertificate(cert Certificate) {
// delete the actual cert from the cache
delete(certCache.cache, cert.hash)
if certCache.logger != nil {
certCache.logger.Debug("removed certificate from cache",
zap.Strings("subjects", cert.Names),
zap.Time("expiration", cert.Leaf.NotAfter),
zap.Bool("managed", cert.managed),
zap.String("issuer_key", cert.issuerKey),
zap.String("hash", cert.hash),
zap.Int("cache_size", len(certCache.cache)),
zap.Int("cache_capacity", certCache.options.Capacity))
}
certCache.logger.Debug("removed certificate from cache",
zap.Strings("subjects", cert.Names),
zap.Time("expiration", expiresAt(cert.Leaf)),
zap.Bool("managed", cert.managed),
zap.String("issuer_key", cert.issuerKey),
zap.String("hash", cert.hash),
zap.Int("cache_size", len(certCache.cache)),
zap.Int("cache_capacity", certCache.options.Capacity))
}
// replaceCertificate atomically replaces oldCert with newCert in
@ -296,11 +293,9 @@ func (certCache *Cache) replaceCertificate(oldCert, newCert Certificate) {
certCache.removeCertificate(oldCert)
certCache.unsyncedCacheCertificate(newCert)
certCache.mu.Unlock()
if certCache.logger != nil {
certCache.logger.Info("replaced certificate in cache",
zap.Strings("subjects", newCert.Names),
zap.Time("new_expiration", newCert.Leaf.NotAfter))
}
certCache.logger.Info("replaced certificate in cache",
zap.Strings("subjects", newCert.Names),
zap.Time("new_expiration", expiresAt(newCert.Leaf)))
}
func (certCache *Cache) getAllMatchingCerts(name string) []Certificate {

View File

@ -67,7 +67,7 @@ func (cert Certificate) Empty() bool {
// NeedsRenewal returns true if the certificate is
// expiring soon (according to cfg) or has expired.
func (cert Certificate) NeedsRenewal(cfg *Config) bool {
return currentlyInRenewalWindow(cert.Leaf.NotBefore, cert.Leaf.NotAfter, cfg.RenewalWindowRatio)
return currentlyInRenewalWindow(cert.Leaf.NotBefore, expiresAt(cert.Leaf), cfg.RenewalWindowRatio)
}
// Expired returns true if the certificate has expired.
@ -79,7 +79,7 @@ func (cert Certificate) Expired() bool {
// tls.X509KeyPair() discards the leaf; oh well
return false
}
return time.Now().After(cert.Leaf.NotAfter)
return time.Now().After(expiresAt(cert.Leaf))
}
// currentlyInRenewalWindow returns true if the current time is
@ -109,6 +109,13 @@ func (cert Certificate) HasTag(tag string) bool {
return false
}
// expiresAt return the time that a certificate expires. Account for the 1s
// resolution of ASN.1 UTCTime/GeneralizedTime by including the extra fraction
// of a second of certificate validity beyond the NotAfter value.
func expiresAt(cert *x509.Certificate) time.Time {
return cert.NotAfter.Truncate(time.Second).Add(1 * time.Second)
}
// CacheManagedCertificate loads the certificate for domain into the
// cache, from the TLS storage for managed certificates. It returns a
// copy of the Certificate that was put into the cache.
@ -122,7 +129,7 @@ func (cfg *Config) CacheManagedCertificate(ctx context.Context, domain string) (
return cert, err
}
cfg.certCache.cacheCertificate(cert)
cfg.emit("cached_managed_cert", cert.Names)
cfg.emit(ctx, "cached_managed_cert", map[string]any{"sans": cert.Names})
return cert, nil
}
@ -155,7 +162,7 @@ func (cfg *Config) CacheUnmanagedCertificatePEMFile(ctx context.Context, certFil
}
cert.Tags = tags
cfg.certCache.cacheCertificate(cert)
cfg.emit("cached_unmanaged_cert", cert.Names)
cfg.emit(ctx, "cached_unmanaged_cert", map[string]any{"sans": cert.Names})
return nil
}
@ -170,10 +177,10 @@ func (cfg *Config) CacheUnmanagedTLSCertificate(ctx context.Context, tlsCert tls
return err
}
err = stapleOCSP(ctx, cfg.OCSP, cfg.Storage, &cert, nil)
if err != nil && cfg.Logger != nil {
if err != nil {
cfg.Logger.Warn("stapling OCSP", zap.Error(err))
}
cfg.emit("cached_unmanaged_cert", cert.Names)
cfg.emit(ctx, "cached_unmanaged_cert", map[string]any{"sans": cert.Names})
cert.Tags = tags
cfg.certCache.cacheCertificate(cert)
return nil
@ -190,7 +197,7 @@ func (cfg *Config) CacheUnmanagedCertificatePEMBytes(ctx context.Context, certBy
}
cert.Tags = tags
cfg.certCache.cacheCertificate(cert)
cfg.emit("cached_unmanaged_cert", cert.Names)
cfg.emit(ctx, "cached_unmanaged_cert", map[string]any{"sans": cert.Names})
return nil
}
@ -218,7 +225,7 @@ func (cfg Config) makeCertificateWithOCSP(ctx context.Context, certPEMBlock, key
return cert, err
}
err = stapleOCSP(ctx, cfg.OCSP, cfg.Storage, &cert, certPEMBlock)
if err != nil && cfg.Logger != nil {
if err != nil {
cfg.Logger.Warn("stapling OCSP", zap.Error(err), zap.Strings("identifiers", cert.Names))
}
return cert, nil
@ -326,9 +333,7 @@ func (cfg *Config) managedCertInStorageExpiresSoon(ctx context.Context, cert Cer
// to the new cert. It assumes that the new certificate for oldCert.Names[0] is
// already in storage. It returns the newly-loaded certificate if successful.
func (cfg *Config) reloadManagedCertificate(ctx context.Context, oldCert Certificate) (Certificate, error) {
if cfg.Logger != nil {
cfg.Logger.Info("reloading managed certificate", zap.Strings("identifiers", oldCert.Names))
}
cfg.Logger.Info("reloading managed certificate", zap.Strings("identifiers", oldCert.Names))
newCert, err := cfg.loadManagedCertificate(ctx, oldCert.Names[0])
if err != nil {
return Certificate{}, fmt.Errorf("loading managed certificate for %v from storage: %v", oldCert.Names, err)

View File

@ -43,10 +43,14 @@ import (
"log"
"net"
"net/http"
"os"
"sort"
"strings"
"sync"
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// HTTPS serves mux for all domainNames using the HTTP
@ -405,7 +409,7 @@ type IssuedCertificate struct {
// Any extra information to serialize alongside the
// certificate in storage.
Metadata interface{}
Metadata any
}
// CertificateResource associates a certificate with its private
@ -425,7 +429,7 @@ type CertificateResource struct {
// Any extra information associated with the certificate,
// usually provided by the issuer implementation.
IssuerData interface{} `json:"issuer_data,omitempty"`
IssuerData any `json:"issuer_data,omitempty"`
// The unique string identifying the issuer of the
// certificate; internally useful for storage access.
@ -468,8 +472,16 @@ var Default = Config{
RenewalWindowRatio: DefaultRenewalWindowRatio,
Storage: defaultFileStorage,
KeySource: DefaultKeyGenerator,
Logger: defaultLogger,
}
// defaultLogger is guaranteed to be a non-nil fallback logger.
var defaultLogger = zap.New(zapcore.NewCore(
zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig()),
os.Stderr,
zap.InfoLevel,
))
const (
// HTTPChallengePort is the officially-designated port for
// the HTTP challenge according to the ACME spec.

View File

@ -56,7 +56,16 @@ type Config struct {
// to subscribe to certain things happening
// internally by this config; invocations are
// synchronous, so make them return quickly!
OnEvent func(event string, data interface{})
// Functions should honor context cancellation.
//
// An error should only be returned to advise
// the emitter to abort or cancel an upcoming
// event. Some events, especially those that have
// already happened, cannot be aborted. For example,
// cert_obtaining can be canceled, but
// cert_obtained cannot. Emitters may choose to
// ignore returned errors.
OnEvent func(ctx context.Context, event string, data map[string]any) error
// DefaultServerName specifies a server name
// to use when choosing a certificate if the
@ -110,7 +119,8 @@ type Config struct {
// TLS assets. Default is the local file system.
Storage Storage
// Set a logger to enable logging.
// Set a logger to enable logging. If not set,
// a default logger will be created.
Logger *zap.Logger
// required pointer to the in-memory cert cache
@ -196,6 +206,16 @@ func newWithCache(certCache *Cache, cfg Config) *Config {
if cfg.OnDemand == nil {
cfg.OnDemand = Default.OnDemand
}
if !cfg.MustStaple {
cfg.MustStaple = Default.MustStaple
}
if len(cfg.Issuers) == 0 {
cfg.Issuers = Default.Issuers
if len(cfg.Issuers) == 0 {
// at least one issuer is absolutely required
cfg.Issuers = []Issuer{NewACMEIssuer(&cfg, DefaultACME)}
}
}
if cfg.RenewalWindowRatio == 0 {
cfg.RenewalWindowRatio = Default.RenewalWindowRatio
}
@ -208,18 +228,11 @@ func newWithCache(certCache *Cache, cfg Config) *Config {
if cfg.DefaultServerName == "" {
cfg.DefaultServerName = Default.DefaultServerName
}
if !cfg.MustStaple {
cfg.MustStaple = Default.MustStaple
}
if cfg.Storage == nil {
cfg.Storage = Default.Storage
}
if len(cfg.Issuers) == 0 {
cfg.Issuers = Default.Issuers
if len(cfg.Issuers) == 0 {
// at least one issuer is absolutely required
cfg.Issuers = []Issuer{NewACMEIssuer(&cfg, DefaultACME)}
}
if cfg.Logger == nil {
cfg.Logger = Default.Logger
}
// absolutely don't allow a nil storage,
@ -229,6 +242,12 @@ func newWithCache(certCache *Cache, cfg Config) *Config {
cfg.Storage = defaultFileStorage
}
// absolutely don't allow a nil logger either,
// because that would result in panics
if cfg.Logger == nil {
cfg.Logger = defaultLogger
}
cfg.certCache = certCache
return &cfg
@ -460,11 +479,9 @@ func (cfg *Config) obtainCert(ctx context.Context, name string, interactive bool
return fmt.Errorf("failed storage check: %v - storage is probably misconfigured", err)
}
log := loggerNamed(cfg.Logger, "obtain")
log := cfg.Logger.Named("obtain")
if log != nil {
log.Info("acquiring lock", zap.String("identifier", name))
}
log.Info("acquiring lock", zap.String("identifier", name))
// ensure idempotency of the obtain operation for this name
lockKey := cfg.lockKey(certIssueLockOp, name)
@ -473,33 +490,30 @@ func (cfg *Config) obtainCert(ctx context.Context, name string, interactive bool
return fmt.Errorf("unable to acquire lock '%s': %v", lockKey, err)
}
defer func() {
if log != nil {
log.Info("releasing lock", zap.String("identifier", name))
}
log.Info("releasing lock", zap.String("identifier", name))
if err := releaseLock(ctx, cfg.Storage, lockKey); err != nil {
if log != nil {
log.Error("unable to unlock",
zap.String("identifier", name),
zap.String("lock_key", lockKey),
zap.Error(err))
}
log.Error("unable to unlock",
zap.String("identifier", name),
zap.String("lock_key", lockKey),
zap.Error(err))
}
}()
if log != nil {
log.Info("lock acquired", zap.String("identifier", name))
}
log.Info("lock acquired", zap.String("identifier", name))
f := func(ctx context.Context) error {
// check if obtain is still needed -- might have been obtained during lock
if cfg.storageHasCertResourcesAnyIssuer(ctx, name) {
if log != nil {
log.Info("certificate already exists in storage", zap.String("identifier", name))
}
log.Info("certificate already exists in storage", zap.String("identifier", name))
return nil
}
// if storage has a private key already, use it; otherwise,
// we'll generate our own
log.Info("obtaining certificate", zap.String("identifier", name))
if err := cfg.emit(ctx, "cert_obtaining", map[string]any{"identifier": name}); err != nil {
return fmt.Errorf("obtaining certificate aborted by event handler: %w", err)
}
// if storage has a private key already, use it; otherwise we'll generate our own
privKey, privKeyPEM, issuers, err := cfg.reusePrivateKey(ctx, name)
if err != nil {
return err
@ -523,11 +537,12 @@ func (cfg *Config) obtainCert(ctx context.Context, name string, interactive bool
// try to obtain from each issuer until we succeed
var issuedCert *IssuedCertificate
var issuerUsed Issuer
var issuerKeys []string
for i, issuer := range issuers {
if log != nil {
log.Debug(fmt.Sprintf("trying issuer %d/%d", i+1, len(cfg.Issuers)),
zap.String("issuer", issuer.IssuerKey()))
}
issuerKeys = append(issuerKeys, issuer.IssuerKey())
log.Debug(fmt.Sprintf("trying issuer %d/%d", i+1, len(cfg.Issuers)),
zap.String("issuer", issuer.IssuerKey()))
if prechecker, ok := issuer.(PreChecker); ok {
err = prechecker.PreCheck(ctx, []string{name}, interactive)
@ -549,14 +564,19 @@ func (cfg *Config) obtainCert(ctx context.Context, name string, interactive bool
if errors.As(err, &problem) {
errToLog = problem
}
if log != nil {
log.Error("could not get certificate from issuer",
zap.String("identifier", name),
zap.String("issuer", issuer.IssuerKey()),
zap.Error(errToLog))
}
log.Error("could not get certificate from issuer",
zap.String("identifier", name),
zap.String("issuer", issuer.IssuerKey()),
zap.Error(errToLog))
}
if err != nil {
cfg.emit(ctx, "cert_failed", map[string]any{
"renewal": false,
"identifier": name,
"issuers": issuerKeys,
"error": err,
})
// only the error from the last issuer will be returned, but we logged the others
return fmt.Errorf("[%s] Obtain: %w", name, err)
}
@ -573,15 +593,14 @@ func (cfg *Config) obtainCert(ctx context.Context, name string, interactive bool
return fmt.Errorf("[%s] Obtain: saving assets: %v", name, err)
}
cfg.emit("cert_obtained", CertificateEventData{
Name: name,
IssuerKey: issuerUsed.IssuerKey(),
StorageKey: certRes.NamesKey(),
})
log.Info("certificate obtained successfully", zap.String("identifier", name))
if log != nil {
log.Info("certificate obtained successfully", zap.String("identifier", name))
}
cfg.emit(ctx, "cert_obtained", map[string]any{
"renewal": false,
"identifier": name,
"issuers": issuerUsed.IssuerKey(),
"storage_key": certRes.NamesKey(),
})
return nil
}
@ -675,11 +694,9 @@ func (cfg *Config) renewCert(ctx context.Context, name string, force, interactiv
return fmt.Errorf("failed storage check: %v - storage is probably misconfigured", err)
}
log := loggerNamed(cfg.Logger, "renew")
log := cfg.Logger.Named("renew")
if log != nil {
log.Info("acquiring lock", zap.String("identifier", name))
}
log.Info("acquiring lock", zap.String("identifier", name))
// ensure idempotency of the renew operation for this name
lockKey := cfg.lockKey(certIssueLockOp, name)
@ -688,21 +705,16 @@ func (cfg *Config) renewCert(ctx context.Context, name string, force, interactiv
return fmt.Errorf("unable to acquire lock '%s': %v", lockKey, err)
}
defer func() {
if log != nil {
log.Info("releasing lock", zap.String("identifier", name))
}
log.Info("releasing lock", zap.String("identifier", name))
if err := releaseLock(ctx, cfg.Storage, lockKey); err != nil {
if log != nil {
log.Error("unable to unlock",
zap.String("identifier", name),
zap.String("lock_key", lockKey),
zap.Error(err))
}
log.Error("unable to unlock",
zap.String("identifier", name),
zap.String("lock_key", lockKey),
zap.Error(err))
}
}()
if log != nil {
log.Info("lock acquired", zap.String("identifier", name))
}
log.Info("lock acquired", zap.String("identifier", name))
f := func(ctx context.Context) error {
// prepare for renewal (load PEM cert, key, and meta)
@ -715,25 +727,29 @@ func (cfg *Config) renewCert(ctx context.Context, name string, force, interactiv
timeLeft, needsRenew := cfg.managedCertNeedsRenewal(certRes)
if !needsRenew {
if force {
if log != nil {
log.Info("certificate does not need to be renewed, but renewal is being forced",
zap.String("identifier", name),
zap.Duration("remaining", timeLeft))
}
log.Info("certificate does not need to be renewed, but renewal is being forced",
zap.String("identifier", name),
zap.Duration("remaining", timeLeft))
} else {
if log != nil {
log.Info("certificate appears to have been renewed already",
zap.String("identifier", name),
zap.Duration("remaining", timeLeft))
}
log.Info("certificate appears to have been renewed already",
zap.String("identifier", name),
zap.Duration("remaining", timeLeft))
return nil
}
}
if log != nil {
log.Info("renewing certificate",
zap.String("identifier", name),
zap.Duration("remaining", timeLeft))
log.Info("renewing certificate",
zap.String("identifier", name),
zap.Duration("remaining", timeLeft))
if err := cfg.emit(ctx, "cert_obtaining", map[string]any{
"renewal": true,
"identifier": name,
"forced": force,
"remaining": timeLeft,
"issuer": certRes.issuerKey, // previous/current issuer
}); err != nil {
return fmt.Errorf("renewing certificate aborted by event handler: %w", err)
}
privateKey, err := PEMDecodePrivateKey(certRes.PrivateKeyPEM)
@ -748,7 +764,9 @@ func (cfg *Config) renewCert(ctx context.Context, name string, force, interactiv
// try to obtain from each issuer until we succeed
var issuedCert *IssuedCertificate
var issuerUsed Issuer
var issuerKeys []string
for _, issuer := range cfg.Issuers {
issuerKeys = append(issuerKeys, issuer.IssuerKey())
if prechecker, ok := issuer.(PreChecker); ok {
err = prechecker.PreCheck(ctx, []string{name}, interactive)
if err != nil {
@ -769,14 +787,21 @@ func (cfg *Config) renewCert(ctx context.Context, name string, force, interactiv
if errors.As(err, &problem) {
errToLog = problem
}
if log != nil {
log.Error("could not get certificate from issuer",
zap.String("identifier", name),
zap.String("issuer", issuer.IssuerKey()),
zap.Error(errToLog))
}
log.Error("could not get certificate from issuer",
zap.String("identifier", name),
zap.String("issuer", issuer.IssuerKey()),
zap.Error(errToLog))
}
if err != nil {
cfg.emit(ctx, "cert_failed", map[string]any{
"renewal": true,
"identifier": name,
"remaining": timeLeft,
"issuers": issuerKeys,
"storage_key": certRes.NamesKey(),
"error": err,
})
// only the error from the last issuer will be returned, but we logged the others
return fmt.Errorf("[%s] Renew: %w", name, err)
}
@ -793,15 +818,15 @@ func (cfg *Config) renewCert(ctx context.Context, name string, force, interactiv
return fmt.Errorf("[%s] Renew: saving assets: %v", name, err)
}
cfg.emit("cert_renewed", CertificateEventData{
Name: name,
IssuerKey: issuerUsed.IssuerKey(),
StorageKey: certRes.NamesKey(),
})
log.Info("certificate renewed successfully", zap.String("identifier", name))
if log != nil {
log.Info("certificate renewed successfully", zap.String("identifier", name))
}
cfg.emit(ctx, "cert_obtained", map[string]any{
"renewal": true,
"remaining": timeLeft,
"identifier": name,
"issuer": issuerUsed.IssuerKey(),
"storage_key": certRes.NamesKey(),
})
return nil
}
@ -876,12 +901,6 @@ func (cfg *Config) RevokeCert(ctx context.Context, domain string, reason int, in
return fmt.Errorf("issuer %d (%s): %v", i, issuerKey, err)
}
cfg.emit("cert_revoked", CertificateEventData{
Name: domain,
IssuerKey: issuerKey,
StorageKey: certRes.NamesKey(),
})
err = cfg.deleteSiteAssets(ctx, issuerKey, domain)
if err != nil {
return fmt.Errorf("certificate revoked, but unable to fully clean up assets from issuer %s: %v", issuerKey, err)
@ -994,10 +1013,8 @@ func (cfg *Config) checkStorage(ctx context.Context) error {
defer func() {
deleteErr := cfg.Storage.Delete(ctx, key)
if deleteErr != nil {
if cfg.Logger != nil {
cfg.Logger.Error("deleting test key from storage",
zap.String("key", key), zap.Error(err))
}
cfg.Logger.Error("deleting test key from storage",
zap.String("key", key), zap.Error(err))
}
// if there was no other error, make sure
// to return any error returned from Delete
@ -1065,23 +1082,16 @@ func (cfg *Config) managedCertNeedsRenewal(certRes CertificateResource) (time.Du
if err != nil {
return 0, true
}
remaining := time.Until(certChain[0].NotAfter)
needsRenew := currentlyInRenewalWindow(certChain[0].NotBefore, certChain[0].NotAfter, cfg.RenewalWindowRatio)
remaining := time.Until(expiresAt(certChain[0]))
needsRenew := currentlyInRenewalWindow(certChain[0].NotBefore, expiresAt(certChain[0]), cfg.RenewalWindowRatio)
return remaining, needsRenew
}
func (cfg *Config) emit(eventName string, data interface{}) {
func (cfg *Config) emit(ctx context.Context, eventName string, data map[string]any) error {
if cfg.OnEvent == nil {
return
}
cfg.OnEvent(eventName, data)
}
func loggerNamed(l *zap.Logger, name string) *zap.Logger {
if l == nil {
return nil
}
return l.Named(name)
return cfg.OnEvent(ctx, eventName, data)
}
// CertificateSelector is a type which can select a certificate to use given multiple choices.
@ -1105,20 +1115,6 @@ type OCSPConfig struct {
ResponderOverrides map[string]string
}
// CertificateEventData contains contextual information for
// an obtained, renewed or revoked certificate.
// EXPERIMENTAL: subject to change.
type CertificateEventData struct {
// Domain or subject name of the certificate.
Name string
// Storage key for the issuer used for this certificate.
IssuerKey string
// Location in storage at which the certificate could be found.
StorageKey string
}
// certIssueLockOp is the name of the operation used
// when naming a lock to make it mutually exclusive
// with other certificate issuance operations for a

View File

@ -226,14 +226,12 @@ func (cfg *Config) loadCertResourceAnyIssuer(ctx context.Context, certNamesKey s
return certResources[j].decoded.NotBefore.Before(certResources[i].decoded.NotBefore)
})
if cfg.Logger != nil {
cfg.Logger.Debug("loading managed certificate",
zap.String("domain", certNamesKey),
zap.Time("expiration", certResources[0].decoded.NotAfter),
zap.String("issuer_key", certResources[0].issuer.IssuerKey()),
zap.Any("storage", cfg.Storage),
)
}
cfg.Logger.Debug("loading managed certificate",
zap.String("domain", certNamesKey),
zap.Time("expiration", expiresAt(certResources[0].decoded)),
zap.String("issuer_key", certResources[0].issuer.IssuerKey()),
zap.Any("storage", cfg.Storage),
)
return certResources[0].CertificateResource, nil
}

View File

@ -243,7 +243,7 @@ func checkAuthoritativeNss(fqdn, value string, nameservers []string) (bool, erro
}
if r.Rcode != dns.RcodeSuccess {
if r.Rcode == dns.RcodeNameError {
if r.Rcode == dns.RcodeNameError || r.Rcode == dns.RcodeServerFailure {
// if Present() succeeded, then it must show up eventually, or else
// something is really broken in the DNS provider or their API;
// no need for error here, simply have the caller try again

View File

@ -149,10 +149,10 @@ func (s *FileStorage) Filename(key string) string {
return filepath.Join(s.Path, filepath.FromSlash(key))
}
// Lock obtains a lock named by the given key. It blocks
// Lock obtains a lock named by the given name. It blocks
// until the lock can be obtained or an error is returned.
func (s *FileStorage) Lock(ctx context.Context, key string) error {
filename := s.lockFilename(key)
func (s *FileStorage) Lock(ctx context.Context, name string) error {
filename := s.lockFilename(name)
for {
err := createLockfile(filename)
@ -172,7 +172,13 @@ func (s *FileStorage) Lock(ctx context.Context, key string) error {
if err == nil {
err2 := json.NewDecoder(f).Decode(&meta)
f.Close()
if err2 != nil {
if errors.Is(err2, io.EOF) {
// lockfile is empty or truncated; I *think* we can assume the previous
// acquirer either crashed or had some sort of failure that caused them
// to be unable to fully acquire or retain the lock, therefore we should
// treat it as if the lockfile did not exist
log.Printf("[INFO][%s] %s: Empty lockfile (%v) - likely previous process crashed or storage medium failure; treating as stale", s, filename, err2)
} else if err2 != nil {
return fmt.Errorf("decoding lockfile contents: %w", err2)
}
}
@ -193,10 +199,10 @@ func (s *FileStorage) Lock(ctx context.Context, key string) error {
// or must give up on perfect mutual exclusivity; however, these cases are rare,
// so we prefer the simpler solution that avoids infinite loops)
log.Printf("[INFO][%s] Lock for '%s' is stale (created: %s, last update: %s); removing then retrying: %s",
s, key, meta.Created, meta.Updated, filename)
s, name, meta.Created, meta.Updated, filename)
if err = os.Remove(filename); err != nil { // hopefully we can replace the lock file quickly!
if !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("unable to delete stale lock; deadlocked: %w", err)
return fmt.Errorf("unable to delete stale lockfile; deadlocked: %w", err)
}
}
continue
@ -215,16 +221,16 @@ func (s *FileStorage) Lock(ctx context.Context, key string) error {
}
// Unlock releases the lock for name.
func (s *FileStorage) Unlock(_ context.Context, key string) error {
return os.Remove(s.lockFilename(key))
func (s *FileStorage) Unlock(_ context.Context, name string) error {
return os.Remove(s.lockFilename(name))
}
func (s *FileStorage) String() string {
return "FileStorage:" + s.Path
}
func (s *FileStorage) lockFilename(key string) string {
return filepath.Join(s.lockDir(), StorageKeys.Safe(key)+".lock")
func (s *FileStorage) lockFilename(name string) string {
return filepath.Join(s.lockDir(), StorageKeys.Safe(name)+".lock")
}
func (s *FileStorage) lockDir() string {

View File

@ -43,7 +43,15 @@ import (
//
// This method is safe for use as a tls.Config.GetCertificate callback.
func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
cfg.emit("tls_handshake_started", clientHello)
ctx := context.TODO() // TODO: get a proper context? from somewhere...
if err := cfg.emit(ctx, "tls_get_certificate", map[string]any{"client_hello": clientHello}); err != nil {
cfg.Logger.Error("TLS handshake aborted by event handler",
zap.String("server_name", clientHello.ServerName),
zap.String("remote", clientHello.Conn.RemoteAddr().String()),
zap.Error(err))
return nil, fmt.Errorf("handshake aborted by event handler: %w", err)
}
// special case: serve up the certificate for a TLS-ALPN ACME challenge
// (https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05)
@ -51,29 +59,23 @@ func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certif
if proto == acmez.ACMETLS1Protocol {
challengeCert, distributed, err := cfg.getTLSALPNChallengeCert(clientHello)
if err != nil {
if cfg.Logger != nil {
cfg.Logger.Error("tls-alpn challenge",
zap.String("server_name", clientHello.ServerName),
zap.Error(err))
}
cfg.Logger.Error("tls-alpn challenge",
zap.String("server_name", clientHello.ServerName),
zap.Error(err))
return nil, err
}
if cfg.Logger != nil {
cfg.Logger.Info("served key authentication certificate",
zap.String("server_name", clientHello.ServerName),
zap.String("challenge", "tls-alpn-01"),
zap.String("remote", clientHello.Conn.RemoteAddr().String()),
zap.Bool("distributed", distributed))
}
cfg.Logger.Info("served key authentication certificate",
zap.String("server_name", clientHello.ServerName),
zap.String("challenge", "tls-alpn-01"),
zap.String("remote", clientHello.Conn.RemoteAddr().String()),
zap.Bool("distributed", distributed))
return challengeCert, nil
}
}
// get the certificate and serve it up
cert, err := cfg.getCertDuringHandshake(clientHello, true, true)
if err == nil {
cfg.emit("tls_handshake_completed", clientHello)
}
cert, err := cfg.getCertDuringHandshake(ctx, clientHello, true, true)
return &cert.Certificate, err
}
@ -152,48 +154,44 @@ func (cfg *Config) getCertificateFromCache(hello *tls.ClientHelloInfo) (cert Cer
// then all certificates in the cache will be passed in
// for the cfg.CertSelection to make the final decision.
func (cfg *Config) selectCert(hello *tls.ClientHelloInfo, name string) (Certificate, bool) {
logger := loggerNamed(cfg.Logger, "handshake")
logger := cfg.Logger.Named("handshake")
choices := cfg.certCache.getAllMatchingCerts(name)
if len(choices) == 0 {
if cfg.CertSelection == nil {
if logger != nil {
logger.Debug("no matching certificates and no custom selection logic", zap.String("identifier", name))
}
logger.Debug("no matching certificates and no custom selection logic", zap.String("identifier", name))
return Certificate{}, false
}
if logger != nil {
logger.Debug("no matching certificate; will choose from all certificates", zap.String("identifier", name))
}
logger.Debug("no matching certificate; will choose from all certificates", zap.String("identifier", name))
choices = cfg.certCache.getAllCerts()
}
if logger != nil {
logger.Debug("choosing certificate",
zap.String("identifier", name),
zap.Int("num_choices", len(choices)))
}
logger.Debug("choosing certificate",
zap.String("identifier", name),
zap.Int("num_choices", len(choices)))
if cfg.CertSelection == nil {
cert, err := DefaultCertificateSelector(hello, choices)
if logger != nil {
logger.Debug("default certificate selection results",
zap.Error(err),
zap.String("identifier", name),
zap.Strings("subjects", cert.Names),
zap.Bool("managed", cert.managed),
zap.String("issuer_key", cert.issuerKey),
zap.String("hash", cert.hash))
}
return cert, err == nil
}
cert, err := cfg.CertSelection.SelectCertificate(hello, choices)
if logger != nil {
logger.Debug("custom certificate selection results",
logger.Debug("default certificate selection results",
zap.Error(err),
zap.String("identifier", name),
zap.Strings("subjects", cert.Names),
zap.Bool("managed", cert.managed),
zap.String("issuer_key", cert.issuerKey),
zap.String("hash", cert.hash))
return cert, err == nil
}
cert, err := cfg.CertSelection.SelectCertificate(hello, choices)
logger.Debug("custom certificate selection results",
zap.Error(err),
zap.String("identifier", name),
zap.Strings("subjects", cert.Names),
zap.Bool("managed", cert.managed),
zap.String("issuer_key", cert.issuerKey),
zap.String("hash", cert.hash))
return cert, err == nil
}
@ -214,7 +212,7 @@ func DefaultCertificateSelector(hello *tls.ClientHelloInfo, choices []Certificat
continue
}
best = choice // at least the client supports it...
if now.After(choice.Leaf.NotBefore) && now.Before(choice.Leaf.NotAfter) {
if now.After(choice.Leaf.NotBefore) && now.Before(expiresAt(choice.Leaf)) {
return choice, nil // ...and unexpired, great! "Certificate, I choose you!"
}
}
@ -234,26 +232,22 @@ func DefaultCertificateSelector(hello *tls.ClientHelloInfo, choices []Certificat
// An error will be returned if and only if no certificate is available.
//
// This function is safe for concurrent use.
func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNecessary, obtainIfNecessary bool) (Certificate, error) {
log := loggerNamed(cfg.Logger, "handshake")
ctx := context.TODO() // TODO: get a proper context? from somewhere...
func (cfg *Config) getCertDuringHandshake(ctx context.Context, hello *tls.ClientHelloInfo, loadIfNecessary, obtainIfNecessary bool) (Certificate, error) {
log := logWithRemote(cfg.Logger.Named("handshake"), hello)
// First check our in-memory cache to see if we've already loaded it
cert, matched, defaulted := cfg.getCertificateFromCache(hello)
if matched {
if log != nil {
log.Debug("matched certificate in cache",
zap.Strings("subjects", cert.Names),
zap.Bool("managed", cert.managed),
zap.Time("expiration", cert.Leaf.NotAfter),
zap.String("hash", cert.hash))
}
log.Debug("matched certificate in cache",
zap.Strings("subjects", cert.Names),
zap.Bool("managed", cert.managed),
zap.Time("expiration", expiresAt(cert.Leaf)),
zap.String("hash", cert.hash))
if cert.managed && cfg.OnDemand != nil && obtainIfNecessary {
// On-demand certificates are maintained in the background, but
// maintenance is triggered by handshakes instead of by a timer
// as in maintain.go.
return cfg.optionalMaintenance(ctx, loggerNamed(cfg.Logger, "on_demand"), cert, hello)
return cfg.optionalMaintenance(ctx, cfg.Logger.Named("on_demand"), cert, hello)
}
return cert, nil
}
@ -302,20 +296,16 @@ func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNece
loadedCert, err = cfg.CacheManagedCertificate(ctx, strings.Join(labels, "."))
}
if err == nil {
if log != nil {
log.Debug("loaded certificate from storage",
zap.Strings("subjects", loadedCert.Names),
zap.Bool("managed", loadedCert.managed),
zap.Time("expiration", loadedCert.Leaf.NotAfter),
zap.String("hash", loadedCert.hash))
}
log.Debug("loaded certificate from storage",
zap.Strings("subjects", loadedCert.Names),
zap.Bool("managed", loadedCert.managed),
zap.Time("expiration", expiresAt(loadedCert.Leaf)),
zap.String("hash", loadedCert.hash))
loadedCert, err = cfg.handshakeMaintenance(ctx, hello, loadedCert)
if err != nil {
if log != nil {
log.Error("maintaining newly-loaded certificate",
zap.String("server_name", name),
zap.Error(err))
}
log.Error("maintaining newly-loaded certificate",
zap.String("server_name", name),
zap.Error(err))
}
return loadedCert, nil
}
@ -327,27 +317,23 @@ func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNece
// Fall back to the default certificate if there is one
if defaulted {
if log != nil {
log.Debug("fell back to default certificate",
zap.Strings("subjects", cert.Names),
zap.Bool("managed", cert.managed),
zap.Time("expiration", cert.Leaf.NotAfter),
zap.String("hash", cert.hash))
}
log.Debug("fell back to default certificate",
zap.Strings("subjects", cert.Names),
zap.Bool("managed", cert.managed),
zap.Time("expiration", expiresAt(cert.Leaf)),
zap.String("hash", cert.hash))
return cert, nil
}
if log != nil {
log.Debug("no certificate matching TLS ClientHello",
zap.String("server_name", hello.ServerName),
zap.String("remote", hello.Conn.RemoteAddr().String()),
zap.String("identifier", name),
zap.Uint16s("cipher_suites", hello.CipherSuites),
zap.Float64("cert_cache_fill", float64(cacheSize)/cacheCapacity), // may be approximate! because we are not within the lock
zap.Bool("load_if_necessary", loadIfNecessary),
zap.Bool("obtain_if_necessary", obtainIfNecessary),
zap.Bool("on_demand", cfg.OnDemand != nil))
}
log.Debug("no certificate matching TLS ClientHello",
zap.String("server_name", hello.ServerName),
zap.String("remote", hello.Conn.RemoteAddr().String()),
zap.String("identifier", name),
zap.Uint16s("cipher_suites", hello.CipherSuites),
zap.Float64("cert_cache_fill", float64(cacheSize)/cacheCapacity), // may be approximate! because we are not within the lock
zap.Bool("load_if_necessary", loadIfNecessary),
zap.Bool("obtain_if_necessary", obtainIfNecessary),
zap.Bool("on_demand", cfg.OnDemand != nil))
return Certificate{}, fmt.Errorf("no certificate available for '%s'", name)
}
@ -361,12 +347,10 @@ func (cfg *Config) optionalMaintenance(ctx context.Context, log *zap.Logger, cer
return newCert, nil
}
if log != nil {
log.Error("renewing certificate on-demand failed",
zap.Strings("subjects", cert.Names),
zap.Time("not_after", cert.Leaf.NotAfter),
zap.Error(err))
}
log.Error("renewing certificate on-demand failed",
zap.Strings("subjects", cert.Names),
zap.Time("not_after", expiresAt(cert.Leaf)),
zap.Error(err))
if cert.Expired() {
return cert, err
@ -402,13 +386,13 @@ func (cfg *Config) checkIfCertShouldBeObtained(name string) error {
//
// This function is safe for use by multiple concurrent goroutines.
func (cfg *Config) obtainOnDemandCertificate(ctx context.Context, hello *tls.ClientHelloInfo) (Certificate, error) {
log := loggerNamed(cfg.Logger, "on_demand")
log := logWithRemote(cfg.Logger.Named("on_demand"), hello)
name := cfg.getNameFromClientHello(hello)
getCertWithoutReobtaining := func() (Certificate, error) {
// very important to set the obtainIfNecessary argument to false, so we don't repeat this infinitely
return cfg.getCertDuringHandshake(hello, true, false)
return cfg.getCertDuringHandshake(ctx, hello, true, false)
}
// We must protect this process from happening concurrently, so synchronize.
@ -451,9 +435,7 @@ func (cfg *Config) obtainOnDemandCertificate(ctx context.Context, hello *tls.Cli
return Certificate{}, err
}
if log != nil {
log.Info("obtaining new certificate", zap.String("server_name", name))
}
log.Info("obtaining new certificate", zap.String("server_name", name))
// TODO: we are only adding a timeout because we don't know if the context passed in is actually cancelable...
// (timeout duration is based on https://caddy.community/t/zerossl-dns-challenge-failing-often-route53-plugin/13822/24?u=matt)
@ -485,35 +467,29 @@ func (cfg *Config) obtainOnDemandCertificate(ctx context.Context, hello *tls.Cli
//
// This function is safe for use by multiple concurrent goroutines.
func (cfg *Config) handshakeMaintenance(ctx context.Context, hello *tls.ClientHelloInfo, cert Certificate) (Certificate, error) {
log := loggerNamed(cfg.Logger, "on_demand")
log := cfg.Logger.Named("on_demand")
// Check OCSP staple validity
if cert.ocsp != nil && !freshOCSP(cert.ocsp) {
if log != nil {
log.Debug("OCSP response needs refreshing",
zap.Strings("identifiers", cert.Names),
zap.Int("ocsp_status", cert.ocsp.Status),
zap.Time("this_update", cert.ocsp.ThisUpdate),
zap.Time("next_update", cert.ocsp.NextUpdate))
}
log.Debug("OCSP response needs refreshing",
zap.Strings("identifiers", cert.Names),
zap.Int("ocsp_status", cert.ocsp.Status),
zap.Time("this_update", cert.ocsp.ThisUpdate),
zap.Time("next_update", cert.ocsp.NextUpdate))
err := stapleOCSP(ctx, cfg.OCSP, cfg.Storage, &cert, nil)
if err != nil {
// An error with OCSP stapling is not the end of the world, and in fact, is
// quite common considering not all certs have issuer URLs that support it.
if log != nil {
log.Warn("stapling OCSP",
zap.String("server_name", hello.ServerName),
zap.Error(err))
}
} else if log != nil {
if log != nil {
log.Debug("successfully stapled new OCSP response",
zap.Strings("identifiers", cert.Names),
zap.Int("ocsp_status", cert.ocsp.Status),
zap.Time("this_update", cert.ocsp.ThisUpdate),
zap.Time("next_update", cert.ocsp.NextUpdate))
}
log.Warn("stapling OCSP",
zap.String("server_name", hello.ServerName),
zap.Error(err))
} else {
log.Debug("successfully stapled new OCSP response",
zap.Strings("identifiers", cert.Names),
zap.Int("ocsp_status", cert.ocsp.Status),
zap.Time("this_update", cert.ocsp.ThisUpdate),
zap.Time("next_update", cert.ocsp.NextUpdate))
}
// our copy of cert has the new OCSP staple, so replace it in the cache
@ -525,19 +501,17 @@ func (cfg *Config) handshakeMaintenance(ctx context.Context, hello *tls.ClientHe
// We attempt to replace any certificates that were revoked.
// Crucially, this happens OUTSIDE a lock on the certCache.
if certShouldBeForceRenewed(cert) {
if log != nil {
log.Warn("on-demand certificate's OCSP status is REVOKED; will try to forcefully renew",
zap.Strings("identifiers", cert.Names),
zap.Int("ocsp_status", cert.ocsp.Status),
zap.Time("revoked_at", cert.ocsp.RevokedAt),
zap.Time("this_update", cert.ocsp.ThisUpdate),
zap.Time("next_update", cert.ocsp.NextUpdate))
}
log.Warn("on-demand certificate's OCSP status is REVOKED; will try to forcefully renew",
zap.Strings("identifiers", cert.Names),
zap.Int("ocsp_status", cert.ocsp.Status),
zap.Time("revoked_at", cert.ocsp.RevokedAt),
zap.Time("this_update", cert.ocsp.ThisUpdate),
zap.Time("next_update", cert.ocsp.NextUpdate))
return cfg.renewDynamicCertificate(ctx, hello, cert)
}
// Check cert expiration
if currentlyInRenewalWindow(cert.Leaf.NotBefore, cert.Leaf.NotAfter, cfg.RenewalWindowRatio) {
if currentlyInRenewalWindow(cert.Leaf.NotBefore, expiresAt(cert.Leaf), cfg.RenewalWindowRatio) {
return cfg.renewDynamicCertificate(ctx, hello, cert)
}
@ -556,15 +530,15 @@ func (cfg *Config) handshakeMaintenance(ctx context.Context, hello *tls.ClientHe
//
// This function is safe for use by multiple concurrent goroutines.
func (cfg *Config) renewDynamicCertificate(ctx context.Context, hello *tls.ClientHelloInfo, currentCert Certificate) (Certificate, error) {
log := loggerNamed(cfg.Logger, "on_demand")
log := cfg.Logger.Named("on_demand")
name := cfg.getNameFromClientHello(hello)
timeLeft := time.Until(currentCert.Leaf.NotAfter)
timeLeft := time.Until(expiresAt(currentCert.Leaf))
revoked := currentCert.ocsp != nil && currentCert.ocsp.Status == ocsp.Revoked
getCertWithoutReobtaining := func() (Certificate, error) {
// very important to set the obtainIfNecessary argument to false, so we don't repeat this infinitely
return cfg.getCertDuringHandshake(hello, true, false)
return cfg.getCertDuringHandshake(ctx, hello, true, false)
}
// see if another goroutine is already working on this certificate
@ -578,23 +552,19 @@ func (cfg *Config) renewDynamicCertificate(ctx context.Context, hello *tls.Clien
// renewing it, so we might as well serve what we have without blocking, UNLESS
// we're forcing renewal, in which case the current certificate is not usable
if timeLeft > 0 && !revoked {
if log != nil {
log.Debug("certificate expires soon but is already being renewed; serving current certificate",
zap.Strings("subjects", currentCert.Names),
zap.Duration("remaining", timeLeft))
}
log.Debug("certificate expires soon but is already being renewed; serving current certificate",
zap.Strings("subjects", currentCert.Names),
zap.Duration("remaining", timeLeft))
return currentCert, nil
}
// otherwise, we'll have to wait for the renewal to finish so we don't serve
// a revoked or expired certificate
if log != nil {
log.Debug("certificate has expired, but is already being renewed; waiting for renewal to complete",
zap.Strings("subjects", currentCert.Names),
zap.Time("expired", currentCert.Leaf.NotAfter),
zap.Bool("revoked", revoked))
}
log.Debug("certificate has expired, but is already being renewed; waiting for renewal to complete",
zap.Strings("subjects", currentCert.Names),
zap.Time("expired", expiresAt(currentCert.Leaf)),
zap.Bool("revoked", revoked))
// TODO: see if we can get a proper context in here, for true cancellation
timeout := time.NewTimer(2 * time.Minute)
@ -620,30 +590,36 @@ func (cfg *Config) renewDynamicCertificate(ctx context.Context, hello *tls.Clien
obtainCertWaitChansMu.Unlock()
}
if log != nil {
log.Info("attempting certificate renewal",
zap.String("server_name", name),
zap.Strings("subjects", currentCert.Names),
zap.Time("expiration", currentCert.Leaf.NotAfter),
zap.Duration("remaining", timeLeft),
zap.Bool("revoked", revoked))
}
// Make sure a certificate for this name should be obtained on-demand
err := cfg.checkIfCertShouldBeObtained(name)
if err != nil {
// if not, remove from cache (it will be deleted from storage later)
cfg.certCache.mu.Lock()
cfg.certCache.removeCertificate(currentCert)
cfg.certCache.mu.Unlock()
unblockWaiters()
return Certificate{}, err
}
log = log.With(
zap.String("server_name", name),
zap.Strings("subjects", currentCert.Names),
zap.Time("expiration", expiresAt(currentCert.Leaf)),
zap.Duration("remaining", timeLeft),
zap.Bool("revoked", revoked),
)
// Renew and reload the certificate
renewAndReload := func(ctx context.Context, cancel context.CancelFunc) (Certificate, error) {
defer cancel()
log.Info("attempting certificate renewal")
// Make sure a certificate for this name should be obtained on-demand
err := cfg.checkIfCertShouldBeObtained(name)
if err != nil {
// if not, remove from cache (it will be deleted from storage later)
cfg.certCache.mu.Lock()
cfg.certCache.removeCertificate(currentCert)
cfg.certCache.mu.Unlock()
unblockWaiters()
if log != nil {
log.Error("certificate should not be obtained", zap.Error(err))
}
return Certificate{}, err
}
// otherwise, renew with issuer, etc.
var newCert Certificate
if revoked {
@ -656,9 +632,7 @@ func (cfg *Config) renewDynamicCertificate(ctx context.Context, hello *tls.Clien
// make the replacement as atomic as possible.
newCert, err = cfg.CacheManagedCertificate(ctx, name)
if err != nil {
if log != nil {
log.Error("loading renewed certificate", zap.String("server_name", name), zap.Error(err))
}
log.Error("loading renewed certificate", zap.String("server_name", name), zap.Error(err))
} else {
// replace the old certificate with the new one
cfg.certCache.replaceCertificate(currentCert, newCert)
@ -672,12 +646,7 @@ func (cfg *Config) renewDynamicCertificate(ctx context.Context, hello *tls.Clien
unblockWaiters()
if err != nil {
if log != nil {
log.Error("renewing and reloading certificate",
zap.String("server_name", name),
zap.Error(err),
zap.Bool("forced", revoked))
}
log.Error("renewing and reloading certificate", zap.Error(err))
return newCert, err
}
@ -723,9 +692,7 @@ func (cfg *Config) getCertFromAnyCertManager(ctx context.Context, hello *tls.Cli
}
}
if upstreamCert == nil {
if log != nil {
log.Debug("all external certificate managers yielded no certificates and no errors", zap.String("sni", hello.ServerName))
}
log.Debug("all external certificate managers yielded no certificates and no errors", zap.String("sni", hello.ServerName))
return Certificate{}, nil
}
@ -735,12 +702,10 @@ func (cfg *Config) getCertFromAnyCertManager(ctx context.Context, hello *tls.Cli
return Certificate{}, fmt.Errorf("external certificate manager: %s: filling cert from leaf: %v", hello.ServerName, err)
}
if log != nil {
log.Debug("using externally-managed certificate",
zap.String("sni", hello.ServerName),
zap.Strings("names", cert.Names),
zap.Time("expiration", cert.Leaf.NotAfter))
}
log.Debug("using externally-managed certificate",
zap.String("sni", hello.ServerName),
zap.Strings("names", cert.Names),
zap.Time("expiration", expiresAt(cert.Leaf)))
return cert, nil
}
@ -785,6 +750,20 @@ func (*Config) getNameFromClientHello(hello *tls.ClientHelloInfo) string {
return localIPFromConn(hello.Conn)
}
// logWithRemote adds the remote host and port to the logger.
func logWithRemote(l *zap.Logger, hello *tls.ClientHelloInfo) *zap.Logger {
if hello.Conn == nil || l == nil {
return l
}
addr := hello.Conn.RemoteAddr().String()
ip, port, err := net.SplitHostPort(addr)
if err != nil {
ip = addr
port = ""
}
return l.With(zap.String("remote_ip", ip), zap.String("remote_port", port))
}
// localIPFromConn returns the host portion of c's local address
// and strips the scope ID if one exists (see RFC 4007).
func localIPFromConn(c net.Conn) string {

View File

@ -73,11 +73,9 @@ func (am *ACMEIssuer) distributedHTTPChallengeSolver(w http.ResponseWriter, r *h
host := hostOnly(r.Host)
chalInfo, distributed, err := am.config.getChallengeInfo(r.Context(), host)
if err != nil {
if am.Logger != nil {
am.Logger.Error("looking up info for HTTP challenge",
zap.String("host", host),
zap.Error(err))
}
am.Logger.Error("looking up info for HTTP challenge",
zap.String("host", host),
zap.Error(err))
return false
}
return solveHTTPChallenge(am.Logger, w, r, chalInfo.Challenge, distributed)
@ -95,13 +93,11 @@ func solveHTTPChallenge(logger *zap.Logger, w http.ResponseWriter, r *http.Reque
w.Header().Add("Content-Type", "text/plain")
w.Write([]byte(challenge.KeyAuthorization))
r.Close = true
if logger != nil {
logger.Info("served key authentication",
zap.String("identifier", challenge.Identifier.Value),
zap.String("challenge", "http-01"),
zap.String("remote", r.RemoteAddr),
zap.Bool("distributed", distributed))
}
logger.Info("served key authentication",
zap.String("identifier", challenge.Identifier.Value),
zap.String("challenge", "http-01"),
zap.String("remote", r.RemoteAddr),
zap.Bool("distributed", distributed))
return true
}
return false

View File

@ -39,18 +39,14 @@ import (
// incrementing panicCount each time. Initial invocation should
// start panicCount at 0.
func (certCache *Cache) maintainAssets(panicCount int) {
log := loggerNamed(certCache.logger, "maintenance")
if log != nil {
log = log.With(zap.String("cache", fmt.Sprintf("%p", certCache)))
}
log := certCache.logger.Named("maintenance")
log = log.With(zap.String("cache", fmt.Sprintf("%p", certCache)))
defer func() {
if err := recover(); err != nil {
buf := make([]byte, stackTraceBufferSize)
buf = buf[:runtime.Stack(buf, false)]
if log != nil {
log.Error("panic", zap.Any("error", err), zap.ByteString("stack", buf))
}
log.Error("panic", zap.Any("error", err), zap.ByteString("stack", buf))
if panicCount < 10 {
certCache.maintainAssets(panicCount + 1)
}
@ -60,9 +56,7 @@ func (certCache *Cache) maintainAssets(panicCount int) {
renewalTicker := time.NewTicker(certCache.options.RenewCheckInterval)
ocspTicker := time.NewTicker(certCache.options.OCSPCheckInterval)
if log != nil {
log.Info("started background certificate maintenance")
}
log.Info("started background certificate maintenance")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@ -71,7 +65,7 @@ func (certCache *Cache) maintainAssets(panicCount int) {
select {
case <-renewalTicker.C:
err := certCache.RenewManagedCertificates(ctx)
if err != nil && log != nil {
if err != nil {
log.Error("renewing managed certificates", zap.Error(err))
}
case <-ocspTicker.C:
@ -79,9 +73,7 @@ func (certCache *Cache) maintainAssets(panicCount int) {
case <-certCache.stopChan:
renewalTicker.Stop()
ocspTicker.Stop()
if log != nil {
log.Info("stopped background certificate maintenance")
}
log.Info("stopped background certificate maintenance")
close(certCache.doneChan)
return
}
@ -94,7 +86,7 @@ func (certCache *Cache) maintainAssets(panicCount int) {
// need to call this. This method assumes non-interactive
// mode (i.e. operating in the background).
func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error {
log := loggerNamed(certCache.logger, "maintenance")
log := certCache.logger.Named("maintenance")
// configs will hold a map of certificate name to the config
// to use when managing that certificate
@ -116,9 +108,7 @@ func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error {
// the list of names on this cert should never be empty... programmer error?
if cert.Names == nil || len(cert.Names) == 0 {
if log != nil {
log.Warn("certificate has no names; removing from cache", zap.String("cert_key", certKey))
}
log.Warn("certificate has no names; removing from cache", zap.String("cert_key", certKey))
deleteQueue = append(deleteQueue, cert)
continue
}
@ -126,19 +116,15 @@ func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error {
// get the config associated with this certificate
cfg, err := certCache.getConfig(cert)
if err != nil {
if log != nil {
log.Error("unable to get configuration to manage certificate; unable to renew",
zap.Strings("identifiers", cert.Names),
zap.Error(err))
}
log.Error("unable to get configuration to manage certificate; unable to renew",
zap.Strings("identifiers", cert.Names),
zap.Error(err))
continue
}
if cfg == nil {
// this is bad if this happens, probably a programmer error (oops)
if log != nil {
log.Error("no configuration associated with certificate; unable to manage",
zap.Strings("identifiers", cert.Names))
}
log.Error("no configuration associated with certificate; unable to manage",
zap.Strings("identifiers", cert.Names))
continue
}
if cfg.OnDemand != nil {
@ -156,11 +142,9 @@ func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error {
storedCertExpiring, err := cfg.managedCertInStorageExpiresSoon(ctx, cert)
if err != nil {
// hmm, weird, but not a big deal, maybe it was deleted or something
if log != nil {
log.Warn("error while checking if stored certificate is also expiring soon",
zap.Strings("identifiers", cert.Names),
zap.Error(err))
}
log.Warn("error while checking if stored certificate is also expiring soon",
zap.Strings("identifiers", cert.Names),
zap.Error(err))
} else if !storedCertExpiring {
// if the certificate is NOT expiring soon and there was no error, then we
// are good to just reload the certificate from storage instead of repeating
@ -180,23 +164,19 @@ func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error {
// Reload certificates that merely need to be updated in memory
for _, oldCert := range reloadQueue {
timeLeft := oldCert.Leaf.NotAfter.Sub(time.Now().UTC())
if log != nil {
log.Info("certificate expires soon, but is already renewed in storage; reloading stored certificate",
zap.Strings("identifiers", oldCert.Names),
zap.Duration("remaining", timeLeft))
}
timeLeft := expiresAt(oldCert.Leaf).Sub(time.Now().UTC())
log.Info("certificate expires soon, but is already renewed in storage; reloading stored certificate",
zap.Strings("identifiers", oldCert.Names),
zap.Duration("remaining", timeLeft))
cfg := configs[oldCert.Names[0]]
// crucially, this happens OUTSIDE a lock on the certCache
_, err := cfg.reloadManagedCertificate(ctx, oldCert)
if err != nil {
if log != nil {
log.Error("loading renewed certificate",
zap.Strings("identifiers", oldCert.Names),
zap.Error(err))
}
log.Error("loading renewed certificate",
zap.Strings("identifiers", oldCert.Names),
zap.Error(err))
continue
}
}
@ -206,11 +186,9 @@ func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error {
cfg := configs[oldCert.Names[0]]
err := certCache.queueRenewalTask(ctx, oldCert, cfg)
if err != nil {
if log != nil {
log.Error("queueing renewal task",
zap.Strings("identifiers", oldCert.Names),
zap.Error(err))
}
log.Error("queueing renewal task",
zap.Strings("identifiers", oldCert.Names),
zap.Error(err))
continue
}
}
@ -226,14 +204,12 @@ func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error {
}
func (certCache *Cache) queueRenewalTask(ctx context.Context, oldCert Certificate, cfg *Config) error {
log := loggerNamed(certCache.logger, "maintenance")
log := certCache.logger.Named("maintenance")
timeLeft := oldCert.Leaf.NotAfter.Sub(time.Now().UTC())
if log != nil {
log.Info("certificate expires soon; queuing for renewal",
zap.Strings("identifiers", oldCert.Names),
zap.Duration("remaining", timeLeft))
}
timeLeft := expiresAt(oldCert.Leaf).Sub(time.Now().UTC())
log.Info("certificate expires soon; queuing for renewal",
zap.Strings("identifiers", oldCert.Names),
zap.Duration("remaining", timeLeft))
// Get the name which we should use to renew this certificate;
// we only support managing certificates with one name per cert,
@ -242,12 +218,10 @@ func (certCache *Cache) queueRenewalTask(ctx context.Context, oldCert Certificat
// queue up this renewal job (is a no-op if already active or queued)
jm.Submit(cfg.Logger, "renew_"+renewName, func() error {
timeLeft := oldCert.Leaf.NotAfter.Sub(time.Now().UTC())
if log != nil {
log.Info("attempting certificate renewal",
zap.Strings("identifiers", oldCert.Names),
zap.Duration("remaining", timeLeft))
}
timeLeft := expiresAt(oldCert.Leaf).Sub(time.Now().UTC())
log.Info("attempting certificate renewal",
zap.Strings("identifiers", oldCert.Names),
zap.Duration("remaining", timeLeft))
// perform renewal - crucially, this happens OUTSIDE a lock on certCache
err := cfg.RenewCertAsync(ctx, renewName, false)
@ -280,7 +254,7 @@ func (certCache *Cache) queueRenewalTask(ctx context.Context, oldCert Certificat
// Ryan Sleevi's recommendations for good OCSP support:
// https://gist.github.com/sleevi/5efe9ef98961ecfb4da8
func (certCache *Cache) updateOCSPStaples(ctx context.Context) {
logger := loggerNamed(certCache.logger, "maintenance")
logger := certCache.logger.Named("maintenance")
// temporary structures to store updates or tasks
// so that we can keep our locks short-lived
@ -311,11 +285,9 @@ func (certCache *Cache) updateOCSPStaples(ctx context.Context) {
}
cfg, err := certCache.getConfig(cert)
if err != nil {
if logger != nil {
logger.Error("unable to get automation config for certificate; maintenance for this certificate will likely fail",
zap.Strings("identifiers", cert.Names),
zap.Error(err))
}
logger.Error("unable to get automation config for certificate; maintenance for this certificate will likely fail",
zap.Strings("identifiers", cert.Names),
zap.Error(err))
continue
}
// always try to replace revoked certificates, even if OCSP response is still fresh
@ -347,10 +319,8 @@ func (certCache *Cache) updateOCSPStaples(ctx context.Context) {
if qe.cfg == nil {
// this is bad if this happens, probably a programmer error (oops)
if logger != nil {
logger.Error("no configuration associated with certificate; unable to manage OCSP staples",
zap.Strings("identifiers", cert.Names))
}
logger.Error("no configuration associated with certificate; unable to manage OCSP staples",
zap.Strings("identifiers", cert.Names))
continue
}
@ -358,11 +328,9 @@ func (certCache *Cache) updateOCSPStaples(ctx context.Context) {
if err != nil {
if cert.ocsp != nil {
// if there was no staple before, that's fine; otherwise we should log the error
if logger != nil {
logger.Error("stapling OCSP",
zap.Strings("identifiers", cert.Names),
zap.Error(err))
}
logger.Error("stapling OCSP",
zap.Strings("identifiers", cert.Names),
zap.Error(err))
}
continue
}
@ -372,17 +340,22 @@ func (certCache *Cache) updateOCSPStaples(ctx context.Context) {
// sure we apply the update to all names on the certificate if
// the status is still Good.
if cert.ocsp != nil && cert.ocsp.Status == ocsp.Good && (lastNextUpdate.IsZero() || lastNextUpdate != cert.ocsp.NextUpdate) {
if logger != nil {
logger.Info("advancing OCSP staple",
zap.Strings("identifiers", cert.Names),
zap.Time("from", lastNextUpdate),
zap.Time("to", cert.ocsp.NextUpdate))
}
logger.Info("advancing OCSP staple",
zap.Strings("identifiers", cert.Names),
zap.Time("from", lastNextUpdate),
zap.Time("to", cert.ocsp.NextUpdate))
updated[certHash] = ocspUpdate{rawBytes: cert.Certificate.OCSPStaple, parsed: cert.ocsp}
}
// If the updated staple shows that the certificate was revoked, we should immediately renew it
if certShouldBeForceRenewed(cert) {
qe.cfg.emit(ctx, "cert_ocsp_revoked", map[string]any{
"subjects": cert.Names,
"certificate": cert,
"reason": cert.ocsp.RevocationReason,
"revoked_at": cert.ocsp.RevokedAt,
})
renewQueue = append(renewQueue, renewQueueEntry{
oldCert: cert,
cfg: qe.cfg,
@ -405,7 +378,7 @@ func (certCache *Cache) updateOCSPStaples(ctx context.Context) {
// Crucially, this happens OUTSIDE a lock on the certCache.
for _, renew := range renewQueue {
_, err := renew.cfg.forceRenew(ctx, logger, renew.oldCert)
if err != nil && logger != nil {
if err != nil {
logger.Info("forcefully renewing certificate due to REVOKED status",
zap.Strings("identifiers", renew.oldCert.Names),
zap.Error(err))
@ -522,7 +495,7 @@ func deleteExpiredCerts(ctx context.Context, storage Storage, gracePeriod time.D
return fmt.Errorf("certificate file %s is malformed; error parsing PEM: %v", assetKey, err)
}
if expiredTime := time.Since(cert.NotAfter); expiredTime >= gracePeriod {
if expiredTime := time.Since(expiresAt(cert)); expiredTime >= gracePeriod {
log.Printf("[INFO] Certificate %s expired %s ago; cleaning up", assetKey, expiredTime)
baseName := strings.TrimSuffix(assetKey, ".crt")
for _, relatedAsset := range []string{
@ -560,16 +533,14 @@ func deleteExpiredCerts(ctx context.Context, storage Storage, gracePeriod time.D
// forceRenew forcefully renews cert and replaces it in the cache, and returns the new certificate. It is intended
// for use primarily in the case of cert revocation. This MUST NOT be called within a lock on cfg.certCacheMu.
func (cfg *Config) forceRenew(ctx context.Context, logger *zap.Logger, cert Certificate) (Certificate, error) {
if logger != nil {
if cert.ocsp != nil && cert.ocsp.Status == ocsp.Revoked {
logger.Warn("OCSP status for managed certificate is REVOKED; attempting to replace with new certificate",
zap.Strings("identifiers", cert.Names),
zap.Time("expiration", cert.Leaf.NotAfter))
} else {
logger.Warn("forcefully renewing certificate",
zap.Strings("identifiers", cert.Names),
zap.Time("expiration", cert.Leaf.NotAfter))
}
if cert.ocsp != nil && cert.ocsp.Status == ocsp.Revoked {
logger.Warn("OCSP status for managed certificate is REVOKED; attempting to replace with new certificate",
zap.Strings("identifiers", cert.Names),
zap.Time("expiration", expiresAt(cert.Leaf)))
} else {
logger.Warn("forcefully renewing certificate",
zap.Strings("identifiers", cert.Names),
zap.Time("expiration", expiresAt(cert.Leaf)))
}
renewName := cert.Names[0]
@ -584,7 +555,7 @@ func (cfg *Config) forceRenew(ctx context.Context, logger *zap.Logger, cert Cert
var obtainInsteadOfRenew bool
if cert.ocsp != nil && cert.ocsp.RevocationReason == acme.ReasonKeyCompromise {
err := cfg.moveCompromisedPrivateKey(ctx, cert, logger)
if err != nil && logger != nil {
if err != nil {
logger.Error("could not remove compromised private key from use",
zap.Strings("identifiers", cert.Names),
zap.String("issuer", cert.issuerKey),
@ -605,11 +576,9 @@ func (cfg *Config) forceRenew(ctx context.Context, logger *zap.Logger, cert Cert
if err != nil {
if cert.ocsp != nil && cert.ocsp.Status == ocsp.Revoked {
// probably better to not serve a revoked certificate at all
if logger != nil {
logger.Error("unable to obtain new to certificate after OCSP status of REVOKED; removing from cache",
zap.Strings("identifiers", cert.Names),
zap.Error(err))
}
logger.Error("unable to obtain new to certificate after OCSP status of REVOKED; removing from cache",
zap.Strings("identifiers", cert.Names),
zap.Error(err))
cfg.certCache.mu.Lock()
cfg.certCache.removeCertificate(cert)
cfg.certCache.mu.Unlock()

View File

@ -98,12 +98,12 @@ func stapleOCSP(ctx context.Context, ocspConfig OCSPConfig, storage Storage, cer
gotNewOCSP = true
}
if ocspResp.NextUpdate.After(cert.Leaf.NotAfter) {
if ocspResp.NextUpdate.After(expiresAt(cert.Leaf)) {
// uh oh, this OCSP response expires AFTER the certificate does, that's kinda bogus.
// it was the reason a lot of Symantec-validated sites (not Caddy) went down
// in October 2017. https://twitter.com/mattiasgeniar/status/919432824708648961
return fmt.Errorf("invalid: OCSP response for %v valid after certificate expiration (%s)",
cert.Names, cert.Leaf.NotAfter.Sub(ocspResp.NextUpdate))
cert.Names, expiresAt(cert.Leaf).Sub(ocspResp.NextUpdate))
}
// Attach the latest OCSP response to the certificate; this is NOT the same

View File

@ -99,7 +99,7 @@ func (s *httpSolver) serve(ctx context.Context, si *solverInfo) {
}
// CleanUp cleans up the HTTP server if it is the last one to finish.
func (s *httpSolver) CleanUp(ctx context.Context, _ acme.Challenge) error {
func (s *httpSolver) CleanUp(_ context.Context, _ acme.Challenge) error {
solversMu.Lock()
defer solversMu.Unlock()
si := getSolverInfo(s.address)
@ -220,7 +220,7 @@ func (*tlsALPNSolver) handleConn(conn net.Conn) {
// CleanUp removes the challenge certificate from the cache, and if
// it is the last one to finish, stops the TLS server.
func (s *tlsALPNSolver) CleanUp(ctx context.Context, chal acme.Challenge) error {
func (s *tlsALPNSolver) CleanUp(_ context.Context, chal acme.Challenge) error {
solversMu.Lock()
defer solversMu.Unlock()
si := getSolverInfo(s.address)
@ -234,7 +234,6 @@ func (s *tlsALPNSolver) CleanUp(ctx context.Context, chal acme.Challenge) error
}
delete(solvers, s.address)
}
return nil
}
@ -357,7 +356,7 @@ func (s *DNS01Solver) Wait(ctx context.Context, challenge acme.Challenge) error
// timings
timeout := s.PropagationTimeout
if timeout == 0 {
timeout = 2 * time.Minute
timeout = defaultDNSPropagationTimeout
}
const interval = 2 * time.Second
@ -386,7 +385,12 @@ func (s *DNS01Solver) Wait(ctx context.Context, challenge acme.Challenge) error
}
// CleanUp deletes the DNS TXT record created in Present().
func (s *DNS01Solver) CleanUp(ctx context.Context, challenge acme.Challenge) error {
//
// We ignore the context because cleanup is often/likely performed after
// a context cancellation, and properly-implemented DNS providers should
// honor cancellation, which would result in cleanup being aborted.
// Cleanup must always occur.
func (s *DNS01Solver) CleanUp(_ context.Context, challenge acme.Challenge) error {
dnsName := challenge.DNS01TXTRecordName()
if s.OverrideDomain != "" {
dnsName = s.OverrideDomain
@ -402,7 +406,17 @@ func (s *DNS01Solver) CleanUp(ctx context.Context, challenge acme.Challenge) err
return err
}
// clean up the record
// clean up the record - use a different context though, since
// one common reason cleanup is performed is because a context
// was canceled, and if so, any HTTP requests by this provider
// should fail if the provider is properly implemented
// (see issue #200)
timeout := s.PropagationTimeout
if timeout <= 0 {
timeout = defaultDNSPropagationTimeout
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
_, err = s.DNSProvider.DeleteRecords(ctx, memory.dnsZone, []libdns.Record{memory.rec})
if err != nil {
return fmt.Errorf("deleting temporary record for name %q in zone %q: %w", memory.dnsName, memory.dnsZone, err)
@ -411,6 +425,8 @@ func (s *DNS01Solver) CleanUp(ctx context.Context, challenge acme.Challenge) err
return nil
}
const defaultDNSPropagationTimeout = 2 * time.Minute
type dnsPresentMemory struct {
dnsZone string
dnsName string
@ -680,7 +696,7 @@ var (
// data that can make it easier or more efficient to solve.
type Challenge struct {
acme.Challenge
data interface{}
data any
}
// challengeKey returns the map key for a given challenge; it is the identifier

View File

@ -29,19 +29,30 @@ import (
// Keys are prefix-based, with forward slash '/' as separators
// and without a leading slash.
//
// Processes running in a cluster will wish to use the
// same Storage value (its implementation and configuration)
// in order to share certificates and other TLS resources
// with the cluster.
// Processes running in a cluster should use the same Storage
// value (with the same configuration) in order to share
// certificates and other TLS resources with the cluster.
//
// The Load, Delete, List, and Stat methods should return
// fs.ErrNotExist if the key does not exist.
//
// Implementations of Storage must be safe for concurrent use
// and honor context cancellations.
// and honor context cancellations. Methods should block until
// their operation is complete; that is, Load() should always
// return the value from the last call to Store() for a given
// key, and concurrent calls to Store() should not corrupt a
// file.
//
// For simplicity, this is not a streaming API and is not
// suitable for very large files.
type Storage interface {
// Locker provides atomic synchronization
// operations, making Storage safe to share.
// The use of Locker is not expected around
// every other method (Store, Load, etc.)
// as those should already be thread-safe;
// Locker is intended for custom jobs or
// transactions that need synchronization.
Locker
// Store puts value at key.
@ -70,10 +81,11 @@ type Storage interface {
Stat(ctx context.Context, key string) (KeyInfo, error)
}
// Locker facilitates synchronization of certificate tasks across
// machines and networks.
// Locker facilitates synchronization across machines and networks.
// It essentially provides a distributed named-mutex service so
// that multiple consumers can coordinate tasks and share resources.
type Locker interface {
// Lock acquires the lock for key, blocking until the lock
// Lock acquires the lock for name, blocking until the lock
// can be obtained or an error is returned. Note that, even
// after acquiring a lock, an idempotent operation may have
// already been performed by another process that acquired
@ -86,20 +98,25 @@ type Locker interface {
// same time always results in only one caller receiving the
// lock at any given time.
//
// To prevent deadlocks, all implementations (where this concern
// is relevant) should put a reasonable expiration on the lock in
// case Unlock is unable to be called due to some sort of network
// failure or system crash. Additionally, implementations should
// honor context cancellation as much as possible (in case the
// caller wishes to give up and free resources before the lock
// can be obtained).
Lock(ctx context.Context, key string) error
// To prevent deadlocks, all implementations should put a
// reasonable expiration on the lock in case Unlock is unable
// to be called due to some sort of network failure or system
// crash. Additionally, implementations should honor context
// cancellation as much as possible (in case the caller wishes
// to give up and free resources before the lock can be obtained).
//
// Additionally, implementations may wish to support fencing
// tokens (https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html)
// in order to be robust against long process pauses, extremely
// high network latency (or other factors that get in the way of
// renewing lock leases).
Lock(ctx context.Context, name string) error
// Unlock releases the lock for key. This method must ONLY be
// Unlock releases the lock for name. This method must ONLY be
// called after a successful call to Lock, and only after the
// critical section is finished, even if it errored or timed
// out. Unlock cleans up any resources allocated during Lock.
Unlock(ctx context.Context, key string) error
Unlock(ctx context.Context, name string) error
}
// KeyInfo holds information about a key in storage.
@ -220,16 +237,14 @@ func CleanUpOwnLocks(ctx context.Context, logger *zap.Logger) {
locksMu.Lock()
defer locksMu.Unlock()
for lockKey, storage := range locks {
err := storage.Unlock(ctx, lockKey)
if err == nil {
delete(locks, lockKey)
} else if logger != nil {
if err := storage.Unlock(ctx, lockKey); err != nil {
logger.Error("unable to clean up lock in storage backend",
zap.Any("storage", storage),
zap.String("lock_key", lockKey),
zap.Error(err),
)
zap.Error(err))
continue
}
delete(locks, lockKey)
}
}

View File

@ -5,7 +5,7 @@ all: build
## test: Run all tests
test:
go test -race -coverprofile=/dev/null -timeout 60s -v ./...
go test -race -coverprofile=/dev/null -v ./...
## vet: Analyze code for potential errors
vet:

View File

@ -53,7 +53,7 @@ type Conn interface {
StreamId() string
// Stats returns accumulated and instantaneous statistics of the connection.
Stats() Statistics
Stats(s *Statistics)
}
type connStats struct {
@ -73,6 +73,7 @@ type connStats struct {
pktRecvKeepalive uint64
pktSentShutdown uint64
pktRecvShutdown uint64
mbpsLinkCapacity float64
}
// Check if we implement the net.Conn interface
@ -118,6 +119,8 @@ type srtConn struct {
tsbpdTimeBaseOffset uint64 // microseconds
tsbpdDelay uint64 // microseconds
tsbpdDrift uint64 // microseconds
peerTsbpdDelay uint64 // microseconds
dropThreshold uint64 // microseconds
// Queue for packets that are coming from the network
networkQueue chan packet.Packet
@ -162,7 +165,8 @@ type srtConnConfig struct {
socketId uint32
peerSocketId uint32
tsbpdTimeBase uint64 // microseconds
tsbpdDelay uint64
tsbpdDelay uint64 // microseconds
peerTsbpdDelay uint64 // microseconds
initialPacketSequenceNumber circular.Number
crypto crypto.Crypto
keyBaseEncryption packet.PacketEncryption
@ -181,6 +185,7 @@ func newSRTConn(config srtConnConfig) *srtConn {
peerSocketId: config.peerSocketId,
tsbpdTimeBase: config.tsbpdTimeBase,
tsbpdDelay: config.tsbpdDelay,
peerTsbpdDelay: config.peerTsbpdDelay,
initialPacketSequenceNumber: config.initialPacketSequenceNumber,
crypto: config.crypto,
keyBaseEncryption: config.keyBaseEncryption,
@ -237,9 +242,16 @@ func newSRTConn(config srtConnConfig) *srtConn {
})
// 4.6. Too-Late Packet Drop -> 125% of SRT latency, at least 1 second
// https://github.com/Haivision/srt/blob/master/docs/API/API-socket-options.md#SRTO_SNDDROPDELAY
c.dropThreshold = uint64(float64(c.peerTsbpdDelay)*1.25) + uint64(c.config.SendDropDelay.Microseconds())
if c.dropThreshold < uint64(time.Second.Microseconds()) {
c.dropThreshold = uint64(time.Second.Microseconds())
}
c.dropThreshold += 20_000
c.snd = congestion.NewLiveSend(congestion.SendConfig{
InitialSequenceNumber: c.initialPacketSequenceNumber,
DropInterval: uint64(c.config.SendDropDelay.Microseconds()),
DropThreshold: c.dropThreshold,
MaxBW: c.config.MaxBW,
InputBW: c.config.InputBW,
MinInputBW: c.config.MinInputBW,
@ -582,6 +594,8 @@ func (c *srtConn) handlePacket(p packet.Packet) {
c.debug.expectedRcvPacketSequenceNumber = header.PacketSequenceNumber.Inc()
//fmt.Printf("%s\n", p.String())
// Ignore FEC filter control packets
// https://github.com/Haivision/srt/blob/master/docs/features/packet-filtering-and-fec.md
// "An FEC control packet is distinguished from a regular data packet by having
@ -682,6 +696,9 @@ func (c *srtConn) handleACK(p packet.Packet) {
// 4.10. Round-Trip Time Estimation
c.recalculateRTT(time.Duration(int64(cif.RTT)) * time.Microsecond)
// Estimated Link Capacity (from packets/s to Mbps)
c.statistics.mbpsLinkCapacity = float64(cif.EstimatedLinkCapacity) * MAX_PAYLOAD_SIZE * 8 / 1024 / 1024
c.sendACKACK(p.Header().TypeSpecific)
}
}
@ -901,14 +918,14 @@ func (c *srtConn) sendACK(seq circular.Number, lite bool) {
p.Header().TypeSpecific = 0
} else {
pps, _ := c.recv.PacketRate()
pps, bps, capacity := c.recv.PacketRate()
cif.RTT = uint32(c.rtt)
cif.RTTVar = uint32(c.rttVar)
cif.AvailableBufferSize = c.config.FC // TODO: available buffer size (packets)
cif.PacketsReceivingRate = pps // packets receiving rate (packets/s)
cif.EstimatedLinkCapacity = 0 // estimated link capacity (packets/s), not relevant for live mode
cif.ReceivingRate = 0 // receiving rate (bytes/s), not relevant for live mode
cif.AvailableBufferSize = c.config.FC // TODO: available buffer size (packets)
cif.PacketsReceivingRate = uint32(pps) // packets receiving rate (packets/s)
cif.EstimatedLinkCapacity = uint32(capacity) // estimated link capacity (packets/s), not relevant for live mode
cif.ReceivingRate = uint32(bps) // receiving rate (bytes/s), not relevant for live mode
p.Header().TypeSpecific = c.nextACKNumber.Val()
@ -1049,63 +1066,119 @@ func (c *srtConn) SetDeadline(t time.Time) error { return nil }
func (c *srtConn) SetReadDeadline(t time.Time) error { return nil }
func (c *srtConn) SetWriteDeadline(t time.Time) error { return nil }
func (c *srtConn) Stats() Statistics {
func (c *srtConn) Stats(s *Statistics) {
now := uint64(time.Since(c.start).Milliseconds())
send := c.snd.Stats()
recv := c.recv.Stats()
s := Statistics{
MsTimeStamp: uint64(time.Since(c.start).Milliseconds()),
previous := s.Accumulated
interval := now - s.MsTimeStamp
// Accumulated
PktSent: send.PktSent,
PktRecv: recv.PktRecv,
PktSentUnique: send.PktSentUnique,
PktRecvUnique: recv.PktRecvUnique,
PktSndLoss: send.PktSndLoss,
PktRcvLoss: recv.PktRcvLoss,
PktRetrans: send.PktRetrans,
PktRcvRetrans: recv.PktRcvRetrans,
PktSentACK: c.statistics.pktSentACK,
PktRecvACK: c.statistics.pktRecvACK,
PktSentNAK: c.statistics.pktSentNAK,
PktRecvNAK: c.statistics.pktRecvNAK,
PktSentKM: c.statistics.pktSentKM,
PktRecvKM: c.statistics.pktRecvKM,
UsSndDuration: send.UsSndDuration,
PktSndDrop: send.PktSndDrop,
PktRcvDrop: recv.PktRcvDrop,
PktRcvUndecrypt: c.statistics.pktRecvUndecrypt,
ByteSent: send.ByteSent + (send.PktSent * c.statistics.headerSize),
ByteRecv: recv.ByteRecv + (recv.PktRecv * c.statistics.headerSize),
ByteSentUnique: send.ByteSentUnique + (send.PktSentUnique * c.statistics.headerSize),
ByteRecvUnique: recv.ByteRecvUnique + (recv.PktRecvUnique * c.statistics.headerSize),
ByteRcvLoss: recv.ByteRcvLoss + (recv.PktRcvLoss * c.statistics.headerSize),
ByteRetrans: send.ByteRetrans + (send.PktRetrans * c.statistics.headerSize),
ByteSndDrop: send.ByteSndDrop + (send.PktSndDrop * c.statistics.headerSize),
ByteRcvDrop: recv.ByteRcvDrop + (recv.PktRcvDrop * c.statistics.headerSize),
ByteRcvUndecrypt: c.statistics.byteRecvUndecrypt + (c.statistics.pktRecvUndecrypt * c.statistics.headerSize),
// Instantaneous
UsPktSndPeriod: send.UsPktSndPeriod,
PktFlowWindow: uint64(c.config.FC),
PktFlightSize: send.PktFlightSize,
MsRTT: c.rtt / 1_000,
MbpsBandwidth: 0,
ByteAvailSndBuf: 0,
ByteAvailRcvBuf: 0,
MbpsMaxBW: float64(c.config.MaxBW / 1024 / 1024),
ByteMSS: uint64(c.config.MSS),
PktSndBuf: send.PktSndBuf,
ByteSndBuf: send.ByteSndBuf,
MsSndBuf: send.MsSndBuf,
MsSndTsbPdDelay: uint64(c.config.PeerLatency),
PktRcvBuf: recv.PktRcvBuf,
ByteRcvBuf: recv.ByteRcvBuf,
MsRcvBuf: recv.MsRcvBuf,
MsRcvTsbPdDelay: uint64(c.config.ReceiverLatency),
PktReorderTolerance: 0,
PktRcvAvgBelatedTime: 0,
// Accumulated
s.Accumulated = StatisticsAccumulated{
PktSent: send.Pkt,
PktRecv: recv.Pkt,
PktSentUnique: send.PktUnique,
PktRecvUnique: recv.PktUnique,
PktSendLoss: send.PktLoss,
PktRecvLoss: recv.PktLoss,
PktRetrans: send.PktRetrans,
PktRecvRetrans: recv.PktRetrans,
PktSentACK: c.statistics.pktSentACK,
PktRecvACK: c.statistics.pktRecvACK,
PktSentNAK: c.statistics.pktSentNAK,
PktRecvNAK: c.statistics.pktRecvNAK,
PktSentKM: c.statistics.pktSentKM,
PktRecvKM: c.statistics.pktRecvKM,
UsSndDuration: send.UsSndDuration,
PktSendDrop: send.PktDrop,
PktRecvDrop: recv.PktDrop,
PktRecvUndecrypt: c.statistics.pktRecvUndecrypt,
ByteSent: send.Byte + (send.Pkt * c.statistics.headerSize),
ByteRecv: recv.Byte + (recv.Pkt * c.statistics.headerSize),
ByteSentUnique: send.ByteUnique + (send.PktUnique * c.statistics.headerSize),
ByteRecvUnique: recv.ByteUnique + (recv.PktUnique * c.statistics.headerSize),
ByteRecvLoss: recv.ByteLoss + (recv.PktLoss * c.statistics.headerSize),
ByteRetrans: send.ByteRetrans + (send.PktRetrans * c.statistics.headerSize),
ByteRecvRetrans: recv.ByteRetrans + (recv.PktRetrans * c.statistics.headerSize),
ByteSendDrop: send.ByteDrop + (send.PktDrop * c.statistics.headerSize),
ByteRecvDrop: recv.ByteDrop + (recv.PktDrop * c.statistics.headerSize),
ByteRecvUndecrypt: c.statistics.byteRecvUndecrypt + (c.statistics.pktRecvUndecrypt * c.statistics.headerSize),
}
return s
// Interval
s.Interval = StatisticsInterval{
MsInterval: interval,
PktSent: s.Accumulated.PktSent - previous.PktSent,
PktRecv: s.Accumulated.PktRecv - previous.PktRecv,
PktSentUnique: s.Accumulated.PktSentUnique - previous.PktSentUnique,
PktRecvUnique: s.Accumulated.PktRecvUnique - previous.PktRecvUnique,
PktSendLoss: s.Accumulated.PktSendLoss - previous.PktSendLoss,
PktRecvLoss: s.Accumulated.PktRecvLoss - previous.PktRecvLoss,
PktRetrans: s.Accumulated.PktRetrans - previous.PktRetrans,
PktRecvRetrans: s.Accumulated.PktRecvRetrans - previous.PktRecvRetrans,
PktSentACK: s.Accumulated.PktSentACK - previous.PktSentACK,
PktRecvACK: s.Accumulated.PktRecvACK - previous.PktRecvACK,
PktSentNAK: s.Accumulated.PktSentNAK - previous.PktSentNAK,
PktRecvNAK: s.Accumulated.PktRecvNAK - previous.PktRecvNAK,
MbpsSendRate: float64(s.Accumulated.ByteSent-previous.ByteSent) * 8 / 1024 / 1024 / (float64(interval) / 1000),
MbpsRecvRate: float64(s.Accumulated.ByteRecv-previous.ByteRecv) * 8 / 1024 / 1024 / (float64(interval) / 1000),
UsSndDuration: s.Accumulated.UsSndDuration - previous.UsSndDuration,
PktReorderDistance: 0,
PktRecvBelated: s.Accumulated.PktRecvBelated - previous.PktRecvBelated,
PktSndDrop: s.Accumulated.PktSendDrop - previous.PktSendDrop,
PktRecvDrop: s.Accumulated.PktRecvDrop - previous.PktRecvDrop,
PktRecvUndecrypt: s.Accumulated.PktRecvUndecrypt - previous.PktRecvUndecrypt,
ByteSent: s.Accumulated.ByteSent - previous.ByteSent,
ByteRecv: s.Accumulated.ByteRecv - previous.ByteRecv,
ByteSentUnique: s.Accumulated.ByteSentUnique - previous.ByteSentUnique,
ByteRecvUnique: s.Accumulated.ByteRecvUnique - previous.ByteRecvUnique,
ByteRecvLoss: s.Accumulated.ByteRecvLoss - previous.ByteRecvLoss,
ByteRetrans: s.Accumulated.ByteRetrans - previous.ByteRetrans,
ByteRecvRetrans: s.Accumulated.ByteRecvRetrans - previous.ByteRecvRetrans,
ByteRecvBelated: s.Accumulated.ByteRecvBelated - previous.ByteRecvBelated,
ByteSendDrop: s.Accumulated.ByteSendDrop - previous.ByteSendDrop,
ByteRecvDrop: s.Accumulated.ByteRecvDrop - previous.ByteRecvDrop,
ByteRecvUndecrypt: s.Accumulated.ByteRecvUndecrypt - previous.ByteRecvUndecrypt,
}
// Instantaneous
s.Instantaneous = StatisticsInstantaneous{
UsPktSendPeriod: send.UsPktSndPeriod,
PktFlowWindow: uint64(c.config.FC),
PktFlightSize: send.PktFlightSize,
MsRTT: c.rtt / 1000,
MbpsSentRate: send.MbpsEstimatedSentBandwidth,
MbpsRecvRate: recv.MbpsEstimatedRecvBandwidth,
MbpsLinkCapacity: recv.MbpsEstimatedLinkCapacity,
ByteAvailSendBuf: 0, // unlimited
ByteAvailRecvBuf: 0, // unlimited
MbpsMaxBW: float64(c.config.MaxBW) / 1024 / 1024,
ByteMSS: uint64(c.config.MSS),
PktSendBuf: send.PktBuf,
ByteSendBuf: send.ByteBuf,
MsSendBuf: send.MsBuf,
MsSendTsbPdDelay: c.peerTsbpdDelay / 1000,
PktRecvBuf: recv.PktBuf,
ByteRecvBuf: recv.ByteBuf,
MsRecvBuf: recv.MsBuf,
MsRecvTsbPdDelay: c.tsbpdDelay / 1000,
PktReorderTolerance: uint64(c.config.LossMaxTTL),
PktRecvAvgBelatedTime: 0,
PktSendLossRate: send.PktLossRate,
PktRecvLossRate: recv.PktLossRate,
}
// If we're only sending, the receiver congestion control value for the link capacity is zero,
// use the value that we got from the receiver via the ACK packets.
if s.Instantaneous.MbpsLinkCapacity == 0 {
s.Instantaneous.MbpsLinkCapacity = c.statistics.mbpsLinkCapacity
}
if c.config.MaxBW < 0 {
s.Instantaneous.MbpsMaxBW = -1
}
s.MsTimeStamp = now
}

View File

@ -475,15 +475,16 @@ func (dl *dialer) handleHandshake(p packet.Packet) {
return
}
// Use the largest TSBPD delay as advertised by the listener, but
// at least 120ms
tsbpdDelay := uint16(120)
if cif.RecvTSBPDDelay > tsbpdDelay {
tsbpdDelay = cif.RecvTSBPDDelay
// Select the largest TSBPD delay advertised by the listener, but at least 120ms
recvTsbpdDelay := uint16(dl.config.ReceiverLatency.Milliseconds())
sendTsbpdDelay := uint16(dl.config.PeerLatency.Milliseconds())
if cif.SendTSBPDDelay > recvTsbpdDelay {
recvTsbpdDelay = cif.SendTSBPDDelay
}
if cif.SendTSBPDDelay > tsbpdDelay {
tsbpdDelay = cif.SendTSBPDDelay
if cif.RecvTSBPDDelay > sendTsbpdDelay {
sendTsbpdDelay = cif.RecvTSBPDDelay
}
// If the peer has a smaller MTU size, adjust to it
@ -512,7 +513,8 @@ func (dl *dialer) handleHandshake(p packet.Packet) {
socketId: dl.socketId,
peerSocketId: cif.SRTSocketId,
tsbpdTimeBase: uint64(time.Since(dl.start).Microseconds()),
tsbpdDelay: uint64(tsbpdDelay) * 1000,
tsbpdDelay: uint64(recvTsbpdDelay) * 1000,
peerTsbpdDelay: uint64(sendTsbpdDelay) * 1000,
initialPacketSequenceNumber: cif.InitialPacketSequenceNumber,
crypto: dl.crypto,
keyBaseEncryption: packet.EvenKeyEncrypted,
@ -700,7 +702,7 @@ func (dl *dialer) writePacket(p packet.Packet) error {
func (dl *dialer) SetDeadline(t time.Time) error { return dl.conn.SetDeadline(t) }
func (dl *dialer) SetReadDeadline(t time.Time) error { return dl.conn.SetReadDeadline(t) }
func (dl *dialer) SetWriteDeadline(t time.Time) error { return dl.conn.SetWriteDeadline(t) }
func (dl *dialer) Stats() Statistics { return dl.conn.Stats() }
func (dl *dialer) Stats(s *Statistics) { dl.conn.Stats(s) }
func (dl *dialer) log(topic string, message func() string) {
dl.config.Logger.Print(topic, dl.socketId, 2, message)

View File

@ -9,7 +9,7 @@ import (
// SendConfig is the configuration for the liveSend congestion control
type SendConfig struct {
InitialSequenceNumber circular.Number
DropInterval uint64
DropThreshold uint64
MaxBW int64
InputBW int64
MinInputBW int64
@ -25,6 +25,7 @@ type Sender interface {
Tick(now uint64)
ACK(sequenceNumber circular.Number)
NAK(sequenceNumbers []circular.Number)
SetDropThreshold(threshold uint64)
}
// ReceiveConfig is the configuration for the liveResv congestion control
@ -40,7 +41,7 @@ type ReceiveConfig struct {
// Receiver is the receiving part of the congestion control
type Receiver interface {
Stats() ReceiveStats
PacketRate() (pps, bps uint32)
PacketRate() (pps, bps, capacity float64)
Flush()
Push(pkt packet.Packet)
Tick(now uint64)
@ -49,55 +50,68 @@ type Receiver interface {
// SendStats are collected statistics from liveSend
type SendStats struct {
PktSent uint64
ByteSent uint64
Pkt uint64 // Sent packets in total
Byte uint64 // Sent bytes in total
PktSentUnique uint64
ByteSentUnique uint64
PktUnique uint64
ByteUnique uint64
PktSndLoss uint64
ByteSndLoss uint64
PktLoss uint64
ByteLoss uint64
PktRetrans uint64
ByteRetrans uint64
UsSndDuration uint64 // microseconds
PktSndDrop uint64
ByteSndDrop uint64
PktDrop uint64
ByteDrop uint64
// instantaneous
PktSndBuf uint64
ByteSndBuf uint64
MsSndBuf uint64
PktBuf uint64
ByteBuf uint64
MsBuf uint64
PktFlightSize uint64
UsPktSndPeriod float64 // microseconds
BytePayload uint64
MbpsEstimatedInputBandwidth float64
MbpsEstimatedSentBandwidth float64
PktLossRate float64
}
// ReceiveStats are collected statistics from liveRecv
type ReceiveStats struct {
PktRecv uint64
ByteRecv uint64
Pkt uint64
Byte uint64
PktRecvUnique uint64
ByteRecvUnique uint64
PktUnique uint64
ByteUnique uint64
PktRcvLoss uint64
ByteRcvLoss uint64
PktLoss uint64
ByteLoss uint64
PktRcvRetrans uint64
ByteRcvRetrans uint64
PktRetrans uint64
ByteRetrans uint64
PktRcvDrop uint64
ByteRcvDrop uint64
PktBelated uint64
ByteBelated uint64
PktDrop uint64
ByteDrop uint64
// instantaneous
PktRcvBuf uint64
ByteRcvBuf uint64
MsRcvBuf uint64
PktBuf uint64
ByteBuf uint64
MsBuf uint64
BytePayload uint64
MbpsEstimatedRecvBandwidth float64
MbpsEstimatedLinkCapacity float64
PktLossRate float64
}

View File

@ -14,13 +14,12 @@ import (
// liveSend implements the Sender interface
type liveSend struct {
nextSequenceNumber circular.Number
dropThreshold uint64
packetList *list.List
lossList *list.List
lock sync.RWMutex
dropInterval uint64 // microseconds
avgPayloadSize float64 // bytes
pktSndPeriod float64 // microseconds
maxBW float64 // bytes/s
@ -29,14 +28,20 @@ type liveSend struct {
statistics SendStats
rate struct {
period time.Duration
last time.Time
probeTime uint64
bytes uint64
prevBytes uint64
rate struct {
period uint64 // microseconds
last uint64
bytes uint64
bytesSent uint64
bytesRetrans uint64
estimatedInputBW float64 // bytes/s
estimatedSentBW float64 // bytes/s
pktLossRate float64
}
deliver func(p packet.Packet)
@ -46,12 +51,11 @@ type liveSend struct {
func NewLiveSend(config SendConfig) Sender {
s := &liveSend{
nextSequenceNumber: config.InitialSequenceNumber,
dropThreshold: config.DropThreshold,
packetList: list.New(),
lossList: list.New(),
dropInterval: config.DropInterval, // microseconds
avgPayloadSize: 1456, // 5.1.2. SRT's Default LiveCC Algorithm
avgPayloadSize: packet.MAX_PAYLOAD_SIZE, // 5.1.2. SRT's Default LiveCC Algorithm
maxBW: float64(config.MaxBW),
inputBW: float64(config.InputBW),
overheadBW: float64(config.OverheadBW),
@ -66,8 +70,8 @@ func NewLiveSend(config SendConfig) Sender {
s.maxBW = 128 * 1024 * 1024 // 1 Gbit/s
s.pktSndPeriod = (s.avgPayloadSize + 16) * 1_000_000 / s.maxBW
s.rate.period = time.Second
s.rate.last = time.Now()
s.rate.period = uint64(time.Second.Microseconds())
s.rate.last = 0
return s
}
@ -78,15 +82,20 @@ func (s *liveSend) Stats() SendStats {
s.statistics.UsPktSndPeriod = s.pktSndPeriod
s.statistics.BytePayload = uint64(s.avgPayloadSize)
s.statistics.MsSndBuf = 0
s.statistics.MsBuf = 0
max := s.lossList.Back()
min := s.lossList.Front()
if max != nil && min != nil {
s.statistics.MsSndBuf = (max.Value.(packet.Packet).Header().PktTsbpdTime - min.Value.(packet.Packet).Header().PktTsbpdTime) / 1_000
s.statistics.MsBuf = (max.Value.(packet.Packet).Header().PktTsbpdTime - min.Value.(packet.Packet).Header().PktTsbpdTime) / 1_000
}
s.statistics.MbpsEstimatedInputBandwidth = s.rate.estimatedInputBW * 8 / 1024 / 1024
s.statistics.MbpsEstimatedSentBandwidth = s.rate.estimatedSentBW * 8 / 1024 / 1024
s.statistics.PktLossRate = s.rate.pktLossRate
return s.statistics
}
@ -112,24 +121,27 @@ func (s *liveSend) Push(p packet.Packet) {
pktLen := p.Len()
s.statistics.PktSndBuf++
s.statistics.ByteSndBuf += pktLen
s.statistics.PktBuf++
s.statistics.ByteBuf += pktLen
// bandwidth calculation
// input bandwidth calculation
s.rate.bytes += pktLen
now := time.Now()
tdiff := now.Sub(s.rate.last)
if tdiff > s.rate.period {
s.rate.estimatedInputBW = float64(s.rate.bytes-s.rate.prevBytes) / tdiff.Seconds()
s.rate.prevBytes = s.rate.bytes
s.rate.last = now
}
p.Header().Timestamp = uint32(p.Header().PktTsbpdTime & uint64(packet.MAX_TIMESTAMP))
// Every 16th and 17th packet should be sent at the same time in order
// for the receiver to determine the link capacity. Not really well
// documented in the specs.
// PktTsbpdTime is used for the timing of sending the packets. Here we
// can modify it because it has already been used to set the packet's
// timestamp.
probe := p.Header().PacketSequenceNumber.Val() & 0xF
if probe == 0 {
s.probeTime = p.Header().PktTsbpdTime
} else if probe == 1 {
p.Header().PktTsbpdTime = s.probeTime
}
s.packetList.PushBack(p)
s.statistics.PktFlightSize = uint64(s.packetList.Len())
@ -142,16 +154,20 @@ func (s *liveSend) Tick(now uint64) {
for e := s.packetList.Front(); e != nil; e = e.Next() {
p := e.Value.(packet.Packet)
if p.Header().PktTsbpdTime <= now {
s.statistics.PktSent++
s.statistics.PktSentUnique++
s.statistics.Pkt++
s.statistics.PktUnique++
s.statistics.ByteSent += p.Len()
s.statistics.ByteSentUnique += p.Len()
pktLen := p.Len()
s.statistics.Byte += pktLen
s.statistics.ByteUnique += pktLen
s.statistics.UsSndDuration += uint64(s.pktSndPeriod)
// 5.1.2. SRT's Default LiveCC Algorithm
s.avgPayloadSize = 0.875*s.avgPayloadSize + 0.125*float64(p.Len())
s.avgPayloadSize = 0.875*s.avgPayloadSize + 0.125*float64(pktLen)
s.rate.bytesSent += pktLen
s.deliver(p)
removeList = append(removeList, e)
@ -171,12 +187,12 @@ func (s *liveSend) Tick(now uint64) {
for e := s.lossList.Front(); e != nil; e = e.Next() {
p := e.Value.(packet.Packet)
if p.Header().PktTsbpdTime+s.dropInterval <= now {
if p.Header().PktTsbpdTime+s.dropThreshold <= now {
// dropped packet because too old
s.statistics.PktSndDrop++
s.statistics.PktSndLoss++
s.statistics.ByteSndDrop += p.Len()
s.statistics.ByteSndLoss += p.Len()
s.statistics.PktDrop++
s.statistics.PktLoss++
s.statistics.ByteDrop += p.Len()
s.statistics.ByteLoss += p.Len()
removeList = append(removeList, e)
}
@ -186,8 +202,8 @@ func (s *liveSend) Tick(now uint64) {
for _, e := range removeList {
p := e.Value.(packet.Packet)
s.statistics.PktSndBuf--
s.statistics.ByteSndBuf -= p.Len()
s.statistics.PktBuf--
s.statistics.ByteBuf -= p.Len()
s.lossList.Remove(e)
@ -195,6 +211,26 @@ func (s *liveSend) Tick(now uint64) {
p.Decommission()
}
s.lock.Unlock()
s.lock.Lock()
tdiff := now - s.rate.last
if tdiff > s.rate.period {
s.rate.estimatedInputBW = float64(s.rate.bytes) / (float64(tdiff) / 1000 / 1000)
s.rate.estimatedSentBW = float64(s.rate.bytesSent) / (float64(tdiff) / 1000 / 1000)
if s.rate.bytesSent != 0 {
s.rate.pktLossRate = float64(s.rate.bytesRetrans) / float64(s.rate.bytesSent) * 100
} else {
s.rate.pktLossRate = 0
}
s.rate.bytes = 0
s.rate.bytesSent = 0
s.rate.bytesRetrans = 0
s.rate.last = now
}
s.lock.Unlock()
}
func (s *liveSend) ACK(sequenceNumber circular.Number) {
@ -216,8 +252,8 @@ func (s *liveSend) ACK(sequenceNumber circular.Number) {
for _, e := range removeList {
p := e.Value.(packet.Packet)
s.statistics.PktSndBuf--
s.statistics.ByteSndBuf -= p.Len()
s.statistics.PktBuf--
s.statistics.ByteBuf -= p.Len()
s.lossList.Remove(e)
@ -242,16 +278,19 @@ func (s *liveSend) NAK(sequenceNumbers []circular.Number) {
for i := 0; i < len(sequenceNumbers); i += 2 {
if p.Header().PacketSequenceNumber.Gte(sequenceNumbers[i]) && p.Header().PacketSequenceNumber.Lte(sequenceNumbers[i+1]) {
s.statistics.PktRetrans++
s.statistics.PktSent++
s.statistics.PktSndLoss++
s.statistics.Pkt++
s.statistics.PktLoss++
s.statistics.ByteRetrans += p.Len()
s.statistics.ByteSent += p.Len()
s.statistics.ByteSndLoss += p.Len()
s.statistics.Byte += p.Len()
s.statistics.ByteLoss += p.Len()
// 5.1.2. SRT's Default LiveCC Algorithm
s.avgPayloadSize = 0.875*s.avgPayloadSize + 0.125*float64(p.Len())
s.rate.bytesSent += p.Len()
s.rate.bytesRetrans += p.Len()
p.Header().RetransmittedPacketFlag = true
s.deliver(p)
}
@ -259,6 +298,13 @@ func (s *liveSend) NAK(sequenceNumbers []circular.Number) {
}
}
func (s *liveSend) SetDropThreshold(threshold uint64) {
s.lock.Lock()
defer s.lock.Unlock()
s.dropThreshold = threshold
}
// liveReceive implements the Receiver interface
type liveReceive struct {
maxSeenSequenceNumber circular.Number
@ -275,21 +321,26 @@ type liveReceive struct {
lastPeriodicACK uint64
lastPeriodicNAK uint64
avgPayloadSize float64 // bytes
avgPayloadSize float64 // bytes
avgLinkCapacity float64 // packets per second
probeTime time.Time
probeNextSeq circular.Number
statistics ReceiveStats
rate struct {
last time.Time
period time.Duration
last uint64 // microseconds
period uint64
packets uint64
prevPackets uint64
bytes uint64
prevBytes uint64
packets uint64
bytes uint64
bytesRetrans uint64
pps uint32
bps uint32
packetsPerSecond float64
bytesPerSecond float64
pktLossRate float64
}
sendACK func(seq circular.Number, light bool)
@ -327,8 +378,8 @@ func NewLiveReceive(config ReceiveConfig) Receiver {
r.deliver = func(p packet.Packet) {}
}
r.rate.last = time.Now()
r.rate.period = time.Second
r.rate.last = 0
r.rate.period = uint64(time.Second.Microseconds())
return r
}
@ -338,34 +389,20 @@ func (r *liveReceive) Stats() ReceiveStats {
defer r.lock.RUnlock()
r.statistics.BytePayload = uint64(r.avgPayloadSize)
r.statistics.MbpsEstimatedRecvBandwidth = r.rate.bytesPerSecond * 8 / 1024 / 1024
r.statistics.MbpsEstimatedLinkCapacity = r.avgLinkCapacity * packet.MAX_PAYLOAD_SIZE * 8 / 1024 / 1024
r.statistics.PktLossRate = r.rate.pktLossRate
return r.statistics
}
func (r *liveReceive) PacketRate() (pps, bps uint32) {
func (r *liveReceive) PacketRate() (pps, bps, capacity float64) {
r.lock.Lock()
defer r.lock.Unlock()
tdiff := time.Since(r.rate.last)
if tdiff < r.rate.period {
pps = r.rate.pps
bps = r.rate.bps
return
}
pdiff := r.rate.packets - r.rate.prevPackets
bdiff := r.rate.bytes - r.rate.prevBytes
r.rate.pps = uint32(float64(pdiff) / tdiff.Seconds())
r.rate.bps = uint32(float64(bdiff) / tdiff.Seconds())
r.rate.prevPackets, r.rate.prevBytes = r.rate.packets, r.rate.bytes
r.rate.last = time.Now()
pps = r.rate.pps
bps = r.rate.bps
pps = r.rate.packetsPerSecond
bps = r.rate.bytesPerSecond
capacity = r.avgLinkCapacity
return
}
@ -385,6 +422,28 @@ func (r *liveReceive) Push(pkt packet.Packet) {
return
}
// This is not really well (not at all) described in the specs. See core.cpp and window.h
// and search for PUMASK_SEQNO_PROBE (0xF). Every 16th and 17th packet are
// sent in pairs. This is used as a probe for the theoretical capacity of the link.
if !pkt.Header().RetransmittedPacketFlag {
probe := pkt.Header().PacketSequenceNumber.Val() & 0xF
if probe == 0 {
r.probeTime = time.Now()
r.probeNextSeq = pkt.Header().PacketSequenceNumber.Inc()
} else if probe == 1 && pkt.Header().PacketSequenceNumber.Equals(r.probeNextSeq) && !r.probeTime.IsZero() && pkt.Len() != 0 {
// The time between packets scaled to a fully loaded packet
diff := float64(time.Since(r.probeTime).Microseconds()) * (packet.MAX_PAYLOAD_SIZE / float64(pkt.Len()))
if diff != 0 {
// Here we're doing an average of the measurements.
r.avgLinkCapacity = 0.875*r.avgLinkCapacity + 0.125*1_000_000/diff
}
} else {
r.probeTime = time.Time{}
}
} else {
r.probeTime = time.Time{}
}
r.nPackets++
pktLen := pkt.Len()
@ -392,13 +451,15 @@ func (r *liveReceive) Push(pkt packet.Packet) {
r.rate.packets++
r.rate.bytes += pktLen
r.statistics.PktRecv++
r.statistics.ByteRecv += pktLen
r.statistics.Pkt++
r.statistics.Byte += pktLen
//pkt.PktTsbpdTime = pkt.Timestamp + r.delay
if pkt.Header().RetransmittedPacketFlag {
r.statistics.PktRcvRetrans++
r.statistics.ByteRcvRetrans += pktLen
r.statistics.PktRetrans++
r.statistics.ByteRetrans += pktLen
r.rate.bytesRetrans += pktLen
}
// 5.1.2. SRT's Default LiveCC Algorithm
@ -406,16 +467,19 @@ func (r *liveReceive) Push(pkt packet.Packet) {
if pkt.Header().PacketSequenceNumber.Lte(r.lastDeliveredSequenceNumber) {
// too old, because up until r.lastDeliveredSequenceNumber, we already delivered
r.statistics.PktRcvDrop++
r.statistics.ByteRcvDrop += pktLen
r.statistics.PktBelated++
r.statistics.ByteBelated += pktLen
r.statistics.PktDrop++
r.statistics.ByteDrop += pktLen
return
}
if pkt.Header().PacketSequenceNumber.Lt(r.lastACKSequenceNumber) {
// already acknowledged, ignoring
r.statistics.PktRcvDrop++
r.statistics.ByteRcvDrop += pktLen
r.statistics.PktDrop++
r.statistics.ByteDrop += pktLen
return
}
@ -430,17 +494,17 @@ func (r *liveReceive) Push(pkt packet.Packet) {
if p.Header().PacketSequenceNumber == pkt.Header().PacketSequenceNumber {
// already received (has been sent more than once), ignoring
r.statistics.PktRcvDrop++
r.statistics.ByteRcvDrop += pktLen
r.statistics.PktDrop++
r.statistics.ByteDrop += pktLen
break
} else if p.Header().PacketSequenceNumber.Gt(pkt.Header().PacketSequenceNumber) {
// late arrival, this fills a gap
r.statistics.PktRcvBuf++
r.statistics.PktRecvUnique++
r.statistics.PktBuf++
r.statistics.PktUnique++
r.statistics.ByteRcvBuf += pktLen
r.statistics.ByteRecvUnique += pktLen
r.statistics.ByteBuf += pktLen
r.statistics.ByteUnique += pktLen
r.packetList.InsertBefore(pkt, e)
@ -455,17 +519,17 @@ func (r *liveReceive) Push(pkt packet.Packet) {
r.sendNAK(r.maxSeenSequenceNumber.Inc(), pkt.Header().PacketSequenceNumber.Dec())
len := uint64(pkt.Header().PacketSequenceNumber.Distance(r.maxSeenSequenceNumber))
r.statistics.PktRcvLoss += len
r.statistics.ByteRcvLoss += len * uint64(r.avgPayloadSize)
r.statistics.PktLoss += len
r.statistics.ByteLoss += len * uint64(r.avgPayloadSize)
r.maxSeenSequenceNumber = pkt.Header().PacketSequenceNumber
}
r.statistics.PktRcvBuf++
r.statistics.PktRecvUnique++
r.statistics.PktBuf++
r.statistics.PktUnique++
r.statistics.ByteRcvBuf += pktLen
r.statistics.ByteRecvUnique += pktLen
r.statistics.ByteBuf += pktLen
r.statistics.ByteUnique += pktLen
r.packetList.PushBack(pkt)
}
@ -521,7 +585,7 @@ func (r *liveReceive) periodicACK(now uint64) (ok bool, sequenceNumber circular.
r.lastPeriodicACK = now
r.nPackets = 0
r.statistics.MsRcvBuf = (maxPktTsbpdTime - minPktTsbpdTime) / 1_000
r.statistics.MsBuf = (maxPktTsbpdTime - minPktTsbpdTime) / 1_000
return
}
@ -572,15 +636,13 @@ func (r *liveReceive) Tick(now uint64) {
// deliver packets whose PktTsbpdTime is ripe
r.lock.Lock()
defer r.lock.Unlock()
removeList := make([]*list.Element, 0, r.packetList.Len())
for e := r.packetList.Front(); e != nil; e = e.Next() {
p := e.Value.(packet.Packet)
if p.Header().PacketSequenceNumber.Lte(r.lastACKSequenceNumber) && p.Header().PktTsbpdTime <= now {
r.statistics.PktRcvBuf--
r.statistics.ByteRcvBuf -= p.Len()
r.statistics.PktBuf--
r.statistics.ByteBuf -= p.Len()
r.lastDeliveredSequenceNumber = p.Header().PacketSequenceNumber
@ -594,6 +656,27 @@ func (r *liveReceive) Tick(now uint64) {
for _, e := range removeList {
r.packetList.Remove(e)
}
r.lock.Unlock()
r.lock.Lock()
tdiff := now - r.rate.last // microseconds
if tdiff > r.rate.period {
r.rate.packetsPerSecond = float64(r.rate.packets) / (float64(tdiff) / 1000 / 1000)
r.rate.bytesPerSecond = float64(r.rate.bytes) / (float64(tdiff) / 1000 / 1000)
if r.rate.bytes != 0 {
r.rate.pktLossRate = float64(r.rate.bytesRetrans) / float64(r.rate.bytes) * 100
} else {
r.rate.bytes = 0
}
r.rate.packets = 0
r.rate.bytes = 0
r.rate.bytesRetrans = 0
r.rate.last = now
}
r.lock.Unlock()
}
func (r *liveReceive) SetNAKInterval(nakInterval uint64) {
@ -630,7 +713,6 @@ type fakeLiveReceive struct {
periodicNAKInterval uint64 // config
lastPeriodicACK uint64
lastPeriodicNAK uint64
avgPayloadSize float64 // bytes
@ -638,13 +720,11 @@ type fakeLiveReceive struct {
last time.Time
period time.Duration
packets uint64
prevPackets uint64
bytes uint64
prevBytes uint64
packets uint64
bytes uint64
pps uint32
bps uint32
pps float64
bps float64
}
sendACK func(seq circular.Number, light bool)
@ -689,7 +769,7 @@ func NewFakeLiveReceive(config ReceiveConfig) Receiver {
}
func (r *fakeLiveReceive) Stats() ReceiveStats { return ReceiveStats{} }
func (r *fakeLiveReceive) PacketRate() (pps, bps uint32) {
func (r *fakeLiveReceive) PacketRate() (pps, bps, capacity float64) {
r.lock.Lock()
defer r.lock.Unlock()
@ -702,13 +782,10 @@ func (r *fakeLiveReceive) PacketRate() (pps, bps uint32) {
return
}
pdiff := r.rate.packets - r.rate.prevPackets
bdiff := r.rate.bytes - r.rate.prevBytes
r.rate.pps = float64(r.rate.packets) / tdiff.Seconds()
r.rate.bps = float64(r.rate.bytes) / tdiff.Seconds()
r.rate.pps = uint32(float64(pdiff) / tdiff.Seconds())
r.rate.bps = uint32(float64(bdiff) / tdiff.Seconds())
r.rate.prevPackets, r.rate.prevBytes = r.rate.packets, r.rate.bytes
r.rate.packets, r.rate.bytes = 0, 0
r.rate.last = time.Now()
pps = r.rate.pps

View File

@ -18,6 +18,7 @@ import (
const MAX_SEQUENCENUMBER uint32 = 0b01111111_11111111_11111111_11111111
const MAX_TIMESTAMP uint32 = 0b11111111_11111111_11111111_11111111
const MAX_PAYLOAD_SIZE = 1456
// Table 1: SRT Control Packet Types
const (
@ -279,7 +280,7 @@ func (p *pkt) Decommission() {
func (p pkt) String() string {
var b strings.Builder
fmt.Fprintf(&b, "timestamp=%#08x, destId=%#08x\n", p.header.Timestamp, p.header.DestinationSocketId)
fmt.Fprintf(&b, "timestamp=%#08x (%d), destId=%#08x\n", p.header.Timestamp, p.header.Timestamp, p.header.DestinationSocketId)
if p.header.IsControlPacket {
fmt.Fprintf(&b, "control packet:\n")

View File

@ -312,15 +312,16 @@ func (ln *listener) Accept(acceptFn AcceptFunc) (Conn, ConnType, error) {
// Create a new socket ID
socketId := uint32(time.Since(ln.start).Microseconds())
// Select the largest TSBPD delay advertised by the caller, but at
// least 120ms
tsbpdDelay := uint16(120)
if request.handshake.RecvTSBPDDelay > tsbpdDelay {
tsbpdDelay = request.handshake.RecvTSBPDDelay
// Select the largest TSBPD delay advertised by the listener, but at least 120ms
recvTsbpdDelay := uint16(ln.config.ReceiverLatency.Milliseconds())
sendTsbpdDelay := uint16(ln.config.PeerLatency.Milliseconds())
if request.handshake.SendTSBPDDelay > recvTsbpdDelay {
recvTsbpdDelay = request.handshake.SendTSBPDDelay
}
if request.handshake.SendTSBPDDelay > tsbpdDelay {
tsbpdDelay = request.handshake.SendTSBPDDelay
if request.handshake.RecvTSBPDDelay > sendTsbpdDelay {
sendTsbpdDelay = request.handshake.RecvTSBPDDelay
}
ln.config.StreamId = request.handshake.StreamId
@ -335,7 +336,8 @@ func (ln *listener) Accept(acceptFn AcceptFunc) (Conn, ConnType, error) {
socketId: socketId,
peerSocketId: request.handshake.SRTSocketId,
tsbpdTimeBase: uint64(request.timestamp),
tsbpdDelay: uint64(tsbpdDelay) * 1000,
tsbpdDelay: uint64(recvTsbpdDelay) * 1000,
peerTsbpdDelay: uint64(sendTsbpdDelay) * 1000,
initialPacketSequenceNumber: request.handshake.InitialPacketSequenceNumber,
crypto: request.crypto,
keyBaseEncryption: packet.EvenKeyEncrypted,
@ -359,6 +361,8 @@ func (ln *listener) Accept(acceptFn AcceptFunc) (Conn, ConnType, error) {
request.handshake.SRTFlags.REXMITFLG = true
request.handshake.SRTFlags.STREAM = false
request.handshake.SRTFlags.PACKET_FILTER = false
request.handshake.RecvTSBPDDelay = recvTsbpdDelay
request.handshake.SendTSBPDDelay = sendTsbpdDelay
ln.accept(request)

View File

@ -7,55 +7,111 @@ type Statistics struct {
MsTimeStamp uint64 // The time elapsed, in milliseconds, since the SRT socket has been created
// Accumulated
Accumulated StatisticsAccumulated
PktSent uint64 // The total number of sent DATA packets, including retransmitted packets
PktRecv uint64 // The total number of received DATA packets, including retransmitted packets
PktSentUnique uint64 // The total number of unique DATA packets sent by the SRT sender
PktRecvUnique uint64 // The total number of unique original, retransmitted or recovered by the packet filter DATA packets received in time, decrypted without errors and, as a result, scheduled for delivery to the upstream application by the SRT receiver.
PktSndLoss uint64 // The total number of data packets considered or reported as lost at the sender side. Does not correspond to the packets detected as lost at the receiver side.
PktRcvLoss uint64 // The total number of SRT DATA packets detected as presently missing (either reordered or lost) at the receiver side
PktRetrans uint64 // The total number of retransmitted packets sent by the SRT sender
PktRcvRetrans uint64 // The total number of retransmitted packets registered at the receiver side
PktSentACK uint64 // The total number of sent ACK (Acknowledgement) control packets
PktRecvACK uint64 // The total number of received ACK (Acknowledgement) control packets
PktSentNAK uint64 // The total number of sent NAK (Negative Acknowledgement) control packets
PktRecvNAK uint64 // The total number of received NAK (Negative Acknowledgement) control packets
PktSentKM uint64 // The total number of sent KM (Key Material) control packets
PktRecvKM uint64 // The total number of received KM (Key Material) control packets
UsSndDuration uint64 // The total accumulated time in microseconds, during which the SRT sender has some data to transmit, including packets that have been sent, but not yet acknowledged
PktSndDrop uint64 // The total number of dropped by the SRT sender DATA packets that have no chance to be delivered in time
PktRcvDrop uint64 // The total number of dropped by the SRT receiver and, as a result, not delivered to the upstream application DATA packets
PktRcvUndecrypt uint64 // The total number of packets that failed to be decrypted at the receiver side
ByteSent uint64 // Same as pktSent, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecv uint64 // Same as pktRecv, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteSentUnique uint64 // Same as pktSentUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecvUnique uint64 // Same as pktRecvUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRcvLoss uint64 // Same as pktRcvLoss, but expressed in bytes, including payload and all the headers (IP, TCP, SRT), bytes for the presently missing (either reordered or lost) packets' payloads are estimated based on the average packet size
ByteRetrans uint64 // Same as pktRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteSndDrop uint64 // Same as pktSndDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRcvDrop uint64 // Same as pktRcvDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRcvUndecrypt uint64 // Same as pktRcvUndecrypt, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
// Interval
Interval StatisticsInterval
// Instantaneous
UsPktSndPeriod float64 // Current minimum time interval between which consecutive packets are sent, in microseconds
PktFlowWindow uint64 // The maximum number of packets that can be "in flight"
PktFlightSize uint64 // The number of packets in flight
MsRTT float64 // Smoothed round-trip time (SRTT), an exponentially-weighted moving average (EWMA) of an endpoint's RTT samples, in milliseconds
MbpsBandwidth float64 // Estimated bandwidth of the network link, in Mbps
ByteAvailSndBuf uint64 // The available space in the sender's buffer, in bytes
ByteAvailRcvBuf uint64 // The available space in the receiver's buffer, in bytes
MbpsMaxBW float64 // Transmission bandwidth limit, in Mbps
ByteMSS uint64 // Maximum Segment Size (MSS), in bytes
PktSndBuf uint64 // The number of packets in the sender's buffer that are already scheduled for sending or even possibly sent, but not yet acknowledged
ByteSndBuf uint64 // Instantaneous (current) value of pktSndBuf, but expressed in bytes, including payload and all headers (IP, TCP, SRT)
MsSndBuf uint64 // The timespan (msec) of packets in the sender's buffer (unacknowledged packets)
MsSndTsbPdDelay uint64 // Timestamp-based Packet Delivery Delay value of the peer
PktRcvBuf uint64 // The number of acknowledged packets in receiver's buffer
ByteRcvBuf uint64 // Instantaneous (current) value of pktRcvBuf, expressed in bytes, including payload and all headers (IP, TCP, SRT)
MsRcvBuf uint64 // The timespan (msec) of acknowledged packets in the receiver's buffer
MsRcvTsbPdDelay uint64 // Timestamp-based Packet Delivery Delay value set on the socket via SRTO_RCVLATENCY or SRTO_LATENCY
PktReorderTolerance uint64 // Instant value of the packet reorder tolerance
PktRcvAvgBelatedTime uint64 // Accumulated difference between the current time and the time-to-play of a packet that is received late
Instantaneous StatisticsInstantaneous
}
type StatisticsAccumulated struct {
PktSent uint64 // The total number of sent DATA packets, including retransmitted packets
PktRecv uint64 // The total number of received DATA packets, including retransmitted packets
PktSentUnique uint64 // The total number of unique DATA packets sent by the SRT sender
PktRecvUnique uint64 // The total number of unique original, retransmitted or recovered by the packet filter DATA packets received in time, decrypted without errors and, as a result, scheduled for delivery to the upstream application by the SRT receiver.
PktSendLoss uint64 // The total number of data packets considered or reported as lost at the sender side. Does not correspond to the packets detected as lost at the receiver side.
PktRecvLoss uint64 // The total number of SRT DATA packets detected as presently missing (either reordered or lost) at the receiver side
PktRetrans uint64 // The total number of retransmitted packets sent by the SRT sender
PktRecvRetrans uint64 // The total number of retransmitted packets registered at the receiver side
PktSentACK uint64 // The total number of sent ACK (Acknowledgement) control packets
PktRecvACK uint64 // The total number of received ACK (Acknowledgement) control packets
PktSentNAK uint64 // The total number of sent NAK (Negative Acknowledgement) control packets
PktRecvNAK uint64 // The total number of received NAK (Negative Acknowledgement) control packets
PktSentKM uint64 // The total number of sent KM (Key Material) control packets
PktRecvKM uint64 // The total number of received KM (Key Material) control packets
UsSndDuration uint64 // The total accumulated time in microseconds, during which the SRT sender has some data to transmit, including packets that have been sent, but not yet acknowledged
PktRecvBelated uint64
PktSendDrop uint64 // The total number of dropped by the SRT sender DATA packets that have no chance to be delivered in time
PktRecvDrop uint64 // The total number of dropped by the SRT receiver and, as a result, not delivered to the upstream application DATA packets
PktRecvUndecrypt uint64 // The total number of packets that failed to be decrypted at the receiver side
ByteSent uint64 // Same as pktSent, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecv uint64 // Same as pktRecv, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteSentUnique uint64 // Same as pktSentUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecvUnique uint64 // Same as pktRecvUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecvLoss uint64 // Same as pktRecvLoss, but expressed in bytes, including payload and all the headers (IP, TCP, SRT), bytes for the presently missing (either reordered or lost) packets' payloads are estimated based on the average packet size
ByteRetrans uint64 // Same as pktRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecvRetrans uint64 // Same as pktRecvRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecvBelated uint64
ByteSendDrop uint64 // Same as pktSendDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecvDrop uint64 // Same as pktRecvDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecvUndecrypt uint64 // Same as pktRecvUndecrypt, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
}
type StatisticsInterval struct {
MsInterval uint64 // Length of the interval, in milliseconds
PktSent uint64 // Number of sent DATA packets, including retransmitted packets
PktRecv uint64 // Number of received DATA packets, including retransmitted packets
PktSentUnique uint64 // Number of unique DATA packets sent by the SRT sender
PktRecvUnique uint64 // Number of unique original, retransmitted or recovered by the packet filter DATA packets received in time, decrypted without errors and, as a result, scheduled for delivery to the upstream application by the SRT receiver.
PktSendLoss uint64 // Number of data packets considered or reported as lost at the sender side. Does not correspond to the packets detected as lost at the receiver side.
PktRecvLoss uint64 // Number of SRT DATA packets detected as presently missing (either reordered or lost) at the receiver side
PktRetrans uint64 // Number of retransmitted packets sent by the SRT sender
PktRecvRetrans uint64 // Number of retransmitted packets registered at the receiver side
PktSentACK uint64 // Number of sent ACK (Acknowledgement) control packets
PktRecvACK uint64 // Number of received ACK (Acknowledgement) control packets
PktSentNAK uint64 // Number of sent NAK (Negative Acknowledgement) control packets
PktRecvNAK uint64 // Number of received NAK (Negative Acknowledgement) control packets
MbpsSendRate float64 // Sending rate, in Mbps
MbpsRecvRate float64 // Receiving rate, in Mbps
UsSndDuration uint64 // Accumulated time in microseconds, during which the SRT sender has some data to transmit, including packets that have been sent, but not yet acknowledged
PktReorderDistance uint64
PktRecvBelated uint64 // Number of packets that arrive too late
PktSndDrop uint64 // Number of dropped by the SRT sender DATA packets that have no chance to be delivered in time
PktRecvDrop uint64 // Number of dropped by the SRT receiver and, as a result, not delivered to the upstream application DATA packets
PktRecvUndecrypt uint64 // Number of packets that failed to be decrypted at the receiver side
ByteSent uint64 // Same as pktSent, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecv uint64 // Same as pktRecv, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteSentUnique uint64 // Same as pktSentUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecvUnique uint64 // Same as pktRecvUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecvLoss uint64 // Same as pktRecvLoss, but expressed in bytes, including payload and all the headers (IP, TCP, SRT), bytes for the presently missing (either reordered or lost) packets' payloads are estimated based on the average packet size
ByteRetrans uint64 // Same as pktRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecvRetrans uint64 // Same as pktRecvRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecvBelated uint64 // Same as pktRecvBelated, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteSendDrop uint64 // Same as pktSendDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecvDrop uint64 // Same as pktRecvDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecvUndecrypt uint64 // Same as pktRecvUndecrypt, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
}
type StatisticsInstantaneous struct {
UsPktSendPeriod float64 // Current minimum time interval between which consecutive packets are sent, in microseconds
PktFlowWindow uint64 // The maximum number of packets that can be "in flight"
PktFlightSize uint64 // The number of packets in flight
MsRTT float64 // Smoothed round-trip time (SRTT), an exponentially-weighted moving average (EWMA) of an endpoint's RTT samples, in milliseconds
MbpsSentRate float64 // Current transmission bandwidth, in Mbps
MbpsRecvRate float64 // Current receiving bandwidth, in Mbps
MbpsLinkCapacity float64 // Estimated capacity of the network link, in Mbps
ByteAvailSendBuf uint64 // The available space in the sender's buffer, in bytes
ByteAvailRecvBuf uint64 // The available space in the receiver's buffer, in bytes
MbpsMaxBW float64 // Transmission bandwidth limit, in Mbps
ByteMSS uint64 // Maximum Segment Size (MSS), in bytes
PktSendBuf uint64 // The number of packets in the sender's buffer that are already scheduled for sending or even possibly sent, but not yet acknowledged
ByteSendBuf uint64 // Instantaneous (current) value of pktSndBuf, but expressed in bytes, including payload and all headers (IP, TCP, SRT)
MsSendBuf uint64 // The timespan (msec) of packets in the sender's buffer (unacknowledged packets)
MsSendTsbPdDelay uint64 // Timestamp-based Packet Delivery Delay value of the peer
PktRecvBuf uint64 // The number of acknowledged packets in receiver's buffer
ByteRecvBuf uint64 // Instantaneous (current) value of pktRcvBuf, expressed in bytes, including payload and all headers (IP, TCP, SRT)
MsRecvBuf uint64 // The timespan (msec) of acknowledged packets in the receiver's buffer
MsRecvTsbPdDelay uint64 // Timestamp-based Packet Delivery Delay value set on the socket via SRTO_RCVLATENCY or SRTO_LATENCY
PktReorderTolerance uint64 // Instant value of the packet reorder tolerance
PktRecvAvgBelatedTime uint64 // Accumulated difference between the current time and the time-to-play of a packet that is received late
PktSendLossRate float64 // Percentage of resent data vs. sent data
PktRecvLossRate float64 // Percentage of retransmitted data vs. received data
}

View File

@ -1,7 +1,7 @@
Package validator
=================
<img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
![Project status](https://img.shields.io/badge/version-10.11.0-green.svg)
![Project status](https://img.shields.io/badge/version-10.11.1-green.svg)
[![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator)
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)

View File

@ -1484,10 +1484,15 @@ func isAlphaUnicode(fl FieldLevel) bool {
return alphaUnicodeRegex.MatchString(fl.Field().String())
}
// isBoolean is the validation function for validating if the current field's value can be safely converted to a boolean.
// isBoolean is the validation function for validating if the current field's value is a valid boolean value or can be safely converted to a boolean value.
func isBoolean(fl FieldLevel) bool {
_, err := strconv.ParseBool(fl.Field().String())
return err == nil
switch fl.Field().Kind() {
case reflect.Bool:
return true
default:
_, err := strconv.ParseBool(fl.Field().String())
return err == nil
}
}
// isDefault is the opposite of required aka hasValue

View File

@ -132,6 +132,127 @@ func main() {
}
```
## commandline
Download as binary from: https://github.com/klauspost/cpuid/releases
Install from source:
`go install github.com/klauspost/cpuid/v2/cmd/cpuid@latest`
### Example
```
λ cpuid
Name: AMD Ryzen 9 3950X 16-Core Processor
Vendor String: AuthenticAMD
Vendor ID: AMD
PhysicalCores: 16
Threads Per Core: 2
Logical Cores: 32
CPU Family 23 Model: 113
Features: ADX,AESNI,AVX,AVX2,BMI1,BMI2,CLMUL,CLZERO,CMOV,CMPXCHG8,CPBOOST,CX16,F16C,FMA3,FXSR,FXSROPT,HTT,HYPERVISOR,LAHF,LZCNT,MCAOVERFLOW,MMX,MMXEXT,MOVBE,NX,OSXSAVE,POPCNT,RDRAND,RDSEED,RDTSCP,SCE,SHA,SSE,SSE2,SSE3,SSE4,SSE42,SSE4A,SSSE3,SUCCOR,X87,XSAVE
Microarchitecture level: 3
Cacheline bytes: 64
L1 Instruction Cache: 32768 bytes
L1 Data Cache: 32768 bytes
L2 Cache: 524288 bytes
L3 Cache: 16777216 bytes
```
### JSON Output:
```
λ cpuid --json
{
"BrandName": "AMD Ryzen 9 3950X 16-Core Processor",
"VendorID": 2,
"VendorString": "AuthenticAMD",
"PhysicalCores": 16,
"ThreadsPerCore": 2,
"LogicalCores": 32,
"Family": 23,
"Model": 113,
"CacheLine": 64,
"Hz": 0,
"BoostFreq": 0,
"Cache": {
"L1I": 32768,
"L1D": 32768,
"L2": 524288,
"L3": 16777216
},
"SGX": {
"Available": false,
"LaunchControl": false,
"SGX1Supported": false,
"SGX2Supported": false,
"MaxEnclaveSizeNot64": 0,
"MaxEnclaveSize64": 0,
"EPCSections": null
},
"Features": [
"ADX",
"AESNI",
"AVX",
"AVX2",
"BMI1",
"BMI2",
"CLMUL",
"CLZERO",
"CMOV",
"CMPXCHG8",
"CPBOOST",
"CX16",
"F16C",
"FMA3",
"FXSR",
"FXSROPT",
"HTT",
"HYPERVISOR",
"LAHF",
"LZCNT",
"MCAOVERFLOW",
"MMX",
"MMXEXT",
"MOVBE",
"NX",
"OSXSAVE",
"POPCNT",
"RDRAND",
"RDSEED",
"RDTSCP",
"SCE",
"SHA",
"SSE",
"SSE2",
"SSE3",
"SSE4",
"SSE42",
"SSE4A",
"SSSE3",
"SUCCOR",
"X87",
"XSAVE"
],
"X64Level": 3
}
```
### Check CPU microarch level
```
λ cpuid --check-level=3
2022/03/18 17:04:40 AMD Ryzen 9 3950X 16-Core Processor
2022/03/18 17:04:40 Microarchitecture level 3 is supported. Max level is 3.
Exit Code 0
λ cpuid --check-level=4
2022/03/18 17:06:18 AMD Ryzen 9 3950X 16-Core Processor
2022/03/18 17:06:18 Microarchitecture level 4 not supported. Max level is 3.
Exit Code 1
```
# license
This code is published under an MIT license. See LICENSE file for more information.

View File

@ -14,6 +14,7 @@ import (
"flag"
"fmt"
"math"
"math/bits"
"os"
"runtime"
"strings"
@ -92,7 +93,8 @@ const (
AVX512VNNI // AVX-512 Vector Neural Network Instructions
AVX512VP2INTERSECT // AVX-512 Intersect for D/Q
AVX512VPOPCNTDQ // AVX-512 Vector Population Count Doubleword and Quadword
AVXSLOW // Indicates the CPU performs 2 128 bit operations instead of one.
AVXSLOW // Indicates the CPU performs 2 128 bit operations instead of one
AVXVNNI // AVX (VEX encoded) VNNI neural network instructions
BMI1 // Bit Manipulation Instruction Set 1
BMI2 // Bit Manipulation Instruction Set 2
CETIBT // Intel CET Indirect Branch Tracking
@ -101,21 +103,28 @@ const (
CLMUL // Carry-less Multiplication
CLZERO // CLZERO instruction supported
CMOV // i686 CMOV
CMPSB_SCADBS_SHORT // Fast short CMPSB and SCASB
CMPXCHG8 // CMPXCHG8 instruction
CPBOOST // Core Performance Boost
CX16 // CMPXCHG16B Instruction
ENQCMD // Enqueue Command
ERMS // Enhanced REP MOVSB/STOSB
F16C // Half-precision floating-point conversion
FLUSH_L1D // Flush L1D cache
FMA3 // Intel FMA 3. Does not imply AVX.
FMA4 // Bulldozer FMA4 functions
FSRM // Fast Short Rep Mov
FXSR // FXSAVE, FXRESTOR instructions, CR4 bit 9
FXSROPT // FXSAVE/FXRSTOR optimizations
GFNI // Galois Field New Instructions
GFNI // Galois Field New Instructions. May require other features (AVX, AVX512VL,AVX512F) based on usage.
HLE // Hardware Lock Elision
HRESET // If set CPU supports history reset and the IA32_HRESET_ENABLE MSR
HTT // Hyperthreading (enabled)
HWA // Hardware assert supported. Indicates support for MSRC001_10
HYBRID_CPU // This part has CPUs of more than one type.
HYPERVISOR // This bit has been reserved by Intel & AMD for use by hypervisors
IA32_ARCH_CAP // IA32_ARCH_CAPABILITIES MSR (Intel)
IA32_CORE_CAP // IA32_CORE_CAPABILITIES MSR
IBPB // Indirect Branch Restricted Speculation (IBRS) and Indirect Branch Predictor Barrier (IBPB)
IBS // Instruction Based Sampling (AMD)
IBSBRNTRGT // Instruction Based Sampling Feature (AMD)
@ -126,21 +135,30 @@ const (
IBSOPSAM // Instruction Based Sampling Feature (AMD)
IBSRDWROPCNT // Instruction Based Sampling Feature (AMD)
IBSRIPINVALIDCHK // Instruction Based Sampling Feature (AMD)
IBS_PREVENTHOST // Disallowing IBS use by the host supported
INT_WBINVD // WBINVD/WBNOINVD are interruptible.
INVLPGB // NVLPGB and TLBSYNC instruction supported
LAHF // LAHF/SAHF in long mode
LAM // If set, CPU supports Linear Address Masking
LBRVIRT // LBR virtualization
LZCNT // LZCNT instruction
MCAOVERFLOW // MCA overflow recovery support.
MCDT_NO // Processor do not exhibit MXCSR Configuration Dependent Timing behavior and do not need to mitigate it.
MCOMMIT // MCOMMIT instruction supported
MD_CLEAR // VERW clears CPU buffers
MMX // standard MMX
MMXEXT // SSE integer functions or AMD MMX ext
MOVBE // MOVBE instruction (big-endian)
MOVDIR64B // Move 64 Bytes as Direct Store
MOVDIRI // Move Doubleword as Direct Store
MOVSB_ZL // Fast Zero-Length MOVSB
MPX // Intel MPX (Memory Protection Extensions)
MSRIRC // Instruction Retired Counter MSR available
MSR_PAGEFLUSH // Page Flush MSR available
NRIPS // Indicates support for NRIP save on VMEXIT
NX // NX (No-Execute) bit
OSXSAVE // XSAVE enabled by OS
PCONFIG // PCONFIG for Intel Multi-Key Total Memory Encryption
POPCNT // POPCNT instruction
RDPRU // RDPRU instruction supported
RDRAND // RDRAND instruction is available
@ -148,11 +166,21 @@ const (
RDTSCP // RDTSCP Instruction
RTM // Restricted Transactional Memory
RTM_ALWAYS_ABORT // Indicates that the loaded microcode is forcing RTM abort.
SCE // SYSENTER and SYSEXIT instructions
SERIALIZE // Serialize Instruction Execution
SEV // AMD Secure Encrypted Virtualization supported
SEV_64BIT // AMD SEV guest execution only allowed from a 64-bit host
SEV_ALTERNATIVE // AMD SEV Alternate Injection supported
SEV_DEBUGSWAP // Full debug state swap supported for SEV-ES guests
SEV_ES // AMD SEV Encrypted State supported
SEV_RESTRICTED // AMD SEV Restricted Injection supported
SEV_SNP // AMD SEV Secure Nested Paging supported
SGX // Software Guard Extensions
SGXLC // Software Guard Extensions Launch Control
SHA // Intel SHA Extensions
SME // AMD Secure Memory Encryption supported
SME_COHERENT // AMD Hardware cache coherency across encryption domains enforced
SPEC_CTRL_SSBD // Speculative Store Bypass Disable
SRBDS_CTRL // SRBDS mitigation MSR available
SSE // SSE functions
SSE2 // P4 SSE functions
SSE3 // Prescott SSE3 functions
@ -161,17 +189,38 @@ const (
SSE4A // AMD Barcelona microarchitecture SSE4a instructions
SSSE3 // Conroe SSSE3 functions
STIBP // Single Thread Indirect Branch Predictors
STOSB_SHORT // Fast short STOSB
SUCCOR // Software uncorrectable error containment and recovery capability.
SVM // AMD Secure Virtual Machine
SVMDA // Indicates support for the SVM decode assists.
SVMFBASID // SVM, Indicates that TLB flush events, including CR3 writes and CR4.PGE toggles, flush only the current ASID's TLB entries. Also indicates support for the extended VMCBTLB_Control
SVML // AMD SVM lock. Indicates support for SVM-Lock.
SVMNP // AMD SVM nested paging
SVMPF // SVM pause intercept filter. Indicates support for the pause intercept filter
SVMPFT // SVM PAUSE filter threshold. Indicates support for the PAUSE filter cycle count threshold
SYSCALL // System-Call Extension (SCE): SYSCALL and SYSRET instructions.
SYSEE // SYSENTER and SYSEXIT instructions
TBM // AMD Trailing Bit Manipulation
TME // Intel Total Memory Encryption. The following MSRs are supported: IA32_TME_CAPABILITY, IA32_TME_ACTIVATE, IA32_TME_EXCLUDE_MASK, and IA32_TME_EXCLUDE_BASE.
TOPEXT // TopologyExtensions: topology extensions support. Indicates support for CPUID Fn8000_001D_EAX_x[N:0]-CPUID Fn8000_001E_EDX.
TSCRATEMSR // MSR based TSC rate control. Indicates support for MSR TSC ratio MSRC000_0104
TSXLDTRK // Intel TSX Suspend Load Address Tracking
VAES // Vector AES
VAES // Vector AES. AVX(512) versions requires additional checks.
VMCBCLEAN // VMCB clean bits. Indicates support for VMCB clean bits.
VMPL // AMD VM Permission Levels supported
VMSA_REGPROT // AMD VMSA Register Protection supported
VMX // Virtual Machine Extensions
VPCLMULQDQ // Carry-Less Multiplication Quadword
VPCLMULQDQ // Carry-Less Multiplication Quadword. Requires AVX for 3 register versions.
VTE // AMD Virtual Transparent Encryption supported
WAITPKG // TPAUSE, UMONITOR, UMWAIT
WBNOINVD // Write Back and Do Not Invalidate Cache
X87 // FPU
XGETBV1 // Supports XGETBV with ECX = 1
XOP // Bulldozer XOP functions
XSAVE // XSAVE, XRESTOR, XSETBV, XGETBV
XSAVEC // Supports XSAVEC and the compacted form of XRSTOR.
XSAVEOPT // XSAVEOPT available
XSAVES // Supports XSAVES/XRSTORS and IA32_XSS
// ARM features:
AESARM // AES instructions
@ -198,7 +247,6 @@ const (
SM3 // SM3 instructions
SM4 // SM4 instructions
SVE // Scalable Vector Extension
// Keep it last. It automatically defines the size of []flagSet
lastID
@ -216,6 +264,7 @@ type CPUInfo struct {
LogicalCores int // Number of physical cores times threads that can run on each core through the use of hyperthreading. Will be 0 if undetectable.
Family int // CPU family number
Model int // CPU model number
Stepping int // CPU stepping info
CacheLine int // Cache line size in bytes. Will be 0 if undetectable.
Hz int64 // Clock speed, if known, 0 otherwise. Will attempt to contain base clock speed.
BoostFreq int64 // Max clock speed, if known, 0 otherwise
@ -322,11 +371,21 @@ func (c CPUInfo) Has(id FeatureID) bool {
return c.featureSet.inSet(id)
}
// AnyOf returns whether the CPU supports one or more of the requested features.
func (c CPUInfo) AnyOf(ids ...FeatureID) bool {
for _, id := range ids {
if c.featureSet.inSet(id) {
return true
}
}
return false
}
// https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels
var level1Features = flagSetWith(CMOV, CMPXCHG8, X87, FXSR, MMX, SCE, SSE, SSE2)
var level2Features = flagSetWith(CMOV, CMPXCHG8, X87, FXSR, MMX, SCE, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3)
var level3Features = flagSetWith(CMOV, CMPXCHG8, X87, FXSR, MMX, SCE, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3, AVX, AVX2, BMI1, BMI2, F16C, FMA3, LZCNT, MOVBE, OSXSAVE)
var level4Features = flagSetWith(CMOV, CMPXCHG8, X87, FXSR, MMX, SCE, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3, AVX, AVX2, BMI1, BMI2, F16C, FMA3, LZCNT, MOVBE, OSXSAVE, AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL)
var level1Features = flagSetWith(CMOV, CMPXCHG8, X87, FXSR, MMX, SYSCALL, SSE, SSE2)
var level2Features = flagSetWith(CMOV, CMPXCHG8, X87, FXSR, MMX, SYSCALL, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3)
var level3Features = flagSetWith(CMOV, CMPXCHG8, X87, FXSR, MMX, SYSCALL, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3, AVX, AVX2, BMI1, BMI2, F16C, FMA3, LZCNT, MOVBE, OSXSAVE)
var level4Features = flagSetWith(CMOV, CMPXCHG8, X87, FXSR, MMX, SYSCALL, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3, AVX, AVX2, BMI1, BMI2, F16C, FMA3, LZCNT, MOVBE, OSXSAVE, AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL)
// X64Level returns the microarchitecture level detected on the CPU.
// If features are lacking or non x64 mode, 0 is returned.
@ -369,8 +428,9 @@ func (c CPUInfo) IsVendor(v Vendor) bool {
return c.VendorID == v
}
// FeatureSet returns all available features as strings.
func (c CPUInfo) FeatureSet() []string {
s := make([]string, 0)
s := make([]string, 0, c.featureSet.nEnabled())
s = append(s, c.featureSet.Strings()...)
return s
}
@ -543,6 +603,14 @@ func (s flagSet) hasSet(other flagSet) bool {
return true
}
// nEnabled will return the number of enabled flags.
func (s flagSet) nEnabled() (n int) {
for _, v := range s[:] {
n += bits.OnesCount64(uint64(v))
}
return n
}
func flagSetWith(feat ...FeatureID) flagSet {
var res flagSet
for _, f := range feat {
@ -631,7 +699,7 @@ func threadsPerCore() int {
if vend == AMD {
// Workaround for AMD returning 0, assume 2 if >= Zen 2
// It will be more correct than not.
fam, _ := familyModel()
fam, _, _ := familyModel()
_, _, _, d := cpuid(1)
if (d&(1<<28)) != 0 && fam >= 23 {
return 2
@ -669,14 +737,27 @@ func logicalCores() int {
}
}
func familyModel() (int, int) {
func familyModel() (family, model, stepping int) {
if maxFunctionID() < 0x1 {
return 0, 0
return 0, 0, 0
}
eax, _, _, _ := cpuid(1)
family := ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff)
model := ((eax >> 4) & 0xf) + ((eax >> 12) & 0xf0)
return int(family), int(model)
// If BaseFamily[3:0] is less than Fh then ExtendedFamily[7:0] is reserved and Family is equal to BaseFamily[3:0].
family = int((eax >> 8) & 0xf)
extFam := family == 0x6 // Intel is 0x6, needs extended model.
if family == 0xf {
// Add ExtFamily
family += int((eax >> 20) & 0xff)
extFam = true
}
// If BaseFamily[3:0] is less than 0Fh then ExtendedModel[3:0] is reserved and Model is equal to BaseModel[3:0].
model = int((eax >> 4) & 0xf)
if extFam {
// Add ExtModel
model += int((eax >> 12) & 0xf0)
}
stepping = int(eax & 0xf)
return family, model, stepping
}
func physicalCores() int {
@ -811,9 +892,14 @@ func (c *CPUInfo) cacheSize() {
c.Cache.L2 = int(((ecx >> 16) & 0xFFFF) * 1024)
// CPUID Fn8000_001D_EAX_x[N:0] Cache Properties
if maxExtendedFunction() < 0x8000001D {
if maxExtendedFunction() < 0x8000001D || !c.Has(TOPEXT) {
return
}
// Xen Hypervisor is buggy and returns the same entry no matter ECX value.
// Hack: When we encounter the same entry 100 times we break.
nSame := 0
var last uint32
for i := uint32(0); i < math.MaxUint32; i++ {
eax, ebx, ecx, _ := cpuidex(0x8000001D, i)
@ -829,6 +915,16 @@ func (c *CPUInfo) cacheSize() {
return
}
// Check for the same value repeated.
comb := eax ^ ebx ^ ecx
if comb == last {
nSame++
if nSame == 100 {
return
}
}
last = comb
switch level {
case 1:
switch typ {
@ -913,14 +1009,13 @@ func support() flagSet {
if mfi < 0x1 {
return fs
}
family, model := familyModel()
family, model, _ := familyModel()
_, _, c, d := cpuid(1)
fs.setIf((d&(1<<0)) != 0, X87)
fs.setIf((d&(1<<8)) != 0, CMPXCHG8)
fs.setIf((d&(1<<11)) != 0, SCE)
fs.setIf((d&(1<<11)) != 0, SYSEE)
fs.setIf((d&(1<<15)) != 0, CMOV)
fs.setIf((d&(1<<22)) != 0, MMXEXT)
fs.setIf((d&(1<<23)) != 0, MMX)
fs.setIf((d&(1<<24)) != 0, FXSR)
fs.setIf((d&(1<<25)) != 0, FXSROPT)
@ -928,9 +1023,9 @@ func support() flagSet {
fs.setIf((d&(1<<26)) != 0, SSE2)
fs.setIf((c&1) != 0, SSE3)
fs.setIf((c&(1<<5)) != 0, VMX)
fs.setIf((c&0x00000200) != 0, SSSE3)
fs.setIf((c&0x00080000) != 0, SSE4)
fs.setIf((c&0x00100000) != 0, SSE42)
fs.setIf((c&(1<<9)) != 0, SSSE3)
fs.setIf((c&(1<<19)) != 0, SSE4)
fs.setIf((c&(1<<20)) != 0, SSE42)
fs.setIf((c&(1<<25)) != 0, AESNI)
fs.setIf((c&(1<<1)) != 0, CLMUL)
fs.setIf(c&(1<<22) != 0, MOVBE)
@ -976,7 +1071,6 @@ func support() flagSet {
// Check AVX2, AVX2 requires OS support, but BMI1/2 don't.
if mfi >= 7 {
_, ebx, ecx, edx := cpuidex(7, 0)
eax1, _, _, _ := cpuidex(7, 1)
if fs.inSet(AVX) && (ebx&0x00000020) != 0 {
fs.set(AVX2)
}
@ -993,21 +1087,45 @@ func support() flagSet {
fs.setIf(ebx&(1<<18) != 0, RDSEED)
fs.setIf(ebx&(1<<19) != 0, ADX)
fs.setIf(ebx&(1<<29) != 0, SHA)
// CPUID.(EAX=7, ECX=0).ECX
fs.setIf(ecx&(1<<5) != 0, WAITPKG)
fs.setIf(ecx&(1<<7) != 0, CETSS)
fs.setIf(ecx&(1<<8) != 0, GFNI)
fs.setIf(ecx&(1<<9) != 0, VAES)
fs.setIf(ecx&(1<<10) != 0, VPCLMULQDQ)
fs.setIf(ecx&(1<<13) != 0, TME)
fs.setIf(ecx&(1<<25) != 0, CLDEMOTE)
fs.setIf(ecx&(1<<27) != 0, MOVDIRI)
fs.setIf(ecx&(1<<28) != 0, MOVDIR64B)
fs.setIf(ecx&(1<<29) != 0, ENQCMD)
fs.setIf(ecx&(1<<30) != 0, SGXLC)
// CPUID.(EAX=7, ECX=0).EDX
fs.setIf(edx&(1<<4) != 0, FSRM)
fs.setIf(edx&(1<<9) != 0, SRBDS_CTRL)
fs.setIf(edx&(1<<10) != 0, MD_CLEAR)
fs.setIf(edx&(1<<11) != 0, RTM_ALWAYS_ABORT)
fs.setIf(edx&(1<<14) != 0, SERIALIZE)
fs.setIf(edx&(1<<15) != 0, HYBRID_CPU)
fs.setIf(edx&(1<<16) != 0, TSXLDTRK)
fs.setIf(edx&(1<<18) != 0, PCONFIG)
fs.setIf(edx&(1<<20) != 0, CETIBT)
fs.setIf(edx&(1<<26) != 0, IBPB)
fs.setIf(edx&(1<<27) != 0, STIBP)
fs.setIf(edx&(1<<28) != 0, FLUSH_L1D)
fs.setIf(edx&(1<<29) != 0, IA32_ARCH_CAP)
fs.setIf(edx&(1<<30) != 0, IA32_CORE_CAP)
fs.setIf(edx&(1<<31) != 0, SPEC_CTRL_SSBD)
// CPUID.(EAX=7, ECX=1)
eax1, _, _, _ := cpuidex(7, 1)
fs.setIf(fs.inSet(AVX) && eax1&(1<<4) != 0, AVXVNNI)
fs.setIf(eax1&(1<<10) != 0, MOVSB_ZL)
fs.setIf(eax1&(1<<11) != 0, STOSB_SHORT)
fs.setIf(eax1&(1<<12) != 0, CMPSB_SCADBS_SHORT)
fs.setIf(eax1&(1<<22) != 0, HRESET)
fs.setIf(eax1&(1<<26) != 0, LAM)
// Only detect AVX-512 features if XGETBV is supported
if c&((1<<26)|(1<<27)) == (1<<26)|(1<<27) {
@ -1033,9 +1151,6 @@ func support() flagSet {
// ecx
fs.setIf(ecx&(1<<1) != 0, AVX512VBMI)
fs.setIf(ecx&(1<<6) != 0, AVX512VBMI2)
fs.setIf(ecx&(1<<8) != 0, GFNI)
fs.setIf(ecx&(1<<9) != 0, VAES)
fs.setIf(ecx&(1<<10) != 0, VPCLMULQDQ)
fs.setIf(ecx&(1<<11) != 0, AVX512VNNI)
fs.setIf(ecx&(1<<12) != 0, AVX512BITALG)
fs.setIf(ecx&(1<<14) != 0, AVX512VPOPCNTDQ)
@ -1049,29 +1164,63 @@ func support() flagSet {
fs.setIf(eax1&(1<<5) != 0, AVX512BF16)
}
}
// CPUID.(EAX=7, ECX=2)
_, _, _, edx = cpuidex(7, 2)
fs.setIf(edx&(1<<5) != 0, MCDT_NO)
}
// Processor Extended State Enumeration Sub-leaf (EAX = 0DH, ECX = 1)
// EAX
// Bit 00: XSAVEOPT is available.
// Bit 01: Supports XSAVEC and the compacted form of XRSTOR if set.
// Bit 02: Supports XGETBV with ECX = 1 if set.
// Bit 03: Supports XSAVES/XRSTORS and IA32_XSS if set.
// Bits 31 - 04: Reserved.
// EBX
// Bits 31 - 00: The size in bytes of the XSAVE area containing all states enabled by XCRO | IA32_XSS.
// ECX
// Bits 31 - 00: Reports the supported bits of the lower 32 bits of the IA32_XSS MSR. IA32_XSS[n] can be set to 1 only if ECX[n] is 1.
// EDX?
// Bits 07 - 00: Used for XCR0. Bit 08: PT state. Bit 09: Used for XCR0. Bits 12 - 10: Reserved. Bit 13: HWP state. Bits 31 - 14: Reserved.
if mfi >= 0xd {
if fs.inSet(XSAVE) {
eax, _, _, _ := cpuidex(0xd, 1)
fs.setIf(eax&(1<<0) != 0, XSAVEOPT)
fs.setIf(eax&(1<<1) != 0, XSAVEC)
fs.setIf(eax&(1<<2) != 0, XGETBV1)
fs.setIf(eax&(1<<3) != 0, XSAVES)
}
}
if maxExtendedFunction() >= 0x80000001 {
_, _, c, d := cpuid(0x80000001)
if (c & (1 << 5)) != 0 {
fs.set(LZCNT)
fs.set(POPCNT)
}
// ECX
fs.setIf((c&(1<<0)) != 0, LAHF)
fs.setIf((c&(1<<10)) != 0, IBS)
fs.setIf((d&(1<<31)) != 0, AMD3DNOW)
fs.setIf((d&(1<<30)) != 0, AMD3DNOWEXT)
fs.setIf((d&(1<<23)) != 0, MMX)
fs.setIf((d&(1<<22)) != 0, MMXEXT)
fs.setIf((c&(1<<2)) != 0, SVM)
fs.setIf((c&(1<<6)) != 0, SSE4A)
fs.setIf((c&(1<<10)) != 0, IBS)
fs.setIf((c&(1<<22)) != 0, TOPEXT)
// EDX
fs.setIf(d&(1<<11) != 0, SYSCALL)
fs.setIf(d&(1<<20) != 0, NX)
fs.setIf(d&(1<<22) != 0, MMXEXT)
fs.setIf(d&(1<<23) != 0, MMX)
fs.setIf(d&(1<<24) != 0, FXSR)
fs.setIf(d&(1<<25) != 0, FXSROPT)
fs.setIf(d&(1<<27) != 0, RDTSCP)
fs.setIf(d&(1<<30) != 0, AMD3DNOWEXT)
fs.setIf(d&(1<<31) != 0, AMD3DNOW)
/* XOP and FMA4 use the AVX instruction coding scheme, so they can't be
* used unless the OS has AVX support. */
if fs.inSet(AVX) {
fs.setIf((c&0x00000800) != 0, XOP)
fs.setIf((c&0x00010000) != 0, FMA4)
fs.setIf((c&(1<<11)) != 0, XOP)
fs.setIf((c&(1<<16)) != 0, FMA4)
}
}
@ -1094,6 +1243,20 @@ func support() flagSet {
fs.setIf((b&(1<<0)) != 0, CLZERO)
}
if fs.inSet(SVM) && maxExtendedFunction() >= 0x8000000A {
_, _, _, edx := cpuid(0x8000000A)
fs.setIf((edx>>0)&1 == 1, SVMNP)
fs.setIf((edx>>1)&1 == 1, LBRVIRT)
fs.setIf((edx>>2)&1 == 1, SVML)
fs.setIf((edx>>3)&1 == 1, NRIPS)
fs.setIf((edx>>4)&1 == 1, TSCRATEMSR)
fs.setIf((edx>>5)&1 == 1, VMCBCLEAN)
fs.setIf((edx>>6)&1 == 1, SVMFBASID)
fs.setIf((edx>>7)&1 == 1, SVMDA)
fs.setIf((edx>>10)&1 == 1, SVMPF)
fs.setIf((edx>>12)&1 == 1, SVMPFT)
}
if maxExtendedFunction() >= 0x8000001b && fs.inSet(IBS) {
eax, _, _, _ := cpuid(0x8000001b)
fs.setIf((eax>>0)&1 == 1, IBSFFV)
@ -1106,6 +1269,24 @@ func support() flagSet {
fs.setIf((eax>>7)&1 == 1, IBSRIPINVALIDCHK)
}
if maxExtendedFunction() >= 0x8000001f && vend == AMD {
a, _, _, _ := cpuid(0x8000001f)
fs.setIf((a>>0)&1 == 1, SME)
fs.setIf((a>>1)&1 == 1, SEV)
fs.setIf((a>>2)&1 == 1, MSR_PAGEFLUSH)
fs.setIf((a>>3)&1 == 1, SEV_ES)
fs.setIf((a>>4)&1 == 1, SEV_SNP)
fs.setIf((a>>5)&1 == 1, VMPL)
fs.setIf((a>>10)&1 == 1, SME_COHERENT)
fs.setIf((a>>11)&1 == 1, SEV_64BIT)
fs.setIf((a>>12)&1 == 1, SEV_RESTRICTED)
fs.setIf((a>>13)&1 == 1, SEV_ALTERNATIVE)
fs.setIf((a>>14)&1 == 1, SEV_DEBUGSWAP)
fs.setIf((a>>15)&1 == 1, IBS_PREVENTHOST)
fs.setIf((a>>16)&1 == 1, VTE)
fs.setIf((a>>24)&1 == 1, VMSA_REGPROT)
}
return fs
}

View File

@ -24,7 +24,7 @@ func addInfo(c *CPUInfo, safe bool) {
c.maxExFunc = maxExtendedFunction()
c.BrandName = brandName()
c.CacheLine = cacheLine()
c.Family, c.Model = familyModel()
c.Family, c.Model, c.Stepping = familyModel()
c.featureSet = support()
c.SGX = hasSGX(c.featureSet.inSet(SGX), c.featureSet.inSet(SGXLC))
c.ThreadsPerCore = threadsPerCore()

View File

@ -34,116 +34,164 @@ func _() {
_ = x[AVX512VP2INTERSECT-24]
_ = x[AVX512VPOPCNTDQ-25]
_ = x[AVXSLOW-26]
_ = x[BMI1-27]
_ = x[BMI2-28]
_ = x[CETIBT-29]
_ = x[CETSS-30]
_ = x[CLDEMOTE-31]
_ = x[CLMUL-32]
_ = x[CLZERO-33]
_ = x[CMOV-34]
_ = x[CMPXCHG8-35]
_ = x[CPBOOST-36]
_ = x[CX16-37]
_ = x[ENQCMD-38]
_ = x[ERMS-39]
_ = x[F16C-40]
_ = x[FMA3-41]
_ = x[FMA4-42]
_ = x[FXSR-43]
_ = x[FXSROPT-44]
_ = x[GFNI-45]
_ = x[HLE-46]
_ = x[HTT-47]
_ = x[HWA-48]
_ = x[HYPERVISOR-49]
_ = x[IBPB-50]
_ = x[IBS-51]
_ = x[IBSBRNTRGT-52]
_ = x[IBSFETCHSAM-53]
_ = x[IBSFFV-54]
_ = x[IBSOPCNT-55]
_ = x[IBSOPCNTEXT-56]
_ = x[IBSOPSAM-57]
_ = x[IBSRDWROPCNT-58]
_ = x[IBSRIPINVALIDCHK-59]
_ = x[INT_WBINVD-60]
_ = x[INVLPGB-61]
_ = x[LAHF-62]
_ = x[LZCNT-63]
_ = x[MCAOVERFLOW-64]
_ = x[MCOMMIT-65]
_ = x[MMX-66]
_ = x[MMXEXT-67]
_ = x[MOVBE-68]
_ = x[MOVDIR64B-69]
_ = x[MOVDIRI-70]
_ = x[MPX-71]
_ = x[MSRIRC-72]
_ = x[NX-73]
_ = x[OSXSAVE-74]
_ = x[POPCNT-75]
_ = x[RDPRU-76]
_ = x[RDRAND-77]
_ = x[RDSEED-78]
_ = x[RDTSCP-79]
_ = x[RTM-80]
_ = x[RTM_ALWAYS_ABORT-81]
_ = x[SCE-82]
_ = x[SERIALIZE-83]
_ = x[SGX-84]
_ = x[SGXLC-85]
_ = x[SHA-86]
_ = x[SSE-87]
_ = x[SSE2-88]
_ = x[SSE3-89]
_ = x[SSE4-90]
_ = x[SSE42-91]
_ = x[SSE4A-92]
_ = x[SSSE3-93]
_ = x[STIBP-94]
_ = x[SUCCOR-95]
_ = x[TBM-96]
_ = x[TSXLDTRK-97]
_ = x[VAES-98]
_ = x[VMX-99]
_ = x[VPCLMULQDQ-100]
_ = x[WAITPKG-101]
_ = x[WBNOINVD-102]
_ = x[X87-103]
_ = x[XOP-104]
_ = x[XSAVE-105]
_ = x[AESARM-106]
_ = x[ARMCPUID-107]
_ = x[ASIMD-108]
_ = x[ASIMDDP-109]
_ = x[ASIMDHP-110]
_ = x[ASIMDRDM-111]
_ = x[ATOMICS-112]
_ = x[CRC32-113]
_ = x[DCPOP-114]
_ = x[EVTSTRM-115]
_ = x[FCMA-116]
_ = x[FP-117]
_ = x[FPHP-118]
_ = x[GPA-119]
_ = x[JSCVT-120]
_ = x[LRCPC-121]
_ = x[PMULL-122]
_ = x[SHA1-123]
_ = x[SHA2-124]
_ = x[SHA3-125]
_ = x[SHA512-126]
_ = x[SM3-127]
_ = x[SM4-128]
_ = x[SVE-129]
_ = x[lastID-130]
_ = x[AVXVNNI-27]
_ = x[BMI1-28]
_ = x[BMI2-29]
_ = x[CETIBT-30]
_ = x[CETSS-31]
_ = x[CLDEMOTE-32]
_ = x[CLMUL-33]
_ = x[CLZERO-34]
_ = x[CMOV-35]
_ = x[CMPSB_SCADBS_SHORT-36]
_ = x[CMPXCHG8-37]
_ = x[CPBOOST-38]
_ = x[CX16-39]
_ = x[ENQCMD-40]
_ = x[ERMS-41]
_ = x[F16C-42]
_ = x[FLUSH_L1D-43]
_ = x[FMA3-44]
_ = x[FMA4-45]
_ = x[FSRM-46]
_ = x[FXSR-47]
_ = x[FXSROPT-48]
_ = x[GFNI-49]
_ = x[HLE-50]
_ = x[HRESET-51]
_ = x[HTT-52]
_ = x[HWA-53]
_ = x[HYBRID_CPU-54]
_ = x[HYPERVISOR-55]
_ = x[IA32_ARCH_CAP-56]
_ = x[IA32_CORE_CAP-57]
_ = x[IBPB-58]
_ = x[IBS-59]
_ = x[IBSBRNTRGT-60]
_ = x[IBSFETCHSAM-61]
_ = x[IBSFFV-62]
_ = x[IBSOPCNT-63]
_ = x[IBSOPCNTEXT-64]
_ = x[IBSOPSAM-65]
_ = x[IBSRDWROPCNT-66]
_ = x[IBSRIPINVALIDCHK-67]
_ = x[IBS_PREVENTHOST-68]
_ = x[INT_WBINVD-69]
_ = x[INVLPGB-70]
_ = x[LAHF-71]
_ = x[LAM-72]
_ = x[LBRVIRT-73]
_ = x[LZCNT-74]
_ = x[MCAOVERFLOW-75]
_ = x[MCDT_NO-76]
_ = x[MCOMMIT-77]
_ = x[MD_CLEAR-78]
_ = x[MMX-79]
_ = x[MMXEXT-80]
_ = x[MOVBE-81]
_ = x[MOVDIR64B-82]
_ = x[MOVDIRI-83]
_ = x[MOVSB_ZL-84]
_ = x[MPX-85]
_ = x[MSRIRC-86]
_ = x[MSR_PAGEFLUSH-87]
_ = x[NRIPS-88]
_ = x[NX-89]
_ = x[OSXSAVE-90]
_ = x[PCONFIG-91]
_ = x[POPCNT-92]
_ = x[RDPRU-93]
_ = x[RDRAND-94]
_ = x[RDSEED-95]
_ = x[RDTSCP-96]
_ = x[RTM-97]
_ = x[RTM_ALWAYS_ABORT-98]
_ = x[SERIALIZE-99]
_ = x[SEV-100]
_ = x[SEV_64BIT-101]
_ = x[SEV_ALTERNATIVE-102]
_ = x[SEV_DEBUGSWAP-103]
_ = x[SEV_ES-104]
_ = x[SEV_RESTRICTED-105]
_ = x[SEV_SNP-106]
_ = x[SGX-107]
_ = x[SGXLC-108]
_ = x[SHA-109]
_ = x[SME-110]
_ = x[SME_COHERENT-111]
_ = x[SPEC_CTRL_SSBD-112]
_ = x[SRBDS_CTRL-113]
_ = x[SSE-114]
_ = x[SSE2-115]
_ = x[SSE3-116]
_ = x[SSE4-117]
_ = x[SSE42-118]
_ = x[SSE4A-119]
_ = x[SSSE3-120]
_ = x[STIBP-121]
_ = x[STOSB_SHORT-122]
_ = x[SUCCOR-123]
_ = x[SVM-124]
_ = x[SVMDA-125]
_ = x[SVMFBASID-126]
_ = x[SVML-127]
_ = x[SVMNP-128]
_ = x[SVMPF-129]
_ = x[SVMPFT-130]
_ = x[SYSCALL-131]
_ = x[SYSEE-132]
_ = x[TBM-133]
_ = x[TME-134]
_ = x[TOPEXT-135]
_ = x[TSCRATEMSR-136]
_ = x[TSXLDTRK-137]
_ = x[VAES-138]
_ = x[VMCBCLEAN-139]
_ = x[VMPL-140]
_ = x[VMSA_REGPROT-141]
_ = x[VMX-142]
_ = x[VPCLMULQDQ-143]
_ = x[VTE-144]
_ = x[WAITPKG-145]
_ = x[WBNOINVD-146]
_ = x[X87-147]
_ = x[XGETBV1-148]
_ = x[XOP-149]
_ = x[XSAVE-150]
_ = x[XSAVEC-151]
_ = x[XSAVEOPT-152]
_ = x[XSAVES-153]
_ = x[AESARM-154]
_ = x[ARMCPUID-155]
_ = x[ASIMD-156]
_ = x[ASIMDDP-157]
_ = x[ASIMDHP-158]
_ = x[ASIMDRDM-159]
_ = x[ATOMICS-160]
_ = x[CRC32-161]
_ = x[DCPOP-162]
_ = x[EVTSTRM-163]
_ = x[FCMA-164]
_ = x[FP-165]
_ = x[FPHP-166]
_ = x[GPA-167]
_ = x[JSCVT-168]
_ = x[LRCPC-169]
_ = x[PMULL-170]
_ = x[SHA1-171]
_ = x[SHA2-172]
_ = x[SHA3-173]
_ = x[SHA512-174]
_ = x[SM3-175]
_ = x[SM4-176]
_ = x[SVE-177]
_ = x[lastID-178]
_ = x[firstID-0]
}
const _FeatureID_name = "firstIDADXAESNIAMD3DNOWAMD3DNOWEXTAMXBF16AMXINT8AMXTILEAVXAVX2AVX512BF16AVX512BITALGAVX512BWAVX512CDAVX512DQAVX512ERAVX512FAVX512FP16AVX512IFMAAVX512PFAVX512VBMIAVX512VBMI2AVX512VLAVX512VNNIAVX512VP2INTERSECTAVX512VPOPCNTDQAVXSLOWBMI1BMI2CETIBTCETSSCLDEMOTECLMULCLZEROCMOVCMPXCHG8CPBOOSTCX16ENQCMDERMSF16CFMA3FMA4FXSRFXSROPTGFNIHLEHTTHWAHYPERVISORIBPBIBSIBSBRNTRGTIBSFETCHSAMIBSFFVIBSOPCNTIBSOPCNTEXTIBSOPSAMIBSRDWROPCNTIBSRIPINVALIDCHKINT_WBINVDINVLPGBLAHFLZCNTMCAOVERFLOWMCOMMITMMXMMXEXTMOVBEMOVDIR64BMOVDIRIMPXMSRIRCNXOSXSAVEPOPCNTRDPRURDRANDRDSEEDRDTSCPRTMRTM_ALWAYS_ABORTSCESERIALIZESGXSGXLCSHASSESSE2SSE3SSE4SSE42SSE4ASSSE3STIBPSUCCORTBMTSXLDTRKVAESVMXVPCLMULQDQWAITPKGWBNOINVDX87XOPXSAVEAESARMARMCPUIDASIMDASIMDDPASIMDHPASIMDRDMATOMICSCRC32DCPOPEVTSTRMFCMAFPFPHPGPAJSCVTLRCPCPMULLSHA1SHA2SHA3SHA512SM3SM4SVElastID"
const _FeatureID_name = "firstIDADXAESNIAMD3DNOWAMD3DNOWEXTAMXBF16AMXINT8AMXTILEAVXAVX2AVX512BF16AVX512BITALGAVX512BWAVX512CDAVX512DQAVX512ERAVX512FAVX512FP16AVX512IFMAAVX512PFAVX512VBMIAVX512VBMI2AVX512VLAVX512VNNIAVX512VP2INTERSECTAVX512VPOPCNTDQAVXSLOWAVXVNNIBMI1BMI2CETIBTCETSSCLDEMOTECLMULCLZEROCMOVCMPSB_SCADBS_SHORTCMPXCHG8CPBOOSTCX16ENQCMDERMSF16CFLUSH_L1DFMA3FMA4FSRMFXSRFXSROPTGFNIHLEHRESETHTTHWAHYBRID_CPUHYPERVISORIA32_ARCH_CAPIA32_CORE_CAPIBPBIBSIBSBRNTRGTIBSFETCHSAMIBSFFVIBSOPCNTIBSOPCNTEXTIBSOPSAMIBSRDWROPCNTIBSRIPINVALIDCHKIBS_PREVENTHOSTINT_WBINVDINVLPGBLAHFLAMLBRVIRTLZCNTMCAOVERFLOWMCDT_NOMCOMMITMD_CLEARMMXMMXEXTMOVBEMOVDIR64BMOVDIRIMOVSB_ZLMPXMSRIRCMSR_PAGEFLUSHNRIPSNXOSXSAVEPCONFIGPOPCNTRDPRURDRANDRDSEEDRDTSCPRTMRTM_ALWAYS_ABORTSERIALIZESEVSEV_64BITSEV_ALTERNATIVESEV_DEBUGSWAPSEV_ESSEV_RESTRICTEDSEV_SNPSGXSGXLCSHASMESME_COHERENTSPEC_CTRL_SSBDSRBDS_CTRLSSESSE2SSE3SSE4SSE42SSE4ASSSE3STIBPSTOSB_SHORTSUCCORSVMSVMDASVMFBASIDSVMLSVMNPSVMPFSVMPFTSYSCALLSYSEETBMTMETOPEXTTSCRATEMSRTSXLDTRKVAESVMCBCLEANVMPLVMSA_REGPROTVMXVPCLMULQDQVTEWAITPKGWBNOINVDX87XGETBV1XOPXSAVEXSAVECXSAVEOPTXSAVESAESARMARMCPUIDASIMDASIMDDPASIMDHPASIMDRDMATOMICSCRC32DCPOPEVTSTRMFCMAFPFPHPGPAJSCVTLRCPCPMULLSHA1SHA2SHA3SHA512SM3SM4SVElastID"
var _FeatureID_index = [...]uint16{0, 7, 10, 15, 23, 34, 41, 48, 55, 58, 62, 72, 84, 92, 100, 108, 116, 123, 133, 143, 151, 161, 172, 180, 190, 208, 223, 230, 234, 238, 244, 249, 257, 262, 268, 272, 280, 287, 291, 297, 301, 305, 309, 313, 317, 324, 328, 331, 334, 337, 347, 351, 354, 364, 375, 381, 389, 400, 408, 420, 436, 446, 453, 457, 462, 473, 480, 483, 489, 494, 503, 510, 513, 519, 521, 528, 534, 539, 545, 551, 557, 560, 576, 579, 588, 591, 596, 599, 602, 606, 610, 614, 619, 624, 629, 634, 640, 643, 651, 655, 658, 668, 675, 683, 686, 689, 694, 700, 708, 713, 720, 727, 735, 742, 747, 752, 759, 763, 765, 769, 772, 777, 782, 787, 791, 795, 799, 805, 808, 811, 814, 820}
var _FeatureID_index = [...]uint16{0, 7, 10, 15, 23, 34, 41, 48, 55, 58, 62, 72, 84, 92, 100, 108, 116, 123, 133, 143, 151, 161, 172, 180, 190, 208, 223, 230, 237, 241, 245, 251, 256, 264, 269, 275, 279, 297, 305, 312, 316, 322, 326, 330, 339, 343, 347, 351, 355, 362, 366, 369, 375, 378, 381, 391, 401, 414, 427, 431, 434, 444, 455, 461, 469, 480, 488, 500, 516, 531, 541, 548, 552, 555, 562, 567, 578, 585, 592, 600, 603, 609, 614, 623, 630, 638, 641, 647, 660, 665, 667, 674, 681, 687, 692, 698, 704, 710, 713, 729, 738, 741, 750, 765, 778, 784, 798, 805, 808, 813, 816, 819, 831, 845, 855, 858, 862, 866, 870, 875, 880, 885, 890, 901, 907, 910, 915, 924, 928, 933, 938, 944, 951, 956, 959, 962, 968, 978, 986, 990, 999, 1003, 1015, 1018, 1028, 1031, 1038, 1046, 1049, 1056, 1059, 1064, 1070, 1078, 1084, 1090, 1098, 1103, 1110, 1117, 1125, 1132, 1137, 1142, 1149, 1153, 1155, 1159, 1162, 1167, 1172, 1177, 1181, 1185, 1189, 1195, 1198, 1201, 1204, 1210}
func (i FeatureID) String() string {
if i < 0 || i >= FeatureID(len(_FeatureID_index)-1) {

View File

@ -2,18 +2,120 @@
package cpuid
import "runtime"
import (
"runtime"
"strings"
"golang.org/x/sys/unix"
)
func detectOS(c *CPUInfo) bool {
if runtime.GOOS != "ios" {
tryToFillCPUInfoFomSysctl(c)
}
// There are no hw.optional sysctl values for the below features on Mac OS 11.0
// to detect their supported state dynamically. Assume the CPU features that
// Apple Silicon M1 supports to be available as a minimal set of features
// to all Go programs running on darwin/arm64.
// TODO: Add more if we know them.
c.featureSet.setIf(runtime.GOOS != "ios", AESARM, PMULL, SHA1, SHA2)
c.PhysicalCores = runtime.NumCPU()
// For now assuming 1 thread per core...
c.ThreadsPerCore = 1
c.LogicalCores = c.PhysicalCores
return true
}
func sysctlGetBool(name string) bool {
value, err := unix.SysctlUint32(name)
if err != nil {
return false
}
return value != 0
}
func sysctlGetString(name string) string {
value, err := unix.Sysctl(name)
if err != nil {
return ""
}
return value
}
func sysctlGetInt(unknown int, names ...string) int {
for _, name := range names {
value, err := unix.SysctlUint32(name)
if err != nil {
continue
}
if value != 0 {
return int(value)
}
}
return unknown
}
func sysctlGetInt64(unknown int, names ...string) int {
for _, name := range names {
value64, err := unix.SysctlUint64(name)
if err != nil {
continue
}
if int(value64) != unknown {
return int(value64)
}
}
return unknown
}
func setFeature(c *CPUInfo, name string, feature FeatureID) {
c.featureSet.setIf(sysctlGetBool(name), feature)
}
func tryToFillCPUInfoFomSysctl(c *CPUInfo) {
c.BrandName = sysctlGetString("machdep.cpu.brand_string")
if len(c.BrandName) != 0 {
c.VendorString = strings.Fields(c.BrandName)[0]
}
c.PhysicalCores = sysctlGetInt(runtime.NumCPU(), "hw.physicalcpu")
c.ThreadsPerCore = sysctlGetInt(1, "machdep.cpu.thread_count", "kern.num_threads") /
sysctlGetInt(1, "hw.physicalcpu")
c.LogicalCores = sysctlGetInt(runtime.NumCPU(), "machdep.cpu.core_count")
c.Family = sysctlGetInt(0, "machdep.cpu.family", "hw.cpufamily")
c.Model = sysctlGetInt(0, "machdep.cpu.model")
c.CacheLine = sysctlGetInt64(0, "hw.cachelinesize")
c.Cache.L1I = sysctlGetInt64(-1, "hw.l1icachesize")
c.Cache.L1D = sysctlGetInt64(-1, "hw.l1icachesize")
c.Cache.L2 = sysctlGetInt64(-1, "hw.l2cachesize")
c.Cache.L3 = sysctlGetInt64(-1, "hw.l3cachesize")
// from https://developer.arm.com/downloads/-/exploration-tools/feature-names-for-a-profile
setFeature(c, "hw.optional.arm.FEAT_AES", AESARM)
setFeature(c, "hw.optional.AdvSIMD", ASIMD)
setFeature(c, "hw.optional.arm.FEAT_DotProd", ASIMDDP)
setFeature(c, "hw.optional.arm.FEAT_RDM", ASIMDRDM)
setFeature(c, "hw.optional.FEAT_CRC32", CRC32)
setFeature(c, "hw.optional.arm.FEAT_DPB", DCPOP)
// setFeature(c, "", EVTSTRM)
setFeature(c, "hw.optional.arm.FEAT_FCMA", FCMA)
setFeature(c, "hw.optional.arm.FEAT_FP", FP)
setFeature(c, "hw.optional.arm.FEAT_FP16", FPHP)
setFeature(c, "hw.optional.arm.FEAT_PAuth", GPA)
setFeature(c, "hw.optional.arm.FEAT_JSCVT", JSCVT)
setFeature(c, "hw.optional.arm.FEAT_LRCPC", LRCPC)
setFeature(c, "hw.optional.arm.FEAT_PMULL", PMULL)
setFeature(c, "hw.optional.arm.FEAT_SHA1", SHA1)
setFeature(c, "hw.optional.arm.FEAT_SHA256", SHA2)
setFeature(c, "hw.optional.arm.FEAT_SHA3", SHA3)
setFeature(c, "hw.optional.arm.FEAT_SHA512", SHA512)
// setFeature(c, "", SM3)
// setFeature(c, "", SM4)
setFeature(c, "hw.optional.arm.FEAT_SVE", SVE)
// from empirical observation
setFeature(c, "hw.optional.AdvSIMD_HPFPCvt", ASIMDHP)
setFeature(c, "hw.optional.armv8_1_atomics", ATOMICS)
setFeature(c, "hw.optional.floatingpoint", FP)
setFeature(c, "hw.optional.armv8_2_sha3", SHA3)
setFeature(c, "hw.optional.armv8_2_sha512", SHA512)
setFeature(c, "hw.optional.armv8_3_compnum", FCMA)
setFeature(c, "hw.optional.armv8_crc32", CRC32)
}

View File

@ -1,5 +1,19 @@
# Changelog
## v4.9.1 - 2022-10-12
**Fixes**
* Fix logger panicing (when template is set to empty) by bumping dependency version [#2295](https://github.com/labstack/echo/issues/2295)
**Enhancements**
* Improve CORS documentation [#2272](https://github.com/labstack/echo/pull/2272)
* Update readme about supported Go versions [#2291](https://github.com/labstack/echo/pull/2291)
* Tests: improve error handling on closing body [#2254](https://github.com/labstack/echo/pull/2254)
* Tests: refactor some of the assertions in tests [#2275](https://github.com/labstack/echo/pull/2275)
* Tests: refactor assertions [#2301](https://github.com/labstack/echo/pull/2301)
## v4.9.0 - 2022-09-04
**Security**

View File

@ -11,12 +11,11 @@
## Supported Go versions
Latest version of Echo supports last four Go major [releases](https://go.dev/doc/devel/release) and might work with older versions.
As of version 4.0.0, Echo is available as a [Go module](https://github.com/golang/go/wiki/Modules).
Therefore a Go version capable of understanding /vN suffixed imports is required:
- 1.9.7+
- 1.10.3+
- 1.14+
Any of these versions will allow you to import Echo as `github.com/labstack/echo/v4` which is the recommended
way of using Echo going forward.

View File

@ -181,7 +181,7 @@ type (
// Logger returns the `Logger` instance.
Logger() Logger
// Set the logger
// SetLogger Set the logger
SetLogger(l Logger)
// Echo returns the `Echo` instance.

View File

@ -15,46 +15,85 @@ type (
// Skipper defines a function to skip middleware.
Skipper Skipper
// AllowOrigin defines a list of origins that may access the resource.
// AllowOrigins determines the value of the Access-Control-Allow-Origin
// response header. This header defines a list of origins that may access the
// resource. The wildcard characters '*' and '?' are supported and are
// converted to regex fragments '.*' and '.' accordingly.
//
// Security: use extreme caution when handling the origin, and carefully
// validate any logic. Remember that attackers may register hostile domain names.
// See https://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
//
// Optional. Default value []string{"*"}.
//
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
AllowOrigins []string `yaml:"allow_origins"`
// AllowOriginFunc is a custom function to validate the origin. It takes the
// origin as an argument and returns true if allowed or false otherwise. If
// an error is returned, it is returned by the handler. If this option is
// set, AllowOrigins is ignored.
//
// Security: use extreme caution when handling the origin, and carefully
// validate any logic. Remember that attackers may register hostile domain names.
// See https://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
//
// Optional.
AllowOriginFunc func(origin string) (bool, error) `yaml:"allow_origin_func"`
// AllowMethods defines a list methods allowed when accessing the resource.
// This is used in response to a preflight request.
// AllowMethods determines the value of the Access-Control-Allow-Methods
// response header. This header specified the list of methods allowed when
// accessing the resource. This is used in response to a preflight request.
//
// Optional. Default value DefaultCORSConfig.AllowMethods.
// If `allowMethods` is left empty will fill for preflight request `Access-Control-Allow-Methods` header value
// If `allowMethods` is left empty, this middleware will fill for preflight
// request `Access-Control-Allow-Methods` header value
// from `Allow` header that echo.Router set into context.
//
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
AllowMethods []string `yaml:"allow_methods"`
// AllowHeaders defines a list of request headers that can be used when
// making the actual request. This is in response to a preflight request.
// AllowHeaders determines the value of the Access-Control-Allow-Headers
// response header. This header is used in response to a preflight request to
// indicate which HTTP headers can be used when making the actual request.
//
// Optional. Default value []string{}.
//
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
AllowHeaders []string `yaml:"allow_headers"`
// AllowCredentials indicates whether or not the response to the request
// can be exposed when the credentials flag is true. When used as part of
// a response to a preflight request, this indicates whether or not the
// actual request can be made using credentials.
// Optional. Default value false.
// AllowCredentials determines the value of the
// Access-Control-Allow-Credentials response header. This header indicates
// whether or not the response to the request can be exposed when the
// credentials mode (Request.credentials) is true. When used as part of a
// response to a preflight request, this indicates whether or not the actual
// request can be made using credentials. See also
// [MDN: Access-Control-Allow-Credentials].
//
// Optional. Default value false, in which case the header is not set.
//
// Security: avoid using `AllowCredentials = true` with `AllowOrigins = *`.
// See http://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
// See "Exploiting CORS misconfigurations for Bitcoins and bounties",
// https://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
//
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
AllowCredentials bool `yaml:"allow_credentials"`
// ExposeHeaders defines a whitelist headers that clients are allowed to
// access.
// Optional. Default value []string{}.
// ExposeHeaders determines the value of Access-Control-Expose-Headers, which
// defines a list of headers that clients are allowed to access.
//
// Optional. Default value []string{}, in which case the header is not set.
//
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Header
ExposeHeaders []string `yaml:"expose_headers"`
// MaxAge indicates how long (in seconds) the results of a preflight request
// can be cached.
// Optional. Default value 0.
// MaxAge determines the value of the Access-Control-Max-Age response header.
// This header indicates how long (in seconds) the results of a preflight
// request can be cached.
//
// Optional. Default value 0. The header is set only if MaxAge > 0.
//
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
MaxAge int `yaml:"max_age"`
}
)
@ -69,13 +108,22 @@ var (
)
// CORS returns a Cross-Origin Resource Sharing (CORS) middleware.
// See: https://developer.mozilla.org/en/docs/Web/HTTP/Access_control_CORS
// See also [MDN: Cross-Origin Resource Sharing (CORS)].
//
// Security: Poorly configured CORS can compromise security because it allows
// relaxation of the browser's Same-Origin policy. See [Exploiting CORS
// misconfigurations for Bitcoins and bounties] and [Portswigger: Cross-origin
// resource sharing (CORS)] for more details.
//
// [MDN: Cross-Origin Resource Sharing (CORS)]: https://developer.mozilla.org/en/docs/Web/HTTP/Access_control_CORS
// [Exploiting CORS misconfigurations for Bitcoins and bounties]: https://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
// [Portswigger: Cross-origin resource sharing (CORS)]: https://portswigger.net/web-security/cors
func CORS() echo.MiddlewareFunc {
return CORSWithConfig(DefaultCORSConfig)
}
// CORSWithConfig returns a CORS middleware with config.
// See: `CORS()`.
// See: [CORS].
func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
// Defaults
if config.Skipper == nil {

View File

@ -12,19 +12,31 @@ type (
Bytes struct{}
)
// binary units (IEC 60027)
const (
_ = 1.0 << (10 * iota) // ignore first value by assigning to blank identifier
KB
MB
GB
TB
PB
EB
KiB
MiB
GiB
TiB
PiB
EiB
)
// decimal units (SI international system of units)
const (
KB = 1000
MB = KB * 1000
GB = MB * 1000
TB = GB * 1000
PB = TB * 1000
EB = PB * 1000
)
var (
pattern = regexp.MustCompile(`(?i)^(-?\d+(?:\.\d+)?)\s?([KMGTPE]B?|B?)$`)
global = New()
patternBinary = regexp.MustCompile(`(?i)^(-?\d+(?:\.\d+)?)\s?([KMGTPE]iB?)$`)
patternDecimal = regexp.MustCompile(`(?i)^(-?\d+(?:\.\d+)?)\s?([KMGTPE]B?|B?)$`)
global = New()
)
// New creates a Bytes instance.
@ -32,44 +44,97 @@ func New() *Bytes {
return &Bytes{}
}
// Format formats bytes integer to human readable string.
// Format formats bytes integer to human readable string according to IEC 60027.
// For example, 31323 bytes will return 30.59KB.
func (*Bytes) Format(b int64) string {
func (b *Bytes) Format(value int64) string {
return b.FormatBinary(value)
}
// FormatBinary formats bytes integer to human readable string according to IEC 60027.
// For example, 31323 bytes will return 30.59KB.
func (*Bytes) FormatBinary(value int64) string {
multiple := ""
value := float64(b)
val := float64(value)
switch {
case b >= EB:
value /= EB
multiple = "EB"
case b >= PB:
value /= PB
multiple = "PB"
case b >= TB:
value /= TB
multiple = "TB"
case b >= GB:
value /= GB
multiple = "GB"
case b >= MB:
value /= MB
multiple = "MB"
case b >= KB:
value /= KB
multiple = "KB"
case b == 0:
case value >= EiB:
val /= EiB
multiple = "EiB"
case value >= PiB:
val /= PiB
multiple = "PiB"
case value >= TiB:
val /= TiB
multiple = "TiB"
case value >= GiB:
val /= GiB
multiple = "GiB"
case value >= MiB:
val /= MiB
multiple = "MiB"
case value >= KiB:
val /= KiB
multiple = "KiB"
case value == 0:
return "0"
default:
return strconv.FormatInt(b, 10) + "B"
return strconv.FormatInt(value, 10) + "B"
}
return fmt.Sprintf("%.2f%s", value, multiple)
return fmt.Sprintf("%.2f%s", val, multiple)
}
// FormatDecimal formats bytes integer to human readable string according to SI international system of units.
// For example, 31323 bytes will return 31.32KB.
func (*Bytes) FormatDecimal(value int64) string {
multiple := ""
val := float64(value)
switch {
case value >= EB:
val /= EB
multiple = "EB"
case value >= PB:
val /= PB
multiple = "PB"
case value >= TB:
val /= TB
multiple = "TB"
case value >= GB:
val /= GB
multiple = "GB"
case value >= MB:
val /= MB
multiple = "MB"
case value >= KB:
val /= KB
multiple = "KB"
case value == 0:
return "0"
default:
return strconv.FormatInt(value, 10) + "B"
}
return fmt.Sprintf("%.2f%s", val, multiple)
}
// Parse parses human readable bytes string to bytes integer.
// For example, 6GB (6G is also valid) will return 6442450944.
func (*Bytes) Parse(value string) (i int64, err error) {
parts := pattern.FindStringSubmatch(value)
// For example, 6GiB (6Gi is also valid) will return 6442450944, and
// 6GB (6G is also valid) will return 6000000000.
func (b *Bytes) Parse(value string) (int64, error) {
i, err := b.ParseBinary(value)
if err == nil {
return i, err
}
return b.ParseDecimal(value)
}
// ParseBinary parses human readable bytes string to bytes integer.
// For example, 6GiB (6Gi is also valid) will return 6442450944.
func (*Bytes) ParseBinary(value string) (i int64, err error) {
parts := patternBinary.FindStringSubmatch(value)
if len(parts) < 3 {
return 0, fmt.Errorf("error parsing value=%s", value)
}
@ -81,8 +146,38 @@ func (*Bytes) Parse(value string) (i int64, err error) {
}
switch multiple {
case "KI", "KIB":
return int64(bytes * KiB), nil
case "MI", "MIB":
return int64(bytes * MiB), nil
case "GI", "GIB":
return int64(bytes * GiB), nil
case "TI", "TIB":
return int64(bytes * TiB), nil
case "PI", "PIB":
return int64(bytes * PiB), nil
case "EI", "EIB":
return int64(bytes * EiB), nil
default:
return int64(bytes), nil
}
}
// ParseDecimal parses human readable bytes string to bytes integer.
// For example, 6GB (6G is also valid) will return 6000000000.
func (*Bytes) ParseDecimal(value string) (i int64, err error) {
parts := patternDecimal.FindStringSubmatch(value)
if len(parts) < 3 {
return 0, fmt.Errorf("error parsing value=%s", value)
}
bytesString := parts[1]
multiple := strings.ToUpper(parts[2])
bytes, err := strconv.ParseFloat(bytesString, 64)
if err != nil {
return
}
switch multiple {
case "K", "KB":
return int64(bytes * KB), nil
case "M", "MB":
@ -95,15 +190,27 @@ func (*Bytes) Parse(value string) (i int64, err error) {
return int64(bytes * PB), nil
case "E", "EB":
return int64(bytes * EB), nil
default:
return int64(bytes), nil
}
}
// Format wraps global Bytes's Format function.
func Format(b int64) string {
return global.Format(b)
func Format(value int64) string {
return global.Format(value)
}
// FormatBinary wraps global Bytes's FormatBinary function.
func FormatBinary(value int64) string {
return global.FormatBinary(value)
}
// FormatDecimal wraps global Bytes's FormatDecimal function.
func FormatDecimal(value int64) string {
return global.FormatDecimal(value)
}
// Parse wraps global Bytes's Parse function.
func Parse(val string) (int64, error) {
return global.Parse(val)
func Parse(value string) (int64, error) {
return global.Parse(value)
}

View File

@ -391,7 +391,7 @@ func (l *Logger) log(level Lvl, format string, args ...interface{}) {
if err == nil {
s := buf.String()
i := buf.Len() - 1
if s[i] == '}' {
if i >= 0 && s[i] == '}' {
// JSON header
buf.Truncate(i)
buf.WriteByte(',')
@ -404,7 +404,9 @@ func (l *Logger) log(level Lvl, format string, args ...interface{}) {
}
} else {
// Text header
buf.WriteByte(' ')
if len(s) > 0 {
buf.WriteByte(' ')
}
buf.WriteString(message)
}
buf.WriteByte('\n')

View File

@ -24,7 +24,7 @@ func isPacketConn(c net.Conn) bool {
}
if ua, ok := c.LocalAddr().(*net.UnixAddr); ok {
return ua.Net == "unixgram"
return ua.Net == "unixgram" || ua.Net == "unixpacket"
}
return true
@ -280,7 +280,7 @@ func (co *Conn) ReadMsg() (*Msg, error) {
}
if t := m.IsTsig(); t != nil {
// Need to work on the original message p, as that was used to calculate the tsig.
err = tsigVerifyProvider(p, co.tsigProvider(), co.tsigRequestMAC, false)
err = TsigVerifyWithProvider(p, co.tsigProvider(), co.tsigRequestMAC, false)
}
return m, err
}
@ -358,7 +358,7 @@ func (co *Conn) WriteMsg(m *Msg) (err error) {
var out []byte
if t := m.IsTsig(); t != nil {
// Set tsigRequestMAC for the next read, although only used in zone transfers.
out, co.tsigRequestMAC, err = tsigGenerateProvider(m, co.tsigProvider(), co.tsigRequestMAC, false)
out, co.tsigRequestMAC, err = TsigGenerateWithProvider(m, co.tsigProvider(), co.tsigRequestMAC, false)
} else {
out, err = m.Pack()
}

View File

@ -218,6 +218,11 @@ func IsDomainName(s string) (labels int, ok bool) {
wasDot = false
case '.':
if i == 0 && len(s) > 1 {
// leading dots are not legal except for the root zone
return labels, false
}
if wasDot {
// two dots back to back is not legal
return labels, false

View File

@ -65,6 +65,9 @@ var AlgorithmToString = map[uint8]string{
}
// AlgorithmToHash is a map of algorithm crypto hash IDs to crypto.Hash's.
// For newer algorithm that do their own hashing (i.e. ED25519) the returned value
// is 0, implying no (external) hashing should occur. The non-exported identityHash is then
// used.
var AlgorithmToHash = map[uint8]crypto.Hash{
RSAMD5: crypto.MD5, // Deprecated in RFC 6725
DSA: crypto.SHA1,
@ -74,7 +77,7 @@ var AlgorithmToHash = map[uint8]crypto.Hash{
ECDSAP256SHA256: crypto.SHA256,
ECDSAP384SHA384: crypto.SHA384,
RSASHA512: crypto.SHA512,
ED25519: crypto.Hash(0),
ED25519: 0,
}
// DNSSEC hashing algorithm codes.
@ -137,12 +140,12 @@ func (k *DNSKEY) KeyTag() uint16 {
var keytag int
switch k.Algorithm {
case RSAMD5:
// Look at the bottom two bytes of the modules, which the last
// item in the pubkey.
// This algorithm has been deprecated, but keep this key-tag calculation.
// Look at the bottom two bytes of the modules, which the last item in the pubkey.
// See https://www.rfc-editor.org/errata/eid193 .
modulus, _ := fromBase64([]byte(k.PublicKey))
if len(modulus) > 1 {
x := binary.BigEndian.Uint16(modulus[len(modulus)-2:])
x := binary.BigEndian.Uint16(modulus[len(modulus)-3:])
keytag = int(x)
}
default:
@ -296,35 +299,20 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
return err
}
hash, ok := AlgorithmToHash[rr.Algorithm]
if !ok {
return ErrAlg
h, cryptohash, err := hashFromAlgorithm(rr.Algorithm)
if err != nil {
return err
}
switch rr.Algorithm {
case ED25519:
// ed25519 signs the raw message and performs hashing internally.
// All other supported signature schemes operate over the pre-hashed
// message, and thus ed25519 must be handled separately here.
//
// The raw message is passed directly into sign and crypto.Hash(0) is
// used to signal to the crypto.Signer that the data has not been hashed.
signature, err := sign(k, append(signdata, wire...), crypto.Hash(0), rr.Algorithm)
if err != nil {
return err
}
rr.Signature = toBase64(signature)
return nil
case RSAMD5, DSA, DSANSEC3SHA1:
// See RFC 6944.
return ErrAlg
default:
h := hash.New()
h.Write(signdata)
h.Write(wire)
signature, err := sign(k, h.Sum(nil), hash, rr.Algorithm)
signature, err := sign(k, h.Sum(nil), cryptohash, rr.Algorithm)
if err != nil {
return err
}
@ -341,7 +329,7 @@ func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte,
}
switch alg {
case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:
case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512, ED25519:
return signature, nil
case ECDSAP256SHA256, ECDSAP384SHA384:
ecdsaSignature := &struct {
@ -362,8 +350,6 @@ func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte,
signature := intToBytes(ecdsaSignature.R, intlen)
signature = append(signature, intToBytes(ecdsaSignature.S, intlen)...)
return signature, nil
case ED25519:
return signature, nil
default:
return nil, ErrAlg
}
@ -437,9 +423,9 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
// remove the domain name and assume its ours?
}
hash, ok := AlgorithmToHash[rr.Algorithm]
if !ok {
return ErrAlg
h, cryptohash, err := hashFromAlgorithm(rr.Algorithm)
if err != nil {
return err
}
switch rr.Algorithm {
@ -450,10 +436,9 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
return ErrKey
}
h := hash.New()
h.Write(signeddata)
h.Write(wire)
return rsa.VerifyPKCS1v15(pubkey, hash, h.Sum(nil), sigbuf)
return rsa.VerifyPKCS1v15(pubkey, cryptohash, h.Sum(nil), sigbuf)
case ECDSAP256SHA256, ECDSAP384SHA384:
pubkey := k.publicKeyECDSA()
@ -465,7 +450,6 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
r := new(big.Int).SetBytes(sigbuf[:len(sigbuf)/2])
s := new(big.Int).SetBytes(sigbuf[len(sigbuf)/2:])
h := hash.New()
h.Write(signeddata)
h.Write(wire)
if ecdsa.Verify(pubkey, h.Sum(nil), r, s) {

16
vendor/github.com/miekg/dns/edns.go generated vendored
View File

@ -584,14 +584,17 @@ func (e *EDNS0_N3U) copy() EDNS0 { return &EDNS0_N3U{e.Code, e.AlgCode} }
type EDNS0_EXPIRE struct {
Code uint16 // Always EDNS0EXPIRE
Expire uint32
Empty bool // Empty is used to signal an empty Expire option in a backwards compatible way, it's not used on the wire.
}
// Option implements the EDNS0 interface.
func (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE }
func (e *EDNS0_EXPIRE) String() string { return strconv.FormatUint(uint64(e.Expire), 10) }
func (e *EDNS0_EXPIRE) copy() EDNS0 { return &EDNS0_EXPIRE{e.Code, e.Expire} }
func (e *EDNS0_EXPIRE) copy() EDNS0 { return &EDNS0_EXPIRE{e.Code, e.Expire, e.Empty} }
func (e *EDNS0_EXPIRE) pack() ([]byte, error) {
if e.Empty {
return []byte{}, nil
}
b := make([]byte, 4)
binary.BigEndian.PutUint32(b, e.Expire)
return b, nil
@ -600,15 +603,24 @@ func (e *EDNS0_EXPIRE) pack() ([]byte, error) {
func (e *EDNS0_EXPIRE) unpack(b []byte) error {
if len(b) == 0 {
// zero-length EXPIRE query, see RFC 7314 Section 2
e.Empty = true
return nil
}
if len(b) < 4 {
return ErrBuf
}
e.Expire = binary.BigEndian.Uint32(b)
e.Empty = false
return nil
}
func (e *EDNS0_EXPIRE) String() (s string) {
if e.Empty {
return ""
}
return strconv.FormatUint(uint64(e.Expire), 10)
}
// The EDNS0_LOCAL option is used for local/experimental purposes. The option
// code is recommended to be within the range [EDNS0LOCALSTART, EDNS0LOCALEND]
// (RFC6891), although any unassigned code can actually be used. The content of

31
vendor/github.com/miekg/dns/hash.go generated vendored Normal file
View File

@ -0,0 +1,31 @@
package dns
import (
"bytes"
"crypto"
"hash"
)
// identityHash will not hash, it only buffers the data written into it and returns it as-is.
type identityHash struct {
b *bytes.Buffer
}
// Implement the hash.Hash interface.
func (i identityHash) Write(b []byte) (int, error) { return i.b.Write(b) }
func (i identityHash) Size() int { return i.b.Len() }
func (i identityHash) BlockSize() int { return 1024 }
func (i identityHash) Reset() { i.b.Reset() }
func (i identityHash) Sum(b []byte) []byte { return append(b, i.b.Bytes()...) }
func hashFromAlgorithm(alg uint8) (hash.Hash, crypto.Hash, error) {
hashnumber, ok := AlgorithmToHash[alg]
if !ok {
return nil, 0, ErrAlg
}
if hashnumber == 0 {
return identityHash{b: &bytes.Buffer{}}, hashnumber, nil
}
return hashnumber.New(), hashnumber, nil
}

5
vendor/github.com/miekg/dns/msg.go generated vendored
View File

@ -265,6 +265,11 @@ loop:
wasDot = false
case '.':
if i == 0 && len(s) > 1 {
// leading dots are not legal except for the root zone
return len(msg), ErrRdata
}
if wasDot {
// two dots back to back is not legal
return len(msg), ErrRdata

View File

@ -476,7 +476,7 @@ func unpackDataNsec(msg []byte, off int) ([]uint16, int, error) {
length, window, lastwindow := 0, 0, -1
for off < len(msg) {
if off+2 > len(msg) {
return nsec, len(msg), &Error{err: "overflow unpacking nsecx"}
return nsec, len(msg), &Error{err: "overflow unpacking NSEC(3)"}
}
window = int(msg[off])
length = int(msg[off+1])
@ -484,17 +484,17 @@ func unpackDataNsec(msg []byte, off int) ([]uint16, int, error) {
if window <= lastwindow {
// RFC 4034: Blocks are present in the NSEC RR RDATA in
// increasing numerical order.
return nsec, len(msg), &Error{err: "out of order NSEC block"}
return nsec, len(msg), &Error{err: "out of order NSEC(3) block in type bitmap"}
}
if length == 0 {
// RFC 4034: Blocks with no types present MUST NOT be included.
return nsec, len(msg), &Error{err: "empty NSEC block"}
return nsec, len(msg), &Error{err: "empty NSEC(3) block in type bitmap"}
}
if length > 32 {
return nsec, len(msg), &Error{err: "NSEC block too long"}
return nsec, len(msg), &Error{err: "NSEC(3) block too long in type bitmap"}
}
if off+length > len(msg) {
return nsec, len(msg), &Error{err: "overflowing NSEC block"}
return nsec, len(msg), &Error{err: "overflowing NSEC(3) block in type bitmap"}
}
// Walk the bytes in the window and extract the type bits
@ -558,6 +558,16 @@ func packDataNsec(bitmap []uint16, msg []byte, off int) (int, error) {
if len(bitmap) == 0 {
return off, nil
}
if off > len(msg) {
return off, &Error{err: "overflow packing nsec"}
}
toZero := msg[off:]
if maxLen := typeBitMapLen(bitmap); maxLen < len(toZero) {
toZero = toZero[:maxLen]
}
for i := range toZero {
toZero[i] = 0
}
var lastwindow, lastlength uint16
for _, t := range bitmap {
window := t / 256

View File

@ -646,7 +646,7 @@ func (srv *Server) serveDNS(m []byte, w *response) {
w.tsigStatus = nil
if w.tsigProvider != nil {
if t := req.IsTsig(); t != nil {
w.tsigStatus = tsigVerifyProvider(m, w.tsigProvider, "", false)
w.tsigStatus = TsigVerifyWithProvider(m, w.tsigProvider, "", false)
w.tsigTimersOnly = false
w.tsigRequestMAC = t.MAC
}
@ -728,7 +728,7 @@ func (w *response) WriteMsg(m *Msg) (err error) {
var data []byte
if w.tsigProvider != nil { // if no provider, dont check for the tsig (which is a longer check)
if t := m.IsTsig(); t != nil {
data, w.tsigRequestMAC, err = tsigGenerateProvider(m, w.tsigProvider, w.tsigRequestMAC, w.tsigTimersOnly)
data, w.tsigRequestMAC, err = TsigGenerateWithProvider(m, w.tsigProvider, w.tsigRequestMAC, w.tsigTimersOnly)
if err != nil {
return err
}

51
vendor/github.com/miekg/dns/sig0.go generated vendored
View File

@ -3,6 +3,7 @@ package dns
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"encoding/binary"
"math/big"
@ -38,18 +39,17 @@ func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) {
}
buf = buf[:off:cap(buf)]
hash, ok := AlgorithmToHash[rr.Algorithm]
if !ok {
return nil, ErrAlg
h, cryptohash, err := hashFromAlgorithm(rr.Algorithm)
if err != nil {
return nil, err
}
hasher := hash.New()
// Write SIG rdata
hasher.Write(buf[len(mbuf)+1+2+2+4+2:])
h.Write(buf[len(mbuf)+1+2+2+4+2:])
// Write message
hasher.Write(buf[:len(mbuf)])
h.Write(buf[:len(mbuf)])
signature, err := sign(k, hasher.Sum(nil), hash, rr.Algorithm)
signature, err := sign(k, h.Sum(nil), cryptohash, rr.Algorithm)
if err != nil {
return nil, err
}
@ -82,20 +82,10 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
return ErrKey
}
var hash crypto.Hash
switch rr.Algorithm {
case RSASHA1:
hash = crypto.SHA1
case RSASHA256, ECDSAP256SHA256:
hash = crypto.SHA256
case ECDSAP384SHA384:
hash = crypto.SHA384
case RSASHA512:
hash = crypto.SHA512
default:
return ErrAlg
h, cryptohash, err := hashFromAlgorithm(rr.Algorithm)
if err != nil {
return err
}
hasher := hash.New()
buflen := len(buf)
qdc := binary.BigEndian.Uint16(buf[4:])
@ -103,7 +93,6 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
auc := binary.BigEndian.Uint16(buf[8:])
adc := binary.BigEndian.Uint16(buf[10:])
offset := headerSize
var err error
for i := uint16(0); i < qdc && offset < buflen; i++ {
_, offset, err = UnpackDomainName(buf, offset)
if err != nil {
@ -166,21 +155,21 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
return &Error{err: "signer name doesn't match key name"}
}
sigend := offset
hasher.Write(buf[sigstart:sigend])
hasher.Write(buf[:10])
hasher.Write([]byte{
h.Write(buf[sigstart:sigend])
h.Write(buf[:10])
h.Write([]byte{
byte((adc - 1) << 8),
byte(adc - 1),
})
hasher.Write(buf[12:bodyend])
h.Write(buf[12:bodyend])
hashed := hasher.Sum(nil)
hashed := h.Sum(nil)
sig := buf[sigend:]
switch k.Algorithm {
case RSASHA1, RSASHA256, RSASHA512:
pk := k.publicKeyRSA()
if pk != nil {
return rsa.VerifyPKCS1v15(pk, hash, hashed, sig)
return rsa.VerifyPKCS1v15(pk, cryptohash, hashed, sig)
}
case ECDSAP256SHA256, ECDSAP384SHA384:
pk := k.publicKeyECDSA()
@ -192,6 +181,14 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
}
return ErrSig
}
case ED25519:
pk := k.publicKeyED25519()
if pk != nil {
if ed25519.Verify(pk, hashed, sig) {
return nil
}
return ErrSig
}
}
return ErrKeyAlg
}

330
vendor/github.com/miekg/dns/svcb.go generated vendored
View File

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"net"
"sort"
"strconv"
@ -13,16 +14,18 @@ import (
// SVCBKey is the type of the keys used in the SVCB RR.
type SVCBKey uint16
// Keys defined in draft-ietf-dnsop-svcb-https-01 Section 12.3.2.
// Keys defined in draft-ietf-dnsop-svcb-https-08 Section 14.3.2.
const (
SVCB_MANDATORY SVCBKey = 0
SVCB_ALPN SVCBKey = 1
SVCB_NO_DEFAULT_ALPN SVCBKey = 2
SVCB_PORT SVCBKey = 3
SVCB_IPV4HINT SVCBKey = 4
SVCB_ECHCONFIG SVCBKey = 5
SVCB_IPV6HINT SVCBKey = 6
svcb_RESERVED SVCBKey = 65535
SVCB_MANDATORY SVCBKey = iota
SVCB_ALPN
SVCB_NO_DEFAULT_ALPN
SVCB_PORT
SVCB_IPV4HINT
SVCB_ECHCONFIG
SVCB_IPV6HINT
SVCB_DOHPATH // draft-ietf-add-svcb-dns-02 Section 9
svcb_RESERVED SVCBKey = 65535
)
var svcbKeyToStringMap = map[SVCBKey]string{
@ -31,8 +34,9 @@ var svcbKeyToStringMap = map[SVCBKey]string{
SVCB_NO_DEFAULT_ALPN: "no-default-alpn",
SVCB_PORT: "port",
SVCB_IPV4HINT: "ipv4hint",
SVCB_ECHCONFIG: "echconfig",
SVCB_ECHCONFIG: "ech",
SVCB_IPV6HINT: "ipv6hint",
SVCB_DOHPATH: "dohpath",
}
var svcbStringToKeyMap = reverseSVCBKeyMap(svcbKeyToStringMap)
@ -167,10 +171,14 @@ func (rr *SVCB) parse(c *zlexer, o string) *ParseError {
}
l, _ = c.Next()
}
// "In AliasMode, records SHOULD NOT include any SvcParams, and recipients MUST
// ignore any SvcParams that are present."
// However, we don't check rr.Priority == 0 && len(xs) > 0 here
// It is the responsibility of the user of the library to check this.
// This is to encourage the fixing of the source of this error.
rr.Value = xs
if rr.Priority == 0 && len(xs) > 0 {
return &ParseError{l.token, "SVCB aliasform can't have values", l}
}
return nil
}
@ -191,6 +199,8 @@ func makeSVCBKeyValue(key SVCBKey) SVCBKeyValue {
return new(SVCBECHConfig)
case SVCB_IPV6HINT:
return new(SVCBIPv6Hint)
case SVCB_DOHPATH:
return new(SVCBDoHPath)
case svcb_RESERVED:
return nil
default:
@ -200,16 +210,24 @@ func makeSVCBKeyValue(key SVCBKey) SVCBKeyValue {
}
}
// SVCB RR. See RFC xxxx (https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-01).
// SVCB RR. See RFC xxxx (https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-08).
//
// NOTE: The HTTPS/SVCB RFCs are in the draft stage.
// The API, including constants and types related to SVCBKeyValues, may
// change in future versions in accordance with the latest drafts.
type SVCB struct {
Hdr RR_Header
Priority uint16
Priority uint16 // If zero, Value must be empty or discarded by the user of this library
Target string `dns:"domain-name"`
Value []SVCBKeyValue `dns:"pairs"` // Value must be empty if Priority is zero.
Value []SVCBKeyValue `dns:"pairs"`
}
// HTTPS RR. Everything valid for SVCB applies to HTTPS as well.
// Except that the HTTPS record is intended for use with the HTTP and HTTPS protocols.
//
// NOTE: The HTTPS/SVCB RFCs are in the draft stage.
// The API, including constants and types related to SVCBKeyValues, may
// change in future versions in accordance with the latest drafts.
type HTTPS struct {
SVCB
}
@ -235,15 +253,29 @@ type SVCBKeyValue interface {
}
// SVCBMandatory pair adds to required keys that must be interpreted for the RR
// to be functional.
// to be functional. If ignored, the whole RRSet must be ignored.
// "port" and "no-default-alpn" are mandatory by default if present,
// so they shouldn't be included here.
//
// It is incumbent upon the user of this library to reject the RRSet if
// or avoid constructing such an RRSet that:
// - "mandatory" is included as one of the keys of mandatory
// - no key is listed multiple times in mandatory
// - all keys listed in mandatory are present
// - escape sequences are not used in mandatory
// - mandatory, when present, lists at least one key
//
// Basic use pattern for creating a mandatory option:
//
// s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}
// e := new(dns.SVCBMandatory)
// e.Code = []uint16{65403}
// e.Code = []uint16{dns.SVCB_ALPN}
// s.Value = append(s.Value, e)
// t := new(dns.SVCBAlpn)
// t.Alpn = []string{"xmpp-client"}
// s.Value = append(s.Value, t)
type SVCBMandatory struct {
Code []SVCBKey // Must not include mandatory
Code []SVCBKey
}
func (*SVCBMandatory) Key() SVCBKey { return SVCB_MANDATORY }
@ -302,7 +334,8 @@ func (s *SVCBMandatory) copy() SVCBKeyValue {
}
// SVCBAlpn pair is used to list supported connection protocols.
// Protocol ids can be found at:
// The user of this library must ensure that at least one protocol is listed when alpn is present.
// Protocol IDs can be found at:
// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
// Basic use pattern for creating an alpn option:
//
@ -310,13 +343,57 @@ func (s *SVCBMandatory) copy() SVCBKeyValue {
// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
// e := new(dns.SVCBAlpn)
// e.Alpn = []string{"h2", "http/1.1"}
// h.Value = append(o.Value, e)
// h.Value = append(h.Value, e)
type SVCBAlpn struct {
Alpn []string
}
func (*SVCBAlpn) Key() SVCBKey { return SVCB_ALPN }
func (s *SVCBAlpn) String() string { return strings.Join(s.Alpn, ",") }
func (*SVCBAlpn) Key() SVCBKey { return SVCB_ALPN }
func (s *SVCBAlpn) String() string {
// An ALPN value is a comma-separated list of values, each of which can be
// an arbitrary binary value. In order to allow parsing, the comma and
// backslash characters are themselves excaped.
//
// However, this escaping is done in addition to the normal escaping which
// happens in zone files, meaning that these values must be
// double-escaped. This looks terrible, so if you see a never-ending
// sequence of backslash in a zone file this may be why.
//
// https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-08#appendix-A.1
var str strings.Builder
for i, alpn := range s.Alpn {
// 4*len(alpn) is the worst case where we escape every character in the alpn as \123, plus 1 byte for the ',' separating the alpn from others
str.Grow(4*len(alpn) + 1)
if i > 0 {
str.WriteByte(',')
}
for j := 0; j < len(alpn); j++ {
e := alpn[j]
if ' ' > e || e > '~' {
str.WriteString(escapeByte(e))
continue
}
switch e {
// We escape a few characters which may confuse humans or parsers.
case '"', ';', ' ':
str.WriteByte('\\')
str.WriteByte(e)
// The comma and backslash characters themselves must be
// doubly-escaped. We use `\\` for the first backslash and
// the escaped numeric value for the other value. We especially
// don't want a comma in the output.
case ',':
str.WriteString(`\\\044`)
case '\\':
str.WriteString(`\\\092`)
default:
str.WriteByte(e)
}
}
}
return str.String()
}
func (s *SVCBAlpn) pack() ([]byte, error) {
// Liberally estimate the size of an alpn as 10 octets
@ -351,7 +428,47 @@ func (s *SVCBAlpn) unpack(b []byte) error {
}
func (s *SVCBAlpn) parse(b string) error {
s.Alpn = strings.Split(b, ",")
if len(b) == 0 {
s.Alpn = []string{}
return nil
}
alpn := []string{}
a := []byte{}
for p := 0; p < len(b); {
c, q := nextByte(b, p)
if q == 0 {
return errors.New("dns: svcbalpn: unterminated escape")
}
p += q
// If we find a comma, we have finished reading an alpn.
if c == ',' {
if len(a) == 0 {
return errors.New("dns: svcbalpn: empty protocol identifier")
}
alpn = append(alpn, string(a))
a = []byte{}
continue
}
// If it's a backslash, we need to handle a comma-separated list.
if c == '\\' {
dc, dq := nextByte(b, p)
if dq == 0 {
return errors.New("dns: svcbalpn: unterminated escape decoding comma-separated list")
}
if dc != '\\' && dc != ',' {
return errors.New("dns: svcbalpn: bad escaped character decoding comma-separated list")
}
p += dq
c = dc
}
a = append(a, c)
}
// Add the final alpn.
if len(a) == 0 {
return errors.New("dns: svcbalpn: last protocol identifier empty")
}
s.Alpn = append(alpn, string(a))
return nil
}
@ -370,9 +487,13 @@ func (s *SVCBAlpn) copy() SVCBKeyValue {
}
// SVCBNoDefaultAlpn pair signifies no support for default connection protocols.
// Should be used in conjunction with alpn.
// Basic use pattern for creating a no-default-alpn option:
//
// s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}
// t := new(dns.SVCBAlpn)
// t.Alpn = []string{"xmpp-client"}
// s.Value = append(s.Value, t)
// e := new(dns.SVCBNoDefaultAlpn)
// s.Value = append(s.Value, e)
type SVCBNoDefaultAlpn struct{}
@ -385,14 +506,14 @@ func (*SVCBNoDefaultAlpn) len() int { return 0 }
func (*SVCBNoDefaultAlpn) unpack(b []byte) error {
if len(b) != 0 {
return errors.New("dns: svcbnodefaultalpn: no_default_alpn must have no value")
return errors.New("dns: svcbnodefaultalpn: no-default-alpn must have no value")
}
return nil
}
func (*SVCBNoDefaultAlpn) parse(b string) error {
if b != "" {
return errors.New("dns: svcbnodefaultalpn: no_default_alpn must have no value")
return errors.New("dns: svcbnodefaultalpn: no-default-alpn must have no value")
}
return nil
}
@ -523,7 +644,7 @@ func (s *SVCBIPv4Hint) copy() SVCBKeyValue {
}
// SVCBECHConfig pair contains the ECHConfig structure defined in draft-ietf-tls-esni [RFC xxxx].
// Basic use pattern for creating an echconfig option:
// Basic use pattern for creating an ech option:
//
// h := new(dns.HTTPS)
// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
@ -531,7 +652,7 @@ func (s *SVCBIPv4Hint) copy() SVCBKeyValue {
// e.ECH = []byte{0xfe, 0x08, ...}
// h.Value = append(h.Value, e)
type SVCBECHConfig struct {
ECH []byte
ECH []byte // Specifically ECHConfigList including the redundant length prefix
}
func (*SVCBECHConfig) Key() SVCBKey { return SVCB_ECHCONFIG }
@ -555,7 +676,7 @@ func (s *SVCBECHConfig) unpack(b []byte) error {
func (s *SVCBECHConfig) parse(b string) error {
x, err := fromBase64([]byte(b))
if err != nil {
return errors.New("dns: svcbechconfig: bad base64 echconfig")
return errors.New("dns: svcbech: bad base64 ech")
}
s.ECH = x
return nil
@ -618,9 +739,6 @@ func (s *SVCBIPv6Hint) String() string {
}
func (s *SVCBIPv6Hint) parse(b string) error {
if strings.Contains(b, ".") {
return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4")
}
str := strings.Split(b, ",")
dst := make([]net.IP, len(str))
for i, e := range str {
@ -628,6 +746,9 @@ func (s *SVCBIPv6Hint) parse(b string) error {
if ip == nil {
return errors.New("dns: svcbipv6hint: bad ip")
}
if ip.To4() != nil {
return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4-mapped-ipv6")
}
dst[i] = ip
}
s.Hint = dst
@ -645,6 +766,54 @@ func (s *SVCBIPv6Hint) copy() SVCBKeyValue {
}
}
// SVCBDoHPath pair is used to indicate the URI template that the
// clients may use to construct a DNS over HTTPS URI.
//
// See RFC xxxx (https://datatracker.ietf.org/doc/html/draft-ietf-add-svcb-dns-02)
// and RFC yyyy (https://datatracker.ietf.org/doc/html/draft-ietf-add-ddr-06).
//
// A basic example of using the dohpath option together with the alpn
// option to indicate support for DNS over HTTPS on a certain path:
//
// s := new(dns.SVCB)
// s.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}
// e := new(dns.SVCBAlpn)
// e.Alpn = []string{"h2", "h3"}
// p := new(dns.SVCBDoHPath)
// p.Template = "/dns-query{?dns}"
// s.Value = append(s.Value, e, p)
//
// The parsing currently doesn't validate that Template is a valid
// RFC 6570 URI template.
type SVCBDoHPath struct {
Template string
}
func (*SVCBDoHPath) Key() SVCBKey { return SVCB_DOHPATH }
func (s *SVCBDoHPath) String() string { return svcbParamToStr([]byte(s.Template)) }
func (s *SVCBDoHPath) len() int { return len(s.Template) }
func (s *SVCBDoHPath) pack() ([]byte, error) { return []byte(s.Template), nil }
func (s *SVCBDoHPath) unpack(b []byte) error {
s.Template = string(b)
return nil
}
func (s *SVCBDoHPath) parse(b string) error {
template, err := svcbParseParam(b)
if err != nil {
return fmt.Errorf("dns: svcbdohpath: %w", err)
}
s.Template = string(template)
return nil
}
func (s *SVCBDoHPath) copy() SVCBKeyValue {
return &SVCBDoHPath{
Template: s.Template,
}
}
// SVCBLocal pair is intended for experimental/private use. The key is recommended
// to be in the range [SVCB_PRIVATE_LOWER, SVCB_PRIVATE_UPPER].
// Basic use pattern for creating a keyNNNNN option:
@ -661,6 +830,7 @@ type SVCBLocal struct {
}
func (s *SVCBLocal) Key() SVCBKey { return s.KeyCode }
func (s *SVCBLocal) String() string { return svcbParamToStr(s.Data) }
func (s *SVCBLocal) pack() ([]byte, error) { return append([]byte(nil), s.Data...), nil }
func (s *SVCBLocal) len() int { return len(s.Data) }
@ -669,50 +839,10 @@ func (s *SVCBLocal) unpack(b []byte) error {
return nil
}
func (s *SVCBLocal) String() string {
var str strings.Builder
str.Grow(4 * len(s.Data))
for _, e := range s.Data {
if ' ' <= e && e <= '~' {
switch e {
case '"', ';', ' ', '\\':
str.WriteByte('\\')
str.WriteByte(e)
default:
str.WriteByte(e)
}
} else {
str.WriteString(escapeByte(e))
}
}
return str.String()
}
func (s *SVCBLocal) parse(b string) error {
data := make([]byte, 0, len(b))
for i := 0; i < len(b); {
if b[i] != '\\' {
data = append(data, b[i])
i++
continue
}
if i+1 == len(b) {
return errors.New("dns: svcblocal: svcb private/experimental key escape unterminated")
}
if isDigit(b[i+1]) {
if i+3 < len(b) && isDigit(b[i+2]) && isDigit(b[i+3]) {
a, err := strconv.ParseUint(b[i+1:i+4], 10, 8)
if err == nil {
i += 4
data = append(data, byte(a))
continue
}
}
return errors.New("dns: svcblocal: svcb private/experimental key bad escaped octet")
} else {
data = append(data, b[i+1])
i += 2
}
data, err := svcbParseParam(b)
if err != nil {
return fmt.Errorf("dns: svcblocal: svcb private/experimental key %w", err)
}
s.Data = data
return nil
@ -753,3 +883,53 @@ func areSVCBPairArraysEqual(a []SVCBKeyValue, b []SVCBKeyValue) bool {
}
return true
}
// svcbParamStr converts the value of an SVCB parameter into a DNS presentation-format string.
func svcbParamToStr(s []byte) string {
var str strings.Builder
str.Grow(4 * len(s))
for _, e := range s {
if ' ' <= e && e <= '~' {
switch e {
case '"', ';', ' ', '\\':
str.WriteByte('\\')
str.WriteByte(e)
default:
str.WriteByte(e)
}
} else {
str.WriteString(escapeByte(e))
}
}
return str.String()
}
// svcbParseParam parses a DNS presentation-format string into an SVCB parameter value.
func svcbParseParam(b string) ([]byte, error) {
data := make([]byte, 0, len(b))
for i := 0; i < len(b); {
if b[i] != '\\' {
data = append(data, b[i])
i++
continue
}
if i+1 == len(b) {
return nil, errors.New("escape unterminated")
}
if isDigit(b[i+1]) {
if i+3 < len(b) && isDigit(b[i+2]) && isDigit(b[i+3]) {
a, err := strconv.ParseUint(b[i+1:i+4], 10, 8)
if err == nil {
i += 4
data = append(data, byte(a))
continue
}
}
return nil, errors.New("bad escaped octet")
} else {
data = append(data, b[i+1])
i += 2
}
}
return data, nil
}

26
vendor/github.com/miekg/dns/tsig.go generated vendored
View File

@ -158,18 +158,17 @@ type timerWireFmt struct {
}
// TsigGenerate fills out the TSIG record attached to the message.
// The message should contain
// a "stub" TSIG RR with the algorithm, key name (owner name of the RR),
// time fudge (defaults to 300 seconds) and the current time
// The TSIG MAC is saved in that Tsig RR.
// When TsigGenerate is called for the first time requestMAC is set to the empty string and
// timersOnly is false.
// If something goes wrong an error is returned, otherwise it is nil.
// The message should contain a "stub" TSIG RR with the algorithm, key name
// (owner name of the RR), time fudge (defaults to 300 seconds) and the current
// time The TSIG MAC is saved in that Tsig RR. When TsigGenerate is called for
// the first time requestMAC should be set to the empty string and timersOnly to
// false.
func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
return tsigGenerateProvider(m, tsigHMACProvider(secret), requestMAC, timersOnly)
return TsigGenerateWithProvider(m, tsigHMACProvider(secret), requestMAC, timersOnly)
}
func tsigGenerateProvider(m *Msg, provider TsigProvider, requestMAC string, timersOnly bool) ([]byte, string, error) {
// TsigGenerateWithProvider is similar to TsigGenerate, but allows for a custom TsigProvider.
func TsigGenerateWithProvider(m *Msg, provider TsigProvider, requestMAC string, timersOnly bool) ([]byte, string, error) {
if m.IsTsig() == nil {
panic("dns: TSIG not last RR in additional")
}
@ -216,14 +215,15 @@ func tsigGenerateProvider(m *Msg, provider TsigProvider, requestMAC string, time
return mbuf, t.MAC, nil
}
// TsigVerify verifies the TSIG on a message.
// If the signature does not validate err contains the
// error, otherwise it is nil.
// TsigVerify verifies the TSIG on a message. If the signature does not
// validate the returned error contains the cause. If the signature is OK, the
// error is nil.
func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
return tsigVerify(msg, tsigHMACProvider(secret), requestMAC, timersOnly, uint64(time.Now().Unix()))
}
func tsigVerifyProvider(msg []byte, provider TsigProvider, requestMAC string, timersOnly bool) error {
// TsigVerifyWithProvider is similar to TsigVerify, but allows for a custom TsigProvider.
func TsigVerifyWithProvider(msg []byte, provider TsigProvider, requestMAC string, timersOnly bool) error {
return tsigVerify(msg, provider, requestMAC, timersOnly, uint64(time.Now().Unix()))
}

View File

@ -3,7 +3,7 @@ package dns
import "fmt"
// Version is current version of this library.
var Version = v{1, 1, 46}
var Version = v{1, 1, 50}
// v holds the version of this library.
type v struct {

4
vendor/github.com/miekg/dns/xfr.go generated vendored
View File

@ -237,7 +237,7 @@ func (t *Transfer) ReadMsg() (*Msg, error) {
}
if ts, tp := m.IsTsig(), t.tsigProvider(); ts != nil && tp != nil {
// Need to work on the original message p, as that was used to calculate the tsig.
err = tsigVerifyProvider(p, tp, t.tsigRequestMAC, t.tsigTimersOnly)
err = TsigVerifyWithProvider(p, tp, t.tsigRequestMAC, t.tsigTimersOnly)
t.tsigRequestMAC = ts.MAC
}
return m, err
@ -247,7 +247,7 @@ func (t *Transfer) ReadMsg() (*Msg, error) {
func (t *Transfer) WriteMsg(m *Msg) (err error) {
var out []byte
if ts, tp := m.IsTsig(), t.tsigProvider(); ts != nil && tp != nil {
out, t.tsigRequestMAC, err = tsigGenerateProvider(m, tp, t.tsigRequestMAC, t.tsigTimersOnly)
out, t.tsigRequestMAC, err = TsigGenerateWithProvider(m, tp, t.tsigRequestMAC, t.tsigTimersOnly)
} else {
out, err = m.Pack()
}

View File

@ -1,5 +1,5 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: metrics.proto
// source: io/prometheus/client/metrics.proto
package io_prometheus_client
@ -24,11 +24,18 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type MetricType int32
const (
MetricType_COUNTER MetricType = 0
MetricType_GAUGE MetricType = 1
MetricType_SUMMARY MetricType = 2
MetricType_UNTYPED MetricType = 3
// COUNTER must use the Metric field "counter".
MetricType_COUNTER MetricType = 0
// GAUGE must use the Metric field "gauge".
MetricType_GAUGE MetricType = 1
// SUMMARY must use the Metric field "summary".
MetricType_SUMMARY MetricType = 2
// UNTYPED must use the Metric field "untyped".
MetricType_UNTYPED MetricType = 3
// HISTOGRAM must use the Metric field "histogram".
MetricType_HISTOGRAM MetricType = 4
// GAUGE_HISTOGRAM must use the Metric field "histogram".
MetricType_GAUGE_HISTOGRAM MetricType = 5
)
var MetricType_name = map[int32]string{
@ -37,14 +44,16 @@ var MetricType_name = map[int32]string{
2: "SUMMARY",
3: "UNTYPED",
4: "HISTOGRAM",
5: "GAUGE_HISTOGRAM",
}
var MetricType_value = map[string]int32{
"COUNTER": 0,
"GAUGE": 1,
"SUMMARY": 2,
"UNTYPED": 3,
"HISTOGRAM": 4,
"COUNTER": 0,
"GAUGE": 1,
"SUMMARY": 2,
"UNTYPED": 3,
"HISTOGRAM": 4,
"GAUGE_HISTOGRAM": 5,
}
func (x MetricType) Enum() *MetricType {
@ -67,7 +76,7 @@ func (x *MetricType) UnmarshalJSON(data []byte) error {
}
func (MetricType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_6039342a2ba47b72, []int{0}
return fileDescriptor_d1e5ddb18987a258, []int{0}
}
type LabelPair struct {
@ -82,7 +91,7 @@ func (m *LabelPair) Reset() { *m = LabelPair{} }
func (m *LabelPair) String() string { return proto.CompactTextString(m) }
func (*LabelPair) ProtoMessage() {}
func (*LabelPair) Descriptor() ([]byte, []int) {
return fileDescriptor_6039342a2ba47b72, []int{0}
return fileDescriptor_d1e5ddb18987a258, []int{0}
}
func (m *LabelPair) XXX_Unmarshal(b []byte) error {
@ -128,7 +137,7 @@ func (m *Gauge) Reset() { *m = Gauge{} }
func (m *Gauge) String() string { return proto.CompactTextString(m) }
func (*Gauge) ProtoMessage() {}
func (*Gauge) Descriptor() ([]byte, []int) {
return fileDescriptor_6039342a2ba47b72, []int{1}
return fileDescriptor_d1e5ddb18987a258, []int{1}
}
func (m *Gauge) XXX_Unmarshal(b []byte) error {
@ -168,7 +177,7 @@ func (m *Counter) Reset() { *m = Counter{} }
func (m *Counter) String() string { return proto.CompactTextString(m) }
func (*Counter) ProtoMessage() {}
func (*Counter) Descriptor() ([]byte, []int) {
return fileDescriptor_6039342a2ba47b72, []int{2}
return fileDescriptor_d1e5ddb18987a258, []int{2}
}
func (m *Counter) XXX_Unmarshal(b []byte) error {
@ -215,7 +224,7 @@ func (m *Quantile) Reset() { *m = Quantile{} }
func (m *Quantile) String() string { return proto.CompactTextString(m) }
func (*Quantile) ProtoMessage() {}
func (*Quantile) Descriptor() ([]byte, []int) {
return fileDescriptor_6039342a2ba47b72, []int{3}
return fileDescriptor_d1e5ddb18987a258, []int{3}
}
func (m *Quantile) XXX_Unmarshal(b []byte) error {
@ -263,7 +272,7 @@ func (m *Summary) Reset() { *m = Summary{} }
func (m *Summary) String() string { return proto.CompactTextString(m) }
func (*Summary) ProtoMessage() {}
func (*Summary) Descriptor() ([]byte, []int) {
return fileDescriptor_6039342a2ba47b72, []int{4}
return fileDescriptor_d1e5ddb18987a258, []int{4}
}
func (m *Summary) XXX_Unmarshal(b []byte) error {
@ -316,7 +325,7 @@ func (m *Untyped) Reset() { *m = Untyped{} }
func (m *Untyped) String() string { return proto.CompactTextString(m) }
func (*Untyped) ProtoMessage() {}
func (*Untyped) Descriptor() ([]byte, []int) {
return fileDescriptor_6039342a2ba47b72, []int{5}
return fileDescriptor_d1e5ddb18987a258, []int{5}
}
func (m *Untyped) XXX_Unmarshal(b []byte) error {
@ -345,9 +354,34 @@ func (m *Untyped) GetValue() float64 {
}
type Histogram struct {
SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count,json=sampleCount" json:"sample_count,omitempty"`
SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum,json=sampleSum" json:"sample_sum,omitempty"`
Bucket []*Bucket `protobuf:"bytes,3,rep,name=bucket" json:"bucket,omitempty"`
SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count,json=sampleCount" json:"sample_count,omitempty"`
SampleCountFloat *float64 `protobuf:"fixed64,4,opt,name=sample_count_float,json=sampleCountFloat" json:"sample_count_float,omitempty"`
SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum,json=sampleSum" json:"sample_sum,omitempty"`
// Buckets for the conventional histogram.
Bucket []*Bucket `protobuf:"bytes,3,rep,name=bucket" json:"bucket,omitempty"`
// schema defines the bucket schema. Currently, valid numbers are -4 <= n <= 8.
// They are all for base-2 bucket schemas, where 1 is a bucket boundary in each case, and
// then each power of two is divided into 2^n logarithmic buckets.
// Or in other words, each bucket boundary is the previous boundary times 2^(2^-n).
// In the future, more bucket schemas may be added using numbers < -4 or > 8.
Schema *int32 `protobuf:"zigzag32,5,opt,name=schema" json:"schema,omitempty"`
ZeroThreshold *float64 `protobuf:"fixed64,6,opt,name=zero_threshold,json=zeroThreshold" json:"zero_threshold,omitempty"`
ZeroCount *uint64 `protobuf:"varint,7,opt,name=zero_count,json=zeroCount" json:"zero_count,omitempty"`
ZeroCountFloat *float64 `protobuf:"fixed64,8,opt,name=zero_count_float,json=zeroCountFloat" json:"zero_count_float,omitempty"`
// Negative buckets for the native histogram.
NegativeSpan []*BucketSpan `protobuf:"bytes,9,rep,name=negative_span,json=negativeSpan" json:"negative_span,omitempty"`
// Use either "negative_delta" or "negative_count", the former for
// regular histograms with integer counts, the latter for float
// histograms.
NegativeDelta []int64 `protobuf:"zigzag64,10,rep,name=negative_delta,json=negativeDelta" json:"negative_delta,omitempty"`
NegativeCount []float64 `protobuf:"fixed64,11,rep,name=negative_count,json=negativeCount" json:"negative_count,omitempty"`
// Positive buckets for the native histogram.
PositiveSpan []*BucketSpan `protobuf:"bytes,12,rep,name=positive_span,json=positiveSpan" json:"positive_span,omitempty"`
// Use either "positive_delta" or "positive_count", the former for
// regular histograms with integer counts, the latter for float
// histograms.
PositiveDelta []int64 `protobuf:"zigzag64,13,rep,name=positive_delta,json=positiveDelta" json:"positive_delta,omitempty"`
PositiveCount []float64 `protobuf:"fixed64,14,rep,name=positive_count,json=positiveCount" json:"positive_count,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -357,7 +391,7 @@ func (m *Histogram) Reset() { *m = Histogram{} }
func (m *Histogram) String() string { return proto.CompactTextString(m) }
func (*Histogram) ProtoMessage() {}
func (*Histogram) Descriptor() ([]byte, []int) {
return fileDescriptor_6039342a2ba47b72, []int{6}
return fileDescriptor_d1e5ddb18987a258, []int{6}
}
func (m *Histogram) XXX_Unmarshal(b []byte) error {
@ -385,6 +419,13 @@ func (m *Histogram) GetSampleCount() uint64 {
return 0
}
func (m *Histogram) GetSampleCountFloat() float64 {
if m != nil && m.SampleCountFloat != nil {
return *m.SampleCountFloat
}
return 0
}
func (m *Histogram) GetSampleSum() float64 {
if m != nil && m.SampleSum != nil {
return *m.SampleSum
@ -399,8 +440,81 @@ func (m *Histogram) GetBucket() []*Bucket {
return nil
}
func (m *Histogram) GetSchema() int32 {
if m != nil && m.Schema != nil {
return *m.Schema
}
return 0
}
func (m *Histogram) GetZeroThreshold() float64 {
if m != nil && m.ZeroThreshold != nil {
return *m.ZeroThreshold
}
return 0
}
func (m *Histogram) GetZeroCount() uint64 {
if m != nil && m.ZeroCount != nil {
return *m.ZeroCount
}
return 0
}
func (m *Histogram) GetZeroCountFloat() float64 {
if m != nil && m.ZeroCountFloat != nil {
return *m.ZeroCountFloat
}
return 0
}
func (m *Histogram) GetNegativeSpan() []*BucketSpan {
if m != nil {
return m.NegativeSpan
}
return nil
}
func (m *Histogram) GetNegativeDelta() []int64 {
if m != nil {
return m.NegativeDelta
}
return nil
}
func (m *Histogram) GetNegativeCount() []float64 {
if m != nil {
return m.NegativeCount
}
return nil
}
func (m *Histogram) GetPositiveSpan() []*BucketSpan {
if m != nil {
return m.PositiveSpan
}
return nil
}
func (m *Histogram) GetPositiveDelta() []int64 {
if m != nil {
return m.PositiveDelta
}
return nil
}
func (m *Histogram) GetPositiveCount() []float64 {
if m != nil {
return m.PositiveCount
}
return nil
}
// A Bucket of a conventional histogram, each of which is treated as
// an individual counter-like time series by Prometheus.
type Bucket struct {
CumulativeCount *uint64 `protobuf:"varint,1,opt,name=cumulative_count,json=cumulativeCount" json:"cumulative_count,omitempty"`
CumulativeCountFloat *float64 `protobuf:"fixed64,4,opt,name=cumulative_count_float,json=cumulativeCountFloat" json:"cumulative_count_float,omitempty"`
UpperBound *float64 `protobuf:"fixed64,2,opt,name=upper_bound,json=upperBound" json:"upper_bound,omitempty"`
Exemplar *Exemplar `protobuf:"bytes,3,opt,name=exemplar" json:"exemplar,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -412,7 +526,7 @@ func (m *Bucket) Reset() { *m = Bucket{} }
func (m *Bucket) String() string { return proto.CompactTextString(m) }
func (*Bucket) ProtoMessage() {}
func (*Bucket) Descriptor() ([]byte, []int) {
return fileDescriptor_6039342a2ba47b72, []int{7}
return fileDescriptor_d1e5ddb18987a258, []int{7}
}
func (m *Bucket) XXX_Unmarshal(b []byte) error {
@ -440,6 +554,13 @@ func (m *Bucket) GetCumulativeCount() uint64 {
return 0
}
func (m *Bucket) GetCumulativeCountFloat() float64 {
if m != nil && m.CumulativeCountFloat != nil {
return *m.CumulativeCountFloat
}
return 0
}
func (m *Bucket) GetUpperBound() float64 {
if m != nil && m.UpperBound != nil {
return *m.UpperBound
@ -454,6 +575,59 @@ func (m *Bucket) GetExemplar() *Exemplar {
return nil
}
// A BucketSpan defines a number of consecutive buckets in a native
// histogram with their offset. Logically, it would be more
// straightforward to include the bucket counts in the Span. However,
// the protobuf representation is more compact in the way the data is
// structured here (with all the buckets in a single array separate
// from the Spans).
type BucketSpan struct {
Offset *int32 `protobuf:"zigzag32,1,opt,name=offset" json:"offset,omitempty"`
Length *uint32 `protobuf:"varint,2,opt,name=length" json:"length,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *BucketSpan) Reset() { *m = BucketSpan{} }
func (m *BucketSpan) String() string { return proto.CompactTextString(m) }
func (*BucketSpan) ProtoMessage() {}
func (*BucketSpan) Descriptor() ([]byte, []int) {
return fileDescriptor_d1e5ddb18987a258, []int{8}
}
func (m *BucketSpan) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BucketSpan.Unmarshal(m, b)
}
func (m *BucketSpan) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_BucketSpan.Marshal(b, m, deterministic)
}
func (m *BucketSpan) XXX_Merge(src proto.Message) {
xxx_messageInfo_BucketSpan.Merge(m, src)
}
func (m *BucketSpan) XXX_Size() int {
return xxx_messageInfo_BucketSpan.Size(m)
}
func (m *BucketSpan) XXX_DiscardUnknown() {
xxx_messageInfo_BucketSpan.DiscardUnknown(m)
}
var xxx_messageInfo_BucketSpan proto.InternalMessageInfo
func (m *BucketSpan) GetOffset() int32 {
if m != nil && m.Offset != nil {
return *m.Offset
}
return 0
}
func (m *BucketSpan) GetLength() uint32 {
if m != nil && m.Length != nil {
return *m.Length
}
return 0
}
type Exemplar struct {
Label []*LabelPair `protobuf:"bytes,1,rep,name=label" json:"label,omitempty"`
Value *float64 `protobuf:"fixed64,2,opt,name=value" json:"value,omitempty"`
@ -467,7 +641,7 @@ func (m *Exemplar) Reset() { *m = Exemplar{} }
func (m *Exemplar) String() string { return proto.CompactTextString(m) }
func (*Exemplar) ProtoMessage() {}
func (*Exemplar) Descriptor() ([]byte, []int) {
return fileDescriptor_6039342a2ba47b72, []int{8}
return fileDescriptor_d1e5ddb18987a258, []int{9}
}
func (m *Exemplar) XXX_Unmarshal(b []byte) error {
@ -526,7 +700,7 @@ func (m *Metric) Reset() { *m = Metric{} }
func (m *Metric) String() string { return proto.CompactTextString(m) }
func (*Metric) ProtoMessage() {}
func (*Metric) Descriptor() ([]byte, []int) {
return fileDescriptor_6039342a2ba47b72, []int{9}
return fileDescriptor_d1e5ddb18987a258, []int{10}
}
func (m *Metric) XXX_Unmarshal(b []byte) error {
@ -610,7 +784,7 @@ func (m *MetricFamily) Reset() { *m = MetricFamily{} }
func (m *MetricFamily) String() string { return proto.CompactTextString(m) }
func (*MetricFamily) ProtoMessage() {}
func (*MetricFamily) Descriptor() ([]byte, []int) {
return fileDescriptor_6039342a2ba47b72, []int{10}
return fileDescriptor_d1e5ddb18987a258, []int{11}
}
func (m *MetricFamily) XXX_Unmarshal(b []byte) error {
@ -669,55 +843,72 @@ func init() {
proto.RegisterType((*Untyped)(nil), "io.prometheus.client.Untyped")
proto.RegisterType((*Histogram)(nil), "io.prometheus.client.Histogram")
proto.RegisterType((*Bucket)(nil), "io.prometheus.client.Bucket")
proto.RegisterType((*BucketSpan)(nil), "io.prometheus.client.BucketSpan")
proto.RegisterType((*Exemplar)(nil), "io.prometheus.client.Exemplar")
proto.RegisterType((*Metric)(nil), "io.prometheus.client.Metric")
proto.RegisterType((*MetricFamily)(nil), "io.prometheus.client.MetricFamily")
}
func init() { proto.RegisterFile("metrics.proto", fileDescriptor_6039342a2ba47b72) }
var fileDescriptor_6039342a2ba47b72 = []byte{
// 665 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xcd, 0x6e, 0xd3, 0x4c,
0x14, 0xfd, 0xdc, 0x38, 0x3f, 0xbe, 0x69, 0x3f, 0xa2, 0x51, 0x17, 0x56, 0xa1, 0x24, 0x78, 0x55,
0x58, 0x38, 0xa2, 0x6a, 0x05, 0x2a, 0xb0, 0x68, 0x4b, 0x48, 0x91, 0x48, 0x5b, 0x26, 0xc9, 0xa2,
0xb0, 0x88, 0x1c, 0x77, 0x70, 0x2c, 0x3c, 0xb1, 0xb1, 0x67, 0x2a, 0xb2, 0x66, 0xc1, 0x16, 0x5e,
0x81, 0x17, 0x05, 0xcd, 0x8f, 0x6d, 0x2a, 0xb9, 0x95, 0x40, 0xec, 0x66, 0xee, 0x3d, 0xe7, 0xfa,
0xcc, 0xf8, 0x9c, 0x81, 0x0d, 0x4a, 0x58, 0x1a, 0xfa, 0x99, 0x9b, 0xa4, 0x31, 0x8b, 0xd1, 0x66,
0x18, 0x8b, 0x15, 0x25, 0x6c, 0x41, 0x78, 0xe6, 0xfa, 0x51, 0x48, 0x96, 0x6c, 0xab, 0x1b, 0xc4,
0x71, 0x10, 0x91, 0xbe, 0xc4, 0xcc, 0xf9, 0x87, 0x3e, 0x0b, 0x29, 0xc9, 0x98, 0x47, 0x13, 0x45,
0x73, 0xf6, 0xc1, 0x7a, 0xe3, 0xcd, 0x49, 0x74, 0xee, 0x85, 0x29, 0x42, 0x60, 0x2e, 0x3d, 0x4a,
0x6c, 0xa3, 0x67, 0xec, 0x58, 0x58, 0xae, 0xd1, 0x26, 0xd4, 0xaf, 0xbc, 0x88, 0x13, 0x7b, 0x4d,
0x16, 0xd5, 0xc6, 0xd9, 0x86, 0xfa, 0xd0, 0xe3, 0xc1, 0x6f, 0x6d, 0xc1, 0x31, 0xf2, 0xf6, 0x7b,
0x68, 0x1e, 0xc7, 0x7c, 0xc9, 0x48, 0x5a, 0x0d, 0x40, 0x07, 0xd0, 0x22, 0x9f, 0x09, 0x4d, 0x22,
0x2f, 0x95, 0x83, 0xdb, 0xbb, 0xf7, 0xdd, 0xaa, 0x03, 0xb8, 0x03, 0x8d, 0xc2, 0x05, 0xde, 0x79,
0x0e, 0xad, 0xb7, 0xdc, 0x5b, 0xb2, 0x30, 0x22, 0x68, 0x0b, 0x5a, 0x9f, 0xf4, 0x5a, 0x7f, 0xa0,
0xd8, 0x5f, 0x57, 0x5e, 0x48, 0xfb, 0x6a, 0x40, 0x73, 0xcc, 0x29, 0xf5, 0xd2, 0x15, 0x7a, 0x00,
0xeb, 0x99, 0x47, 0x93, 0x88, 0xcc, 0x7c, 0xa1, 0x56, 0x4e, 0x30, 0x71, 0x5b, 0xd5, 0xe4, 0x01,
0xd0, 0x36, 0x80, 0x86, 0x64, 0x9c, 0xea, 0x49, 0x96, 0xaa, 0x8c, 0x39, 0x15, 0xe7, 0x28, 0xbe,
0x5f, 0xeb, 0xd5, 0x6e, 0x3e, 0x47, 0xae, 0xb8, 0xd4, 0xe7, 0x74, 0xa1, 0x39, 0x5d, 0xb2, 0x55,
0x42, 0x2e, 0x6f, 0xb8, 0xc5, 0x2f, 0x06, 0x58, 0x27, 0x61, 0xc6, 0xe2, 0x20, 0xf5, 0xe8, 0x3f,
0x10, 0xbb, 0x07, 0x8d, 0x39, 0xf7, 0x3f, 0x12, 0xa6, 0xa5, 0xde, 0xab, 0x96, 0x7a, 0x24, 0x31,
0x58, 0x63, 0x9d, 0x6f, 0x06, 0x34, 0x54, 0x09, 0x3d, 0x84, 0x8e, 0xcf, 0x29, 0x8f, 0x3c, 0x16,
0x5e, 0x5d, 0x97, 0x71, 0xa7, 0xac, 0x2b, 0x29, 0x5d, 0x68, 0xf3, 0x24, 0x21, 0xe9, 0x6c, 0x1e,
0xf3, 0xe5, 0xa5, 0xd6, 0x02, 0xb2, 0x74, 0x24, 0x2a, 0xd7, 0x1c, 0x50, 0xfb, 0x43, 0x07, 0x7c,
0x37, 0xa0, 0x95, 0x97, 0xd1, 0x3e, 0xd4, 0x23, 0xe1, 0x60, 0xdb, 0x90, 0x87, 0xea, 0x56, 0x4f,
0x29, 0x4c, 0x8e, 0x15, 0xba, 0xda, 0x1d, 0xe8, 0x29, 0x58, 0x45, 0x42, 0xb4, 0xac, 0x2d, 0x57,
0x65, 0xc8, 0xcd, 0x33, 0xe4, 0x4e, 0x72, 0x04, 0x2e, 0xc1, 0xce, 0xcf, 0x35, 0x68, 0x8c, 0x64,
0x22, 0xff, 0x56, 0xd1, 0x63, 0xa8, 0x07, 0x22, 0x53, 0x3a, 0x10, 0x77, 0xab, 0x69, 0x32, 0x76,
0x58, 0x21, 0xd1, 0x13, 0x68, 0xfa, 0x2a, 0x67, 0x5a, 0xec, 0x76, 0x35, 0x49, 0x87, 0x11, 0xe7,
0x68, 0x41, 0xcc, 0x54, 0x08, 0x6c, 0xf3, 0x36, 0xa2, 0x4e, 0x0a, 0xce, 0xd1, 0x82, 0xc8, 0x95,
0x69, 0xed, 0xfa, 0x6d, 0x44, 0xed, 0x6c, 0x9c, 0xa3, 0xd1, 0x0b, 0xb0, 0x16, 0xb9, 0x97, 0xed,
0xa6, 0xa4, 0xde, 0x70, 0x31, 0x85, 0xe5, 0x71, 0xc9, 0x10, 0xee, 0x2f, 0xee, 0x7a, 0x46, 0x33,
0xbb, 0xd1, 0x33, 0x76, 0x6a, 0xb8, 0x5d, 0xd4, 0x46, 0x99, 0xf3, 0xc3, 0x80, 0x75, 0xf5, 0x07,
0x5e, 0x79, 0x34, 0x8c, 0x56, 0x95, 0xcf, 0x19, 0x02, 0x73, 0x41, 0xa2, 0x44, 0xbf, 0x66, 0x72,
0x8d, 0xf6, 0xc0, 0x14, 0x1a, 0xe5, 0x15, 0xfe, 0xbf, 0xdb, 0xab, 0x56, 0xa5, 0x26, 0x4f, 0x56,
0x09, 0xc1, 0x12, 0x2d, 0xd2, 0xa4, 0x5e, 0x60, 0xdb, 0xbc, 0x2d, 0x4d, 0x8a, 0x87, 0x35, 0xf6,
0xd1, 0x08, 0xa0, 0x9c, 0x84, 0xda, 0xd0, 0x3c, 0x3e, 0x9b, 0x9e, 0x4e, 0x06, 0xb8, 0xf3, 0x1f,
0xb2, 0xa0, 0x3e, 0x3c, 0x9c, 0x0e, 0x07, 0x1d, 0x43, 0xd4, 0xc7, 0xd3, 0xd1, 0xe8, 0x10, 0x5f,
0x74, 0xd6, 0xc4, 0x66, 0x7a, 0x3a, 0xb9, 0x38, 0x1f, 0xbc, 0xec, 0xd4, 0xd0, 0x06, 0x58, 0x27,
0xaf, 0xc7, 0x93, 0xb3, 0x21, 0x3e, 0x1c, 0x75, 0xcc, 0x23, 0x0c, 0x95, 0xef, 0xfe, 0xbb, 0x83,
0x20, 0x64, 0x0b, 0x3e, 0x77, 0xfd, 0x98, 0xf6, 0xcb, 0x6e, 0x5f, 0x75, 0x67, 0x34, 0xbe, 0x24,
0x51, 0x3f, 0x88, 0x9f, 0x85, 0xf1, 0xac, 0xec, 0xce, 0x54, 0xf7, 0x57, 0x00, 0x00, 0x00, 0xff,
0xff, 0xd0, 0x84, 0x91, 0x73, 0x59, 0x06, 0x00, 0x00,
func init() {
proto.RegisterFile("io/prometheus/client/metrics.proto", fileDescriptor_d1e5ddb18987a258)
}
var fileDescriptor_d1e5ddb18987a258 = []byte{
// 896 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xdd, 0x8e, 0xdb, 0x44,
0x18, 0xc5, 0x9b, 0x5f, 0x7f, 0xd9, 0x6c, 0xd3, 0x61, 0x55, 0x59, 0x0b, 0xcb, 0x06, 0x4b, 0x48,
0x0b, 0x42, 0x8e, 0x40, 0x5b, 0x81, 0x0a, 0x5c, 0xec, 0xb6, 0xe9, 0x16, 0x89, 0xb4, 0x65, 0x92,
0x5c, 0x14, 0x2e, 0xac, 0x49, 0x32, 0xeb, 0x58, 0x78, 0x3c, 0xc6, 0x1e, 0x57, 0x2c, 0x2f, 0xc0,
0x35, 0xaf, 0xc0, 0xc3, 0xf0, 0x22, 0x3c, 0x08, 0x68, 0xfe, 0xec, 0xdd, 0xe2, 0x94, 0xd2, 0x3b,
0x7f, 0x67, 0xce, 0xf7, 0xcd, 0x39, 0xe3, 0xc9, 0x71, 0xc0, 0x8f, 0xf9, 0x24, 0xcb, 0x39, 0xa3,
0x62, 0x4b, 0xcb, 0x62, 0xb2, 0x4e, 0x62, 0x9a, 0x8a, 0x09, 0xa3, 0x22, 0x8f, 0xd7, 0x45, 0x90,
0xe5, 0x5c, 0x70, 0x74, 0x18, 0xf3, 0xa0, 0xe6, 0x04, 0x9a, 0x73, 0x74, 0x12, 0x71, 0x1e, 0x25,
0x74, 0xa2, 0x38, 0xab, 0xf2, 0x6a, 0x22, 0x62, 0x46, 0x0b, 0x41, 0x58, 0xa6, 0xdb, 0xfc, 0xfb,
0xe0, 0x7e, 0x47, 0x56, 0x34, 0x79, 0x4e, 0xe2, 0x1c, 0x21, 0x68, 0xa7, 0x84, 0x51, 0xcf, 0x19,
0x3b, 0xa7, 0x2e, 0x56, 0xcf, 0xe8, 0x10, 0x3a, 0x2f, 0x49, 0x52, 0x52, 0x6f, 0x4f, 0x81, 0xba,
0xf0, 0x8f, 0xa1, 0x73, 0x49, 0xca, 0xe8, 0xc6, 0xb2, 0xec, 0x71, 0xec, 0xf2, 0x8f, 0xd0, 0x7b,
0xc8, 0xcb, 0x54, 0xd0, 0xbc, 0x99, 0x80, 0x1e, 0x40, 0x9f, 0xfe, 0x42, 0x59, 0x96, 0x90, 0x5c,
0x0d, 0x1e, 0x7c, 0xfe, 0x41, 0xd0, 0x64, 0x20, 0x98, 0x1a, 0x16, 0xae, 0xf8, 0xfe, 0xd7, 0xd0,
0xff, 0xbe, 0x24, 0xa9, 0x88, 0x13, 0x8a, 0x8e, 0xa0, 0xff, 0xb3, 0x79, 0x36, 0x1b, 0x54, 0xf5,
0x6d, 0xe5, 0x95, 0xb4, 0xdf, 0x1c, 0xe8, 0xcd, 0x4b, 0xc6, 0x48, 0x7e, 0x8d, 0x3e, 0x84, 0xfd,
0x82, 0xb0, 0x2c, 0xa1, 0xe1, 0x5a, 0xaa, 0x55, 0x13, 0xda, 0x78, 0xa0, 0x31, 0x65, 0x00, 0x1d,
0x03, 0x18, 0x4a, 0x51, 0x32, 0x33, 0xc9, 0xd5, 0xc8, 0xbc, 0x64, 0xd2, 0x47, 0xb5, 0x7f, 0x6b,
0xdc, 0xda, 0xed, 0xc3, 0x2a, 0xae, 0xf5, 0xf9, 0x27, 0xd0, 0x5b, 0xa6, 0xe2, 0x3a, 0xa3, 0x9b,
0x1d, 0xa7, 0xf8, 0x57, 0x1b, 0xdc, 0x27, 0x71, 0x21, 0x78, 0x94, 0x13, 0xf6, 0x26, 0x62, 0x3f,
0x05, 0x74, 0x93, 0x12, 0x5e, 0x25, 0x9c, 0x08, 0xaf, 0xad, 0x66, 0x8e, 0x6e, 0x10, 0x1f, 0x4b,
0xfc, 0xbf, 0xac, 0x9d, 0x41, 0x77, 0x55, 0xae, 0x7f, 0xa2, 0xc2, 0x18, 0x7b, 0xbf, 0xd9, 0xd8,
0x85, 0xe2, 0x60, 0xc3, 0x45, 0xf7, 0xa0, 0x5b, 0xac, 0xb7, 0x94, 0x11, 0xaf, 0x33, 0x76, 0x4e,
0xef, 0x62, 0x53, 0xa1, 0x8f, 0xe0, 0xe0, 0x57, 0x9a, 0xf3, 0x50, 0x6c, 0x73, 0x5a, 0x6c, 0x79,
0xb2, 0xf1, 0xba, 0x6a, 0xc3, 0xa1, 0x44, 0x17, 0x16, 0x94, 0x9a, 0x14, 0x4d, 0x5b, 0xec, 0x29,
0x8b, 0xae, 0x44, 0xb4, 0xc1, 0x53, 0x18, 0xd5, 0xcb, 0xc6, 0x5e, 0x5f, 0xcd, 0x39, 0xa8, 0x48,
0xda, 0xdc, 0x14, 0x86, 0x29, 0x8d, 0x88, 0x88, 0x5f, 0xd2, 0xb0, 0xc8, 0x48, 0xea, 0xb9, 0xca,
0xc4, 0xf8, 0x75, 0x26, 0xe6, 0x19, 0x49, 0xf1, 0xbe, 0x6d, 0x93, 0x95, 0x94, 0x5d, 0x8d, 0xd9,
0xd0, 0x44, 0x10, 0x0f, 0xc6, 0xad, 0x53, 0x84, 0xab, 0xe1, 0x8f, 0x24, 0x78, 0x8b, 0xa6, 0xa5,
0x0f, 0xc6, 0x2d, 0xe9, 0xce, 0xa2, 0x5a, 0xfe, 0x14, 0x86, 0x19, 0x2f, 0xe2, 0x5a, 0xd4, 0xfe,
0x9b, 0x8a, 0xb2, 0x6d, 0x56, 0x54, 0x35, 0x46, 0x8b, 0x1a, 0x6a, 0x51, 0x16, 0xad, 0x44, 0x55,
0x34, 0x2d, 0xea, 0x40, 0x8b, 0xb2, 0xa8, 0x12, 0xe5, 0xff, 0xe9, 0x40, 0x57, 0x6f, 0x85, 0x3e,
0x86, 0xd1, 0xba, 0x64, 0x65, 0x72, 0xd3, 0x88, 0xbe, 0x66, 0x77, 0x6a, 0x5c, 0x5b, 0x39, 0x83,
0x7b, 0xaf, 0x52, 0x6f, 0x5d, 0xb7, 0xc3, 0x57, 0x1a, 0xf4, 0x5b, 0x39, 0x81, 0x41, 0x99, 0x65,
0x34, 0x0f, 0x57, 0xbc, 0x4c, 0x37, 0xe6, 0xce, 0x81, 0x82, 0x2e, 0x24, 0x72, 0x2b, 0x17, 0x5a,
0xff, 0x3b, 0x17, 0xa0, 0x3e, 0x32, 0x79, 0x11, 0xf9, 0xd5, 0x55, 0x41, 0xb5, 0x83, 0xbb, 0xd8,
0x54, 0x12, 0x4f, 0x68, 0x1a, 0x89, 0xad, 0xda, 0x7d, 0x88, 0x4d, 0xe5, 0xff, 0xee, 0x40, 0xdf,
0x0e, 0x45, 0xf7, 0xa1, 0x93, 0xc8, 0x54, 0xf4, 0x1c, 0xf5, 0x82, 0x4e, 0x9a, 0x35, 0x54, 0xc1,
0x89, 0x35, 0xbb, 0x39, 0x71, 0xd0, 0x97, 0xe0, 0x56, 0xa9, 0x6b, 0x4c, 0x1d, 0x05, 0x3a, 0x97,
0x03, 0x9b, 0xcb, 0xc1, 0xc2, 0x32, 0x70, 0x4d, 0xf6, 0xff, 0xde, 0x83, 0xee, 0x4c, 0xa5, 0xfc,
0xdb, 0x2a, 0xfa, 0x0c, 0x3a, 0x91, 0xcc, 0x69, 0x13, 0xb2, 0xef, 0x35, 0xb7, 0xa9, 0x28, 0xc7,
0x9a, 0x89, 0xbe, 0x80, 0xde, 0x5a, 0x67, 0xb7, 0x11, 0x7b, 0xdc, 0xdc, 0x64, 0x02, 0x1e, 0x5b,
0xb6, 0x6c, 0x2c, 0x74, 0xb0, 0xaa, 0x3b, 0xb0, 0xb3, 0xd1, 0xa4, 0x2f, 0xb6, 0x6c, 0xd9, 0x58,
0xea, 0x20, 0x54, 0xa1, 0xb1, 0xb3, 0xd1, 0xa4, 0x25, 0xb6, 0x6c, 0xf4, 0x0d, 0xb8, 0x5b, 0x9b,
0x8f, 0x2a, 0x2c, 0x76, 0x1e, 0x4c, 0x15, 0xa3, 0xb8, 0xee, 0x90, 0x89, 0x5a, 0x9d, 0x75, 0xc8,
0x0a, 0x95, 0x48, 0x2d, 0x3c, 0xa8, 0xb0, 0x59, 0xe1, 0xff, 0xe1, 0xc0, 0xbe, 0x7e, 0x03, 0x8f,
0x09, 0x8b, 0x93, 0xeb, 0xc6, 0x4f, 0x24, 0x82, 0xf6, 0x96, 0x26, 0x99, 0xf9, 0x42, 0xaa, 0x67,
0x74, 0x06, 0x6d, 0xa9, 0x51, 0x1d, 0xe1, 0xc1, 0xae, 0x5f, 0xb8, 0x9e, 0xbc, 0xb8, 0xce, 0x28,
0x56, 0x6c, 0x99, 0xb9, 0xfa, 0xab, 0xee, 0xb5, 0x5f, 0x97, 0xb9, 0xba, 0x0f, 0x1b, 0xee, 0x27,
0x2b, 0x80, 0x7a, 0x12, 0x1a, 0x40, 0xef, 0xe1, 0xb3, 0xe5, 0xd3, 0xc5, 0x14, 0x8f, 0xde, 0x41,
0x2e, 0x74, 0x2e, 0xcf, 0x97, 0x97, 0xd3, 0x91, 0x23, 0xf1, 0xf9, 0x72, 0x36, 0x3b, 0xc7, 0x2f,
0x46, 0x7b, 0xb2, 0x58, 0x3e, 0x5d, 0xbc, 0x78, 0x3e, 0x7d, 0x34, 0x6a, 0xa1, 0x21, 0xb8, 0x4f,
0xbe, 0x9d, 0x2f, 0x9e, 0x5d, 0xe2, 0xf3, 0xd9, 0xa8, 0x8d, 0xde, 0x85, 0x3b, 0xaa, 0x27, 0xac,
0xc1, 0xce, 0x05, 0x86, 0xc6, 0x3f, 0x18, 0x3f, 0x3c, 0x88, 0x62, 0xb1, 0x2d, 0x57, 0xc1, 0x9a,
0xb3, 0x7f, 0xff, 0x45, 0x09, 0x19, 0xdf, 0xd0, 0x64, 0x12, 0xf1, 0xaf, 0x62, 0x1e, 0xd6, 0xab,
0xa1, 0x5e, 0xfd, 0x27, 0x00, 0x00, 0xff, 0xff, 0x16, 0x77, 0x81, 0x98, 0xd7, 0x08, 0x00, 0x00,
}

View File

@ -5,14 +5,78 @@ package disk
import (
"context"
"regexp"
"strings"
"golang.org/x/sys/unix"
"github.com/shirou/gopsutil/v3/internal/common"
)
var whiteSpaces = regexp.MustCompile(`\s+`)
var startBlank = regexp.MustCompile(`^\s+`)
var ignoreFSType = map[string]bool{"procfs": true}
var FSType = map[int]string{
0: "jfs2", 1: "namefs", 2: "nfs", 3: "jfs", 5: "cdrom", 6: "proc",
16: "special-fs", 17: "cache-fs", 18: "nfs3", 19: "automount-fs", 20: "pool-fs", 32: "vxfs",
33: "veritas-fs", 34: "udfs", 35: "nfs4", 36: "nfs4-pseudo", 37: "smbfs", 38: "mcr-pseudofs",
39: "ahafs", 40: "sterm-nfs", 41: "asmfs",
}
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
return []PartitionStat{}, common.ErrNotImplementedError
var ret []PartitionStat
out, err := invoke.CommandWithContext(ctx, "mount")
if err != nil {
return nil, err
}
// parse head lines for column names
colidx := make(map[string]int)
lines := strings.Split(string(out), "\n")
if len(lines) < 3 {
return nil, common.ErrNotImplementedError
}
idx := 0
start := 0
finished := false
for pos, ch := range lines[1] {
if ch == ' ' && ! finished {
name := strings.TrimSpace(lines[0][start:pos])
colidx[name] = idx
finished = true
} else if ch == '-' && finished {
idx++
start = pos
finished = false
}
}
name := strings.TrimSpace(lines[0][start:len(lines[1])])
colidx[name] = idx
for idx := 2; idx < len(lines); idx++ {
line := lines[idx]
if startBlank.MatchString(line) {
line = "localhost" + line
}
p := whiteSpaces.Split(lines[idx], 6)
if len(p) < 5 || ignoreFSType[p[colidx["vfs"]]] {
continue
}
d := PartitionStat{
Device: p[colidx["mounted"]],
Mountpoint: p[colidx["mounted over"]],
Fstype: p[colidx["vfs"]],
Opts: strings.Split(p[colidx["options"]], ","),
}
ret = append(ret, d)
}
return ret, nil
}
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
return nil, common.ErrNotImplementedError
func getFsType(stat unix.Statfs_t) string {
return FSType[int(stat.Vfstype)]
}

View File

@ -1,5 +1,5 @@
//go:build freebsd || linux || darwin
// +build freebsd linux darwin
//go:build freebsd || linux || darwin || (aix && !cgo)
// +build freebsd linux darwin aix,!cgo
package disk
@ -27,20 +27,8 @@ func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
InodesFree: (uint64(stat.Ffree)),
}
// if could not get InodesTotal, return empty
if ret.InodesTotal < ret.InodesFree {
return ret, nil
}
ret.InodesUsed = (ret.InodesTotal - ret.InodesFree)
ret.Used = (uint64(stat.Blocks) - uint64(stat.Bfree)) * uint64(bsize)
if ret.InodesTotal == 0 {
ret.InodesUsedPercent = 0
} else {
ret.InodesUsedPercent = (float64(ret.InodesUsed) / float64(ret.InodesTotal)) * 100.0
}
if (ret.Used + ret.Free) == 0 {
ret.UsedPercent = 0
} else {
@ -49,6 +37,19 @@ func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
ret.UsedPercent = (float64(ret.Used) / float64(ret.Used+ret.Free)) * 100.0
}
// if could not get InodesTotal, return empty
if ret.InodesTotal < ret.InodesFree {
return ret, nil
}
ret.InodesUsed = (ret.InodesTotal - ret.InodesFree)
if ret.InodesTotal == 0 {
ret.InodesUsedPercent = 0
} else {
ret.InodesUsedPercent = (float64(ret.InodesUsed) / float64(ret.InodesTotal)) * 100.0
}
return ret, nil
}

View File

@ -114,8 +114,8 @@ func ReadLines(filename string) ([]string, error) {
// ReadLinesOffsetN reads contents from file and splits them by new line.
// The offset tells at which line number to start.
// The count determines the number of lines to read (starting from offset):
// n >= 0: at most n lines
// n < 0: whole file
// n >= 0: at most n lines
// n < 0: whole file
func ReadLinesOffsetN(filename string, offset uint, n int) ([]string, error) {
f, err := os.Open(filename)
if err != nil {

View File

@ -149,6 +149,9 @@ func VirtualizationWithContext(ctx context.Context) (string, string, error) {
if StringsContains(contents, "kvm") {
system = "kvm"
role = "host"
} else if StringsContains(contents, "hv_util") {
system = "hyperv"
role = "guest"
} else if StringsContains(contents, "vboxdrv") {
system = "vbox"
role = "host"

View File

@ -161,7 +161,7 @@ var netProtocols = []string{
// If protocols is empty then all protocols are returned, otherwise
// just the protocols in the list are returned.
// Available protocols:
// ip,icmp,icmpmsg,tcp,udp,udplite
// [ip,icmp,icmpmsg,tcp,udp,udplite]
func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
return ProtoCountersWithContext(context.Background(), protocols)
}

View File

@ -408,7 +408,7 @@ func (p *Process) CwdWithContext(_ context.Context) (string, error) {
}
if userProcParams.CurrentDirectoryPathNameLength > 0 {
cwd := readProcessMemory(syscall.Handle(h), procIs32Bits, uint64(userProcParams.CurrentDirectoryPathAddress), uint(userProcParams.CurrentDirectoryPathNameLength))
if len(cwd) != int(userProcParams.CurrentDirectoryPathAddress) {
if len(cwd) != int(userProcParams.CurrentDirectoryPathNameLength) {
return "", errors.New("cannot read current working directory")
}

View File

@ -22,6 +22,7 @@ type Config struct {
InstanceName string
DeepLinking bool
PersistAuthorization bool
SyntaxHighlight bool
// The information for OAuth2 integration, if any.
OAuth *OAuthConfig
@ -54,6 +55,13 @@ func DeepLinking(deepLinking bool) func(*Config) {
}
}
// SyntaxHighlight true, false.
func SyntaxHighlight(syntaxHighlight bool) func(*Config) {
return func(c *Config) {
c.SyntaxHighlight = syntaxHighlight
}
}
// DocExpansion list, full, none.
func DocExpansion(docExpansion string) func(*Config) {
return func(c *Config) {
@ -97,6 +105,7 @@ func newConfig(configFns ...func(*Config)) *Config {
InstanceName: "swagger",
DeepLinking: true,
PersistAuthorization: false,
SyntaxHighlight: true,
}
for _, fn := range configFns {
@ -257,6 +266,7 @@ window.onload = function() {
// Build a system
const ui = SwaggerUIBundle({
url: "{{.URL}}",
syntaxHighlight: {{.SyntaxHighlight}},
deepLinking: {{.DeepLinking}},
docExpansion: "{{.DocExpansion}}",
persistAuthorization: {{.PersistAuthorization}},

View File

@ -52,12 +52,9 @@ Swag converts Go annotations to Swagger Documentation 2.0. We've created a varie
2. Download swag by using:
```sh
$ go get -u github.com/swaggo/swag/cmd/swag
# 1.16 or newer
$ go install github.com/swaggo/swag/cmd/swag@latest
```
To build from source you need [Go](https://golang.org/dl/) (1.15 or newer).
To build from source you need [Go](https://golang.org/dl/) (1.16 or newer).
Or download a pre-compiled binary from the [release page](https://github.com/swaggo/swag/releases).

View File

@ -47,13 +47,10 @@ Swag将Go的注释转换为Swagger2.0文档。我们为流行的 [Go Web Framewo
2. 使用如下命令下载swag
```bash
$ go get -u github.com/swaggo/swag/cmd/swag
# 1.16 及以上版本
$ go install github.com/swaggo/swag/cmd/swag@latest
```
从源码开始构建的话需要有Go环境1.15及以上版本)。
从源码开始构建的话需要有Go环境1.16及以上版本)。
或者从github的release页面下载预编译好的二进制文件。

View File

@ -2,21 +2,18 @@ package swag
import (
"bytes"
"crypto/md5"
"fmt"
"go/ast"
goparser "go/parser"
"go/token"
"io"
"log"
"os"
"regexp"
"sort"
"strings"
"text/tabwriter"
)
const splitTag = "&*"
// Check of @Param @Success @Failure @Response @Header
var specialTagForSplit = map[string]bool{
paramAttr: true,
@ -55,48 +52,93 @@ func (f *Formatter) Format(fileName string, contents []byte) ([]byte, error) {
if err != nil {
return nil, err
}
formattedComments := bytes.Buffer{}
oldComments := map[string]string{}
if ast.Comments != nil {
for _, comment := range ast.Comments {
formatFuncDoc(comment.List, &formattedComments, oldComments)
}
// Formatting changes are described as an edit list of byte range
// replacements. We make these content-level edits directly rather than
// changing the AST nodes and writing those out (via [go/printer] or
// [go/format]) so that we only change the formatting of Swag attribute
// comments. This won't touch the formatting of any other comments, or of
// functions, etc.
maxEdits := 0
for _, comment := range ast.Comments {
maxEdits += len(comment.List)
}
return formatComments(fileName, contents, formattedComments.Bytes(), oldComments), nil
edits := make(edits, 0, maxEdits)
for _, comment := range ast.Comments {
formatFuncDoc(fileSet, comment.List, &edits)
}
return edits.apply(contents), nil
}
func formatComments(fileName string, contents []byte, formattedComments []byte, oldComments map[string]string) []byte {
for _, comment := range bytes.Split(formattedComments, []byte("\n")) {
splits := bytes.SplitN(comment, []byte(splitTag), 2)
if len(splits) == 2 {
hash, line := splits[0], splits[1]
contents = bytes.Replace(contents, []byte(oldComments[string(hash)]), line, 1)
}
type edit struct {
begin int
end int
replacement []byte
}
type edits []edit
func (edits edits) apply(contents []byte) []byte {
// Apply the edits with the highest offset first, so that earlier edits
// don't affect the offsets of later edits.
sort.Slice(edits, func(i, j int) bool {
return edits[i].begin > edits[j].begin
})
for _, edit := range edits {
prefix := contents[:edit.begin]
suffix := contents[edit.end:]
contents = append(prefix, append(edit.replacement, suffix...)...)
}
return contents
}
func formatFuncDoc(commentList []*ast.Comment, formattedComments io.Writer, oldCommentsMap map[string]string) {
w := tabwriter.NewWriter(formattedComments, 0, 0, 1, ' ', 0)
// formatFuncDoc reformats the comment lines in commentList, and appends any
// changes to the edit list.
func formatFuncDoc(fileSet *token.FileSet, commentList []*ast.Comment, edits *edits) {
// Building the edit list to format a comment block is a two-step process.
// First, we iterate over each comment line looking for Swag attributes. In
// each one we find, we replace alignment whitespace with a tab character,
// then write the result into a tab writer.
for _, comment := range commentList {
linesToComments := make(map[int]int, len(commentList))
buffer := &bytes.Buffer{}
w := tabwriter.NewWriter(buffer, 0, 0, 1, ' ', 0)
for commentIndex, comment := range commentList {
text := comment.Text
if attr, body, found := swagComment(text); found {
cmd5 := fmt.Sprintf("%x", md5.Sum([]byte(text)))
oldCommentsMap[cmd5] = text
formatted := "// " + attr
if body != "" {
formatted += "\t" + splitComment2(attr, body)
}
// md5 + splitTag + srcCommentLine
// eg. xxx&*@Description get struct array
_, _ = fmt.Fprintln(w, cmd5+splitTag+formatted)
_, _ = fmt.Fprintln(w, formatted)
linesToComments[len(linesToComments)] = commentIndex
}
}
// format by tabwriter
// Once we've loaded all of the comment lines to be aligned into the tab
// writer, flushing it causes the aligned text to be written out to the
// backing buffer.
_ = w.Flush()
// Now the second step: we iterate over the aligned comment lines that were
// written into the backing buffer, pair each one up to its original
// comment line, and use the combination to describe the edit that needs to
// be made to the original input.
formattedComments := bytes.Split(buffer.Bytes(), []byte("\n"))
for lineIndex, commentIndex := range linesToComments {
comment := commentList[commentIndex]
*edits = append(*edits, edit{
begin: fileSet.Position(comment.Pos()).Offset,
end: fileSet.Position(comment.End()).Offset,
replacement: formattedComments[lineIndex],
})
}
}
func splitComment2(attr, body string) string {

View File

@ -6,10 +6,14 @@ package swag
import (
"errors"
"fmt"
"github.com/go-openapi/spec"
"go/ast"
"strings"
"sync"
"unicode"
)
var genericDefinitionsMutex = &sync.RWMutex{}
var genericsDefinitions = map[*TypeSpecDef]map[string]*TypeSpecDef{}
type genericTypeSpec struct {
@ -55,9 +59,12 @@ func typeSpecFullName(typeSpecDef *TypeSpecDef) string {
return fullName
}
func (pkgDefs *PackagesDefinitions) parametrizeStruct(original *TypeSpecDef, fullGenericForm string, parseDependency bool) *TypeSpecDef {
if spec, ok := genericsDefinitions[original][fullGenericForm]; ok {
return spec
func (pkgDefs *PackagesDefinitions) parametrizeGenericType(file *ast.File, original *TypeSpecDef, fullGenericForm string, parseDependency bool) *TypeSpecDef {
genericDefinitionsMutex.RLock()
tSpec, ok := genericsDefinitions[original][fullGenericForm]
genericDefinitionsMutex.RUnlock()
if ok {
return tSpec
}
pkgName := strings.Split(fullGenericForm, ".")[0]
@ -81,7 +88,11 @@ func (pkgDefs *PackagesDefinitions) parametrizeStruct(original *TypeSpecDef, ful
arrayDepth++
}
tdef := pkgDefs.FindTypeSpec(genericParam, original.File, parseDependency)
tdef := pkgDefs.FindTypeSpec(genericParam, file, parseDependency)
if tdef != nil && !strings.Contains(genericParam, ".") {
genericParam = fullTypeName(file.Name.Name, genericParam)
}
genericParamTypeDefs[original.TypeSpec.TypeParams.List[i].Names[0].Name] = &genericTypeSpec{
ArrayDepth: arrayDepth,
TypeSpec: tdef,
@ -132,31 +143,12 @@ func (pkgDefs *PackagesDefinitions) parametrizeStruct(original *TypeSpecDef, ful
ident.Name = string(IgnoreNameOverridePrefix) + ident.Name
parametrizedTypeSpec.TypeSpec.Name = ident
origStructType := original.TypeSpec.Type.(*ast.StructType)
newStructTypeDef := &ast.StructType{
Struct: origStructType.Struct,
Incomplete: origStructType.Incomplete,
Fields: &ast.FieldList{
Opening: origStructType.Fields.Opening,
Closing: origStructType.Fields.Closing,
},
}
newType := pkgDefs.resolveGenericType(original.File, original.TypeSpec.Type, genericParamTypeDefs, parseDependency)
for _, field := range origStructType.Fields.List {
newField := &ast.Field{
Doc: field.Doc,
Names: field.Names,
Tag: field.Tag,
Comment: field.Comment,
}
newField.Type = resolveType(field.Type, field, genericParamTypeDefs)
newStructTypeDef.Fields.List = append(newStructTypeDef.Fields.List, newField)
}
parametrizedTypeSpec.TypeSpec.Type = newStructTypeDef
genericDefinitionsMutex.Lock()
defer genericDefinitionsMutex.Unlock()
parametrizedTypeSpec.TypeSpec.Type = newType
if genericsDefinitions[original] == nil {
genericsDefinitions[original] = map[string]*TypeSpecDef{}
}
@ -166,12 +158,20 @@ func (pkgDefs *PackagesDefinitions) parametrizeStruct(original *TypeSpecDef, ful
// splitStructName splits a generic struct name in his parts
func splitStructName(fullGenericForm string) (string, []string) {
//remove all spaces character
fullGenericForm = strings.Map(func(r rune) rune {
if unicode.IsSpace(r) {
return -1
}
return r
}, fullGenericForm)
// split only at the first '[' and remove the last ']'
if fullGenericForm[len(fullGenericForm)-1] != ']' {
return "", nil
}
genericParams := strings.SplitN(strings.TrimSpace(fullGenericForm)[:len(fullGenericForm)-1], "[", 2)
genericParams := strings.SplitN(fullGenericForm[:len(fullGenericForm)-1], "[", 2)
if len(genericParams) == 1 {
return "", nil
}
@ -179,73 +179,121 @@ func splitStructName(fullGenericForm string) (string, []string) {
// generic type name
genericTypeName := genericParams[0]
// generic params
insideBrackets := 0
lastParam := ""
params := strings.Split(genericParams[1], ",")
genericParams = []string{}
for _, p := range params {
numOpened := strings.Count(p, "[")
numClosed := strings.Count(p, "]")
if numOpened == numClosed && insideBrackets == 0 {
genericParams = append(genericParams, strings.TrimSpace(p))
continue
}
insideBrackets += numOpened - numClosed
lastParam += p + ","
if insideBrackets == 0 {
genericParams = append(genericParams, strings.TrimSpace(strings.TrimRight(lastParam, ",")))
lastParam = ""
depth := 0
genericParams = strings.FieldsFunc(genericParams[1], func(r rune) bool {
if r == '[' {
depth++
} else if r == ']' {
depth--
} else if r == ',' && depth == 0 {
return true
}
return false
})
if depth != 0 {
return "", nil
}
return genericTypeName, genericParams
}
func resolveType(expr ast.Expr, field *ast.Field, genericParamTypeDefs map[string]*genericTypeSpec) ast.Expr {
func (pkgDefs *PackagesDefinitions) resolveGenericType(file *ast.File, expr ast.Expr, genericParamTypeDefs map[string]*genericTypeSpec, parseDependency bool) ast.Expr {
switch astExpr := expr.(type) {
case *ast.Ident:
if genTypeSpec, ok := genericParamTypeDefs[astExpr.Name]; ok {
if genTypeSpec.ArrayDepth > 0 {
genTypeSpec.ArrayDepth--
return &ast.ArrayType{Elt: resolveType(expr, field, genericParamTypeDefs)}
retType := genTypeSpec.Type()
for i := 0; i < genTypeSpec.ArrayDepth; i++ {
retType = &ast.ArrayType{Elt: retType}
}
return genTypeSpec.Type()
return retType
}
case *ast.ArrayType:
return &ast.ArrayType{
Elt: resolveType(astExpr.Elt, field, genericParamTypeDefs),
Elt: pkgDefs.resolveGenericType(file, astExpr.Elt, genericParamTypeDefs, parseDependency),
Len: astExpr.Len,
Lbrack: astExpr.Lbrack,
}
}
case *ast.StarExpr:
return &ast.StarExpr{
Star: astExpr.Star,
X: pkgDefs.resolveGenericType(file, astExpr.X, genericParamTypeDefs, parseDependency),
}
case *ast.IndexExpr, *ast.IndexListExpr:
fullGenericName, _ := getGenericFieldType(file, expr, genericParamTypeDefs)
typeDef := pkgDefs.findGenericTypeSpec(fullGenericName, file, parseDependency)
if typeDef != nil {
return typeDef.TypeSpec.Type
}
case *ast.StructType:
newStructTypeDef := &ast.StructType{
Struct: astExpr.Struct,
Incomplete: astExpr.Incomplete,
Fields: &ast.FieldList{
Opening: astExpr.Fields.Opening,
Closing: astExpr.Fields.Closing,
},
}
return field.Type
for _, field := range astExpr.Fields.List {
newField := &ast.Field{
Type: field.Type,
Doc: field.Doc,
Names: field.Names,
Tag: field.Tag,
Comment: field.Comment,
}
newField.Type = pkgDefs.resolveGenericType(file, field.Type, genericParamTypeDefs, parseDependency)
newStructTypeDef.Fields.List = append(newStructTypeDef.Fields.List, newField)
}
return newStructTypeDef
}
return expr
}
func getGenericFieldType(file *ast.File, field ast.Expr) (string, error) {
func getExtendedGenericFieldType(file *ast.File, field ast.Expr, genericParamTypeDefs map[string]*genericTypeSpec) (string, error) {
switch fieldType := field.(type) {
case *ast.ArrayType:
fieldName, err := getExtendedGenericFieldType(file, fieldType.Elt, genericParamTypeDefs)
return "[]" + fieldName, err
case *ast.StarExpr:
return getExtendedGenericFieldType(file, fieldType.X, genericParamTypeDefs)
case *ast.Ident:
if genericParamTypeDefs != nil {
if typeSpec, ok := genericParamTypeDefs[fieldType.Name]; ok {
return typeSpec.Name, nil
}
}
if fieldType.Obj == nil {
return fieldType.Name, nil
}
tSpec := &TypeSpecDef{
File: file,
TypeSpec: fieldType.Obj.Decl.(*ast.TypeSpec),
PkgPath: file.Name.Name,
}
return tSpec.FullName(), nil
default:
return getFieldType(file, field)
}
}
func getGenericFieldType(file *ast.File, field ast.Expr, genericParamTypeDefs map[string]*genericTypeSpec) (string, error) {
var fullName string
var baseName string
var err error
switch fieldType := field.(type) {
case *ast.IndexListExpr:
fullName, err := getGenericTypeName(file, fieldType.X)
baseName, err = getGenericTypeName(file, fieldType.X)
if err != nil {
return "", err
}
fullName += "["
fullName = baseName + "["
for _, index := range fieldType.Indices {
var fieldName string
var err error
switch item := index.(type) {
case *ast.ArrayType:
fieldName, err = getFieldType(file, item.Elt)
fieldName = "[]" + fieldName
default:
fieldName, err = getFieldType(file, index)
}
fieldName, err := getExtendedGenericFieldType(file, index, genericParamTypeDefs)
if err != nil {
return "", err
}
@ -253,50 +301,85 @@ func getGenericFieldType(file *ast.File, field ast.Expr) (string, error) {
fullName += fieldName + ","
}
return strings.TrimRight(fullName, ",") + "]", nil
fullName = strings.TrimRight(fullName, ",") + "]"
case *ast.IndexExpr:
x, err := getFieldType(file, fieldType.X)
baseName, err = getGenericTypeName(file, fieldType.X)
if err != nil {
return "", err
}
i, err := getFieldType(file, fieldType.Index)
indexName, err := getExtendedGenericFieldType(file, fieldType.Index, genericParamTypeDefs)
if err != nil {
return "", err
}
packageName := ""
if !strings.Contains(x, ".") {
if file.Name == nil {
return "", errors.New("file name is nil")
}
packageName, _ = getFieldType(file, file.Name)
}
return strings.TrimLeft(fmt.Sprintf("%s.%s[%s]", packageName, x, i), "."), nil
fullName = fmt.Sprintf("%s[%s]", baseName, indexName)
}
return "", fmt.Errorf("unknown field type %#v", field)
if fullName == "" {
return "", fmt.Errorf("unknown field type %#v", field)
}
var packageName string
if !strings.Contains(baseName, ".") {
if file.Name == nil {
return "", errors.New("file name is nil")
}
packageName, _ = getFieldType(file, file.Name)
}
return strings.TrimLeft(fmt.Sprintf("%s.%s", packageName, fullName), "."), nil
}
func getGenericTypeName(file *ast.File, field ast.Expr) (string, error) {
switch indexType := field.(type) {
switch fieldType := field.(type) {
case *ast.Ident:
spec := &TypeSpecDef{
if fieldType.Obj == nil {
return fieldType.Name, nil
}
tSpec := &TypeSpecDef{
File: file,
TypeSpec: indexType.Obj.Decl.(*ast.TypeSpec),
TypeSpec: fieldType.Obj.Decl.(*ast.TypeSpec),
PkgPath: file.Name.Name,
}
return spec.FullName(), nil
return tSpec.FullName(), nil
case *ast.ArrayType:
spec := &TypeSpecDef{
tSpec := &TypeSpecDef{
File: file,
TypeSpec: indexType.Elt.(*ast.Ident).Obj.Decl.(*ast.TypeSpec),
TypeSpec: fieldType.Elt.(*ast.Ident).Obj.Decl.(*ast.TypeSpec),
PkgPath: file.Name.Name,
}
return spec.FullName(), nil
return tSpec.FullName(), nil
case *ast.SelectorExpr:
return fmt.Sprintf("%s.%s", indexType.X.(*ast.Ident).Name, indexType.Sel.Name), nil
return fmt.Sprintf("%s.%s", fieldType.X.(*ast.Ident).Name, fieldType.Sel.Name), nil
}
return "", fmt.Errorf("unknown type %#v", field)
}
func (parser *Parser) parseGenericTypeExpr(file *ast.File, typeExpr ast.Expr) (*spec.Schema, error) {
switch expr := typeExpr.(type) {
// suppress debug messages for these types
case *ast.InterfaceType:
case *ast.StructType:
case *ast.Ident:
case *ast.StarExpr:
case *ast.SelectorExpr:
case *ast.ArrayType:
case *ast.MapType:
case *ast.FuncType:
case *ast.IndexExpr:
name, err := getExtendedGenericFieldType(file, expr, nil)
if err == nil {
if schema, err := parser.getTypeSchema(name, file, false); err == nil {
return schema, nil
}
}
parser.debug.Printf("Type definition of type '%T' is not supported yet. Using 'object' instead. (%s)\n", typeExpr, err)
default:
parser.debug.Printf("Type definition of type '%T' is not supported yet. Using 'object' instead.\n", typeExpr)
}
return PrimitiveSchema(OBJECT), nil
}

View File

@ -5,17 +5,42 @@ package swag
import (
"fmt"
"github.com/go-openapi/spec"
"go/ast"
)
type genericTypeSpec struct {
ArrayDepth int
TypeSpec *TypeSpecDef
Name string
}
func typeSpecFullName(typeSpecDef *TypeSpecDef) string {
return typeSpecDef.FullName()
}
func (pkgDefs *PackagesDefinitions) parametrizeStruct(original *TypeSpecDef, fullGenericForm string, parseDependency bool) *TypeSpecDef {
func (pkgDefs *PackagesDefinitions) parametrizeGenericType(file *ast.File, original *TypeSpecDef, fullGenericForm string, parseDependency bool) *TypeSpecDef {
return original
}
func getGenericFieldType(file *ast.File, field ast.Expr) (string, error) {
func getGenericFieldType(file *ast.File, field ast.Expr, genericParamTypeDefs map[string]*genericTypeSpec) (string, error) {
return "", fmt.Errorf("unknown field type %#v", field)
}
func (parser *Parser) parseGenericTypeExpr(file *ast.File, typeExpr ast.Expr) (*spec.Schema, error) {
switch typeExpr.(type) {
// suppress debug messages for these types
case *ast.InterfaceType:
case *ast.StructType:
case *ast.Ident:
case *ast.StarExpr:
case *ast.SelectorExpr:
case *ast.ArrayType:
case *ast.MapType:
case *ast.FuncType:
default:
parser.debug.Printf("Type definition of type '%T' is not supported yet. Using 'object' instead.\n", typeExpr)
}
return PrimitiveSchema(OBJECT), nil
}

View File

@ -6,7 +6,6 @@ import (
"go/ast"
goparser "go/parser"
"go/token"
"io/ioutil"
"net/http"
"os"
"path/filepath"
@ -226,7 +225,7 @@ func findInSlice(arr []string, target string) bool {
}
func (operation *Operation) parseArrayParam(param *spec.Parameter, paramType, refType, objectType string) error {
if !IsPrimitiveType(refType) {
if !IsPrimitiveType(refType) && !(refType == "file" && paramType == "formData") {
return fmt.Errorf("%s is not supported array type for %s", refType, paramType)
}
@ -270,7 +269,9 @@ func (operation *Operation) parseArrayParam(param *spec.Parameter, paramType, re
// ParseParamComment parses params return []string of param properties
// E.g. @Param queryText formData string true "The email for login"
// [param name] [paramType] [data type] [is mandatory?] [Comment]
//
// [param name] [paramType] [data type] [is mandatory?] [Comment]
//
// E.g. @Param some_id path int true "Some ID".
func (operation *Operation) ParseParamComment(commentLine string, astFile *ast.File) error {
matches := paramPattern.FindStringSubmatch(commentLine)
@ -824,6 +825,10 @@ var responsePattern = regexp.MustCompile(`^([\w,]+)\s+([\w{}]+)\s+([\w\-.\\{}=,\
var combinedPattern = regexp.MustCompile(`^([\w\-./\[\]]+){(.*)}$`)
func (operation *Operation) parseObjectSchema(refType string, astFile *ast.File) (*spec.Schema, error) {
return parseObjectSchema(operation.parser, refType, astFile)
}
func parseObjectSchema(parser *Parser, refType string, astFile *ast.File) (*spec.Schema, error) {
switch {
case refType == NIL:
return nil, nil
@ -838,7 +843,7 @@ func (operation *Operation) parseObjectSchema(refType string, astFile *ast.File)
case IsPrimitiveType(refType):
return PrimitiveSchema(refType), nil
case strings.HasPrefix(refType, "[]"):
schema, err := operation.parseObjectSchema(refType[2:], astFile)
schema, err := parseObjectSchema(parser, refType[2:], astFile)
if err != nil {
return nil, err
}
@ -856,17 +861,17 @@ func (operation *Operation) parseObjectSchema(refType string, astFile *ast.File)
return spec.MapProperty(nil), nil
}
schema, err := operation.parseObjectSchema(refType, astFile)
schema, err := parseObjectSchema(parser, refType, astFile)
if err != nil {
return nil, err
}
return spec.MapProperty(schema), nil
case strings.Contains(refType, "{"):
return operation.parseCombinedObjectSchema(refType, astFile)
return parseCombinedObjectSchema(parser, refType, astFile)
default:
if operation.parser != nil { // checking refType has existing in 'TypeDefinitions'
schema, err := operation.parser.getTypeSchema(refType, astFile, true)
if parser != nil { // checking refType has existing in 'TypeDefinitions'
schema, err := parser.getTypeSchema(refType, astFile, true)
if err != nil {
return nil, err
}
@ -896,13 +901,13 @@ func parseFields(s string) []string {
})
}
func (operation *Operation) parseCombinedObjectSchema(refType string, astFile *ast.File) (*spec.Schema, error) {
func parseCombinedObjectSchema(parser *Parser, refType string, astFile *ast.File) (*spec.Schema, error) {
matches := combinedPattern.FindStringSubmatch(refType)
if len(matches) != 3 {
return nil, fmt.Errorf("invalid type: %s", refType)
}
schema, err := operation.parseObjectSchema(matches[1], astFile)
schema, err := parseObjectSchema(parser, matches[1], astFile)
if err != nil {
return nil, err
}
@ -912,7 +917,7 @@ func (operation *Operation) parseCombinedObjectSchema(refType string, astFile *a
for _, field := range fields {
keyVal := strings.SplitN(field, "=", 2)
if len(keyVal) == 2 {
schema, err := operation.parseObjectSchema(keyVal[1], astFile)
schema, err := parseObjectSchema(parser, keyVal[1], astFile)
if err != nil {
return nil, err
}
@ -1193,17 +1198,17 @@ func createParameter(paramType, description, paramName, schemaType string, requi
}
func getCodeExampleForSummary(summaryName string, dirPath string) ([]byte, error) {
filesInfos, err := ioutil.ReadDir(dirPath)
dirEntries, err := os.ReadDir(dirPath)
if err != nil {
return nil, err
}
for _, fileInfo := range filesInfos {
if fileInfo.IsDir() {
for _, entry := range dirEntries {
if entry.IsDir() {
continue
}
fileName := fileInfo.Name()
fileName := entry.Name()
if !strings.Contains(fileName, ".json") {
continue
@ -1212,7 +1217,7 @@ func getCodeExampleForSummary(summaryName string, dirPath string) ([]byte, error
if strings.Contains(fileName, summaryName) {
fullPath := filepath.Join(dirPath, fileName)
commentInfo, err := ioutil.ReadFile(fullPath)
commentInfo, err := os.ReadFile(fullPath)
if err != nil {
return nil, fmt.Errorf("Failed to read code example file %s error: %s ", fullPath, err)
}

View File

@ -164,7 +164,8 @@ func (pkgDefs *PackagesDefinitions) parseTypesFromFile(astFile *ast.File, packag
func (pkgDefs *PackagesDefinitions) parseFunctionScopedTypesFromFile(astFile *ast.File, packagePath string, parsedSchemas map[*TypeSpecDef]*Schema) {
for _, astDeclaration := range astFile.Decls {
if funcDeclaration, ok := astDeclaration.(*ast.FuncDecl); ok {
funcDeclaration, ok := astDeclaration.(*ast.FuncDecl)
if ok && funcDeclaration.Body != nil {
for _, stmt := range funcDeclaration.Body.List {
if declStmt, ok := (stmt).(*ast.DeclStmt); ok {
if genDecl, ok := (declStmt.Decl).(*ast.GenDecl); ok && genDecl.Tok == token.TYPE {
@ -388,25 +389,17 @@ func (pkgDefs *PackagesDefinitions) FindTypeSpec(typeName string, file *ast.File
}
}
if strings.Contains(typeName, "[") {
// joinedParts differs from typeName in that it does not contain any type parameters
joinedParts := strings.Join(parts, ".")
for tName, tSpec := range pkgDefs.uniqueDefinitions {
if !strings.Contains(tName, "[") {
continue
}
if strings.Contains(tName, joinedParts) {
if parametrized := pkgDefs.parametrizeStruct(tSpec, typeName, parseDependency); parametrized != nil {
return parametrized
}
}
}
if def := pkgDefs.findGenericTypeSpec(typeName, file, parseDependency); def != nil {
return def
}
return pkgDefs.findTypeSpec(pkgPath, parts[1])
}
if def := pkgDefs.findGenericTypeSpec(fullTypeName(file.Name.Name, typeName), file, parseDependency); def != nil {
return def
}
typeDef, ok := pkgDefs.uniqueDefinitions[fullTypeName(file.Name.Name, typeName)]
if ok {
return typeDef
@ -428,3 +421,22 @@ func (pkgDefs *PackagesDefinitions) FindTypeSpec(typeName string, file *ast.File
return nil
}
func (pkgDefs *PackagesDefinitions) findGenericTypeSpec(typeName string, file *ast.File, parseDependency bool) *TypeSpecDef {
if strings.Contains(typeName, "[") {
// genericName differs from typeName in that it does not contain any type parameters
genericName := strings.SplitN(typeName, "[", 2)[0]
for tName, tSpec := range pkgDefs.uniqueDefinitions {
if !strings.Contains(tName, "[") {
continue
}
if strings.Contains(tName, genericName) {
if parametrized := pkgDefs.parametrizeGenericType(file, tSpec, typeName, parseDependency); parametrized != nil {
return parametrized
}
}
}
}
return nil
}

View File

@ -9,7 +9,6 @@ import (
"go/build"
goparser "go/parser"
"go/token"
"io/ioutil"
"log"
"net/http"
"net/url"
@ -733,17 +732,17 @@ func isGeneralAPIComment(comments []string) bool {
}
func getMarkdownForTag(tagName string, dirPath string) ([]byte, error) {
filesInfos, err := ioutil.ReadDir(dirPath)
dirEntries, err := os.ReadDir(dirPath)
if err != nil {
return nil, err
}
for _, fileInfo := range filesInfos {
if fileInfo.IsDir() {
for _, entry := range dirEntries {
if entry.IsDir() {
continue
}
fileName := fileInfo.Name()
fileName := entry.Name()
if !strings.Contains(fileName, ".md") {
continue
@ -752,7 +751,7 @@ func getMarkdownForTag(tagName string, dirPath string) ([]byte, error) {
if strings.Contains(fileName, tagName) {
fullPath := filepath.Join(dirPath, fileName)
commentInfo, err := ioutil.ReadFile(fullPath)
commentInfo, err := os.ReadFile(fullPath)
if err != nil {
return nil, fmt.Errorf("Failed to read markdown file %s error: %s ", fullPath, err)
}
@ -873,7 +872,7 @@ func convertFromSpecificToPrimitive(typeName string) (string, error) {
func (parser *Parser) getTypeSchema(typeName string, file *ast.File, ref bool) (*spec.Schema, error) {
if override, ok := parser.Overrides[typeName]; ok {
parser.debug.Printf("Override detected for %s: using %s instead", typeName, override)
typeName = override
return parseObjectSchema(parser, override, file)
}
if IsInterfaceLike(typeName) {
@ -1186,12 +1185,10 @@ func (parser *Parser) parseTypeExpr(file *ast.File, typeExpr ast.Expr, ref bool)
case *ast.FuncType:
return nil, ErrFuncTypeField
// ...
default:
parser.debug.Printf("Type definition of type '%T' is not supported yet. Using 'object' instead.\n", typeExpr)
// ...
}
return PrimitiveSchema(OBJECT), nil
return parser.parseGenericTypeExpr(file, typeExpr)
}
func (parser *Parser) parseStruct(file *ast.File, fields *ast.FieldList) (*spec.Schema, error) {
@ -1334,7 +1331,7 @@ func getFieldType(file *ast.File, field ast.Expr) (string, error) {
return fullName, nil
default:
return getGenericFieldType(file, field)
return getGenericFieldType(file, field, nil)
}
}
@ -1490,7 +1487,7 @@ func (parser *Parser) getAllGoFileInfoFromDeps(pkg *depth.Pkg) error {
srcDir := pkg.Raw.Dir
files, err := ioutil.ReadDir(srcDir) // only parsing files in the dir(don't contain sub dir files)
files, err := os.ReadDir(srcDir) // only parsing files in the dir(don't contain sub dir files)
if err != nil {
return err
}

View File

@ -1,4 +1,4 @@
package swag
// Version of swag.
const Version = "v1.8.5"
const Version = "v1.8.7"

View File

@ -112,8 +112,7 @@ func ExecuteFuncString(template, startTag, endTag string, f TagFunc) string {
// but when f returns an error, ExecuteFuncStringWithErr won't panic like ExecuteFuncString
// it just returns an empty string and the error f returned
func ExecuteFuncStringWithErr(template, startTag, endTag string, f TagFunc) (string, error) {
tagsCount := bytes.Count(unsafeString2Bytes(template), unsafeString2Bytes(startTag))
if tagsCount == 0 {
if n := bytes.Index(unsafeString2Bytes(template), unsafeString2Bytes(startTag)); n < 0 {
return template, nil
}

View File

@ -336,7 +336,6 @@ func (p *parser) parseTypeReference() *Type {
}
if p.skip(lexer.Bang) {
typ.Position = p.peekPos()
typ.NonNull = true
}
return &typ

View File

@ -10,3 +10,6 @@ lint.log
# Profiling output
*.prof
# Output of fossa analyzer
/fossa

View File

@ -1,27 +0,0 @@
sudo: false
language: go
go_import_path: go.uber.org/atomic
env:
global:
- GO111MODULE=on
matrix:
include:
- go: oldstable
- go: stable
env: LINT=1
cache:
directories:
- vendor
before_install:
- go version
script:
- test -z "$LINT" || make lint
- make cover
after_success:
- bash <(curl -s https://codecov.io/bash)

View File

@ -4,6 +4,37 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.10.0] - 2022-08-11
### Added
- Add `atomic.Float32` type for atomic operations on `float32`.
- Add `CompareAndSwap` and `Swap` methods to `atomic.String`, `atomic.Error`,
and `atomic.Value`.
- Add generic `atomic.Pointer[T]` type for atomic operations on pointers of any
type. This is present only for Go 1.18 or higher, and is a drop-in for
replacement for the standard library's `sync/atomic.Pointer` type.
### Changed
- Deprecate `CAS` methods on all types in favor of corresponding
`CompareAndSwap` methods.
Thanks to @eNV25 and @icpd for their contributions to this release.
[1.10.0]: https://github.com/uber-go/atomic/compare/v1.9.0...v1.10.0
## [1.9.0] - 2021-07-15
### Added
- Add `Float64.Swap` to match int atomic operations.
- Add `atomic.Time` type for atomic operations on `time.Time` values.
[1.9.0]: https://github.com/uber-go/atomic/compare/v1.8.0...v1.9.0
## [1.8.0] - 2021-06-09
### Added
- Add `atomic.Uintptr` type for atomic operations on `uintptr` values.
- Add `atomic.UnsafePointer` type for atomic operations on `unsafe.Pointer` values.
[1.8.0]: https://github.com/uber-go/atomic/compare/v1.7.0...v1.8.0
## [1.7.0] - 2020-09-14
### Added
- Support JSON serialization and deserialization of primitive atomic types.
@ -15,32 +46,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Removed
- Remove dependency on `golang.org/x/{lint, tools}`.
[1.7.0]: https://github.com/uber-go/atomic/compare/v1.6.0...v1.7.0
## [1.6.0] - 2020-02-24
### Changed
- Drop library dependency on `golang.org/x/{lint, tools}`.
[1.6.0]: https://github.com/uber-go/atomic/compare/v1.5.1...v1.6.0
## [1.5.1] - 2019-11-19
- Fix bug where `Bool.CAS` and `Bool.Toggle` do work correctly together
causing `CAS` to fail even though the old value matches.
[1.5.1]: https://github.com/uber-go/atomic/compare/v1.5.0...v1.5.1
## [1.5.0] - 2019-10-29
### Changed
- With Go modules, only the `go.uber.org/atomic` import path is supported now.
If you need to use the old import path, please add a `replace` directive to
your `go.mod`.
[1.5.0]: https://github.com/uber-go/atomic/compare/v1.4.0...v1.5.0
## [1.4.0] - 2019-05-01
### Added
- Add `atomic.Error` type for atomic operations on `error` values.
[1.4.0]: https://github.com/uber-go/atomic/compare/v1.3.2...v1.4.0
## [1.3.2] - 2018-05-02
### Added
- Add `atomic.Duration` type for atomic operations on `time.Duration` values.
[1.3.2]: https://github.com/uber-go/atomic/compare/v1.3.1...v1.3.2
## [1.3.1] - 2017-11-14
### Fixed
- Revert optimization for `atomic.String.Store("")` which caused data races.
[1.3.1]: https://github.com/uber-go/atomic/compare/v1.3.0...v1.3.1
## [1.3.0] - 2017-11-13
### Added
- Add `atomic.Bool.CAS` for compare-and-swap semantics on bools.
@ -48,10 +93,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Optimize `atomic.String.Store("")` by avoiding an allocation.
[1.3.0]: https://github.com/uber-go/atomic/compare/v1.2.0...v1.3.0
## [1.2.0] - 2017-04-12
### Added
- Shadow `atomic.Value` from `sync/atomic`.
[1.2.0]: https://github.com/uber-go/atomic/compare/v1.1.0...v1.2.0
## [1.1.0] - 2017-03-10
### Added
- Add atomic `Float64` type.
@ -59,18 +108,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Support new `go.uber.org/atomic` import path.
[1.1.0]: https://github.com/uber-go/atomic/compare/v1.0.0...v1.1.0
## [1.0.0] - 2016-07-18
- Initial release.
[1.7.0]: https://github.com/uber-go/atomic/compare/v1.6.0...v1.7.0
[1.6.0]: https://github.com/uber-go/atomic/compare/v1.5.1...v1.6.0
[1.5.1]: https://github.com/uber-go/atomic/compare/v1.5.0...v1.5.1
[1.5.0]: https://github.com/uber-go/atomic/compare/v1.4.0...v1.5.0
[1.4.0]: https://github.com/uber-go/atomic/compare/v1.3.2...v1.4.0
[1.3.2]: https://github.com/uber-go/atomic/compare/v1.3.1...v1.3.2
[1.3.1]: https://github.com/uber-go/atomic/compare/v1.3.0...v1.3.1
[1.3.0]: https://github.com/uber-go/atomic/compare/v1.2.0...v1.3.0
[1.2.0]: https://github.com/uber-go/atomic/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/uber-go/atomic/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/uber-go/atomic/releases/tag/v1.0.0

1
vendor/go.uber.org/atomic/Makefile generated vendored
View File

@ -69,6 +69,7 @@ generate: $(GEN_ATOMICINT) $(GEN_ATOMICWRAPPER)
generatenodirty:
@[ -z "$$(git status --porcelain)" ] || ( \
echo "Working tree is dirty. Commit your changes first."; \
git status; \
exit 1 )
@make generate
@status=$$(git status --porcelain); \

View File

@ -55,8 +55,8 @@ Released under the [MIT License](LICENSE.txt).
[doc-img]: https://godoc.org/github.com/uber-go/atomic?status.svg
[doc]: https://godoc.org/go.uber.org/atomic
[ci-img]: https://travis-ci.com/uber-go/atomic.svg?branch=master
[ci]: https://travis-ci.com/uber-go/atomic
[ci-img]: https://github.com/uber-go/atomic/actions/workflows/go.yml/badge.svg
[ci]: https://github.com/uber-go/atomic/actions/workflows/go.yml
[cov-img]: https://codecov.io/gh/uber-go/atomic/branch/master/graph/badge.svg
[cov]: https://codecov.io/gh/uber-go/atomic
[reportcard-img]: https://goreportcard.com/badge/go.uber.org/atomic

27
vendor/go.uber.org/atomic/bool.go generated vendored
View File

@ -1,6 +1,6 @@
// @generated Code generated by gen-atomicwrapper.
// Copyright (c) 2020 Uber Technologies, Inc.
// Copyright (c) 2020-2022 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@ -36,10 +36,10 @@ type Bool struct {
var _zeroBool bool
// NewBool creates a new Bool.
func NewBool(v bool) *Bool {
func NewBool(val bool) *Bool {
x := &Bool{}
if v != _zeroBool {
x.Store(v)
if val != _zeroBool {
x.Store(val)
}
return x
}
@ -50,19 +50,26 @@ func (x *Bool) Load() bool {
}
// Store atomically stores the passed bool.
func (x *Bool) Store(v bool) {
x.v.Store(boolToInt(v))
func (x *Bool) Store(val bool) {
x.v.Store(boolToInt(val))
}
// CAS is an atomic compare-and-swap for bool values.
func (x *Bool) CAS(o, n bool) bool {
return x.v.CAS(boolToInt(o), boolToInt(n))
//
// Deprecated: Use CompareAndSwap.
func (x *Bool) CAS(old, new bool) (swapped bool) {
return x.CompareAndSwap(old, new)
}
// CompareAndSwap is an atomic compare-and-swap for bool values.
func (x *Bool) CompareAndSwap(old, new bool) (swapped bool) {
return x.v.CompareAndSwap(boolToInt(old), boolToInt(new))
}
// Swap atomically stores the given bool and returns the old
// value.
func (x *Bool) Swap(o bool) bool {
return truthy(x.v.Swap(boolToInt(o)))
func (x *Bool) Swap(val bool) (old bool) {
return truthy(x.v.Swap(boolToInt(val)))
}
// MarshalJSON encodes the wrapped bool into JSON.

View File

@ -38,7 +38,7 @@ func boolToInt(b bool) uint32 {
}
// Toggle atomically negates the Boolean and returns the previous value.
func (b *Bool) Toggle() bool {
func (b *Bool) Toggle() (old bool) {
for {
old := b.Load()
if b.CAS(old, !old) {

View File

@ -1,6 +1,6 @@
// @generated Code generated by gen-atomicwrapper.
// Copyright (c) 2020 Uber Technologies, Inc.
// Copyright (c) 2020-2022 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@ -37,10 +37,10 @@ type Duration struct {
var _zeroDuration time.Duration
// NewDuration creates a new Duration.
func NewDuration(v time.Duration) *Duration {
func NewDuration(val time.Duration) *Duration {
x := &Duration{}
if v != _zeroDuration {
x.Store(v)
if val != _zeroDuration {
x.Store(val)
}
return x
}
@ -51,19 +51,26 @@ func (x *Duration) Load() time.Duration {
}
// Store atomically stores the passed time.Duration.
func (x *Duration) Store(v time.Duration) {
x.v.Store(int64(v))
func (x *Duration) Store(val time.Duration) {
x.v.Store(int64(val))
}
// CAS is an atomic compare-and-swap for time.Duration values.
func (x *Duration) CAS(o, n time.Duration) bool {
return x.v.CAS(int64(o), int64(n))
//
// Deprecated: Use CompareAndSwap.
func (x *Duration) CAS(old, new time.Duration) (swapped bool) {
return x.CompareAndSwap(old, new)
}
// CompareAndSwap is an atomic compare-and-swap for time.Duration values.
func (x *Duration) CompareAndSwap(old, new time.Duration) (swapped bool) {
return x.v.CompareAndSwap(int64(old), int64(new))
}
// Swap atomically stores the given time.Duration and returns the old
// value.
func (x *Duration) Swap(o time.Duration) time.Duration {
return time.Duration(x.v.Swap(int64(o)))
func (x *Duration) Swap(val time.Duration) (old time.Duration) {
return time.Duration(x.v.Swap(int64(val)))
}
// MarshalJSON encodes the wrapped time.Duration into JSON.

View File

@ -25,13 +25,13 @@ import "time"
//go:generate bin/gen-atomicwrapper -name=Duration -type=time.Duration -wrapped=Int64 -pack=int64 -unpack=time.Duration -cas -swap -json -imports time -file=duration.go
// Add atomically adds to the wrapped time.Duration and returns the new value.
func (d *Duration) Add(n time.Duration) time.Duration {
return time.Duration(d.v.Add(int64(n)))
func (d *Duration) Add(delta time.Duration) time.Duration {
return time.Duration(d.v.Add(int64(delta)))
}
// Sub atomically subtracts from the wrapped time.Duration and returns the new value.
func (d *Duration) Sub(n time.Duration) time.Duration {
return time.Duration(d.v.Sub(int64(n)))
func (d *Duration) Sub(delta time.Duration) time.Duration {
return time.Duration(d.v.Sub(int64(delta)))
}
// String encodes the wrapped value as a string.

23
vendor/go.uber.org/atomic/error.go generated vendored
View File

@ -1,6 +1,6 @@
// @generated Code generated by gen-atomicwrapper.
// Copyright (c) 2020 Uber Technologies, Inc.
// Copyright (c) 2020-2022 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@ -32,10 +32,10 @@ type Error struct {
var _zeroError error
// NewError creates a new Error.
func NewError(v error) *Error {
func NewError(val error) *Error {
x := &Error{}
if v != _zeroError {
x.Store(v)
if val != _zeroError {
x.Store(val)
}
return x
}
@ -46,6 +46,17 @@ func (x *Error) Load() error {
}
// Store atomically stores the passed error.
func (x *Error) Store(v error) {
x.v.Store(packError(v))
func (x *Error) Store(val error) {
x.v.Store(packError(val))
}
// CompareAndSwap is an atomic compare-and-swap for error values.
func (x *Error) CompareAndSwap(old, new error) (swapped bool) {
return x.v.CompareAndSwap(packError(old), packError(new))
}
// Swap atomically stores the given error and returns the old
// value.
func (x *Error) Swap(val error) (old error) {
return unpackError(x.v.Swap(packError(val)))
}

Some files were not shown because too many files have changed in this diff Show More