diff --git a/go.mod b/go.mod index 1fa5ac86..a4cabae4 100644 --- a/go.mod +++ b/go.mod @@ -10,8 +10,8 @@ require ( github.com/golang/protobuf v1.4.3 github.com/gomodule/redigo v1.8.3 github.com/mmcloughlin/geohash v0.10.0 - github.com/nats-io/nats-server/v2 v2.1.9 // indirect - github.com/nats-io/nats.go v1.10.0 + github.com/nats-io/nats-server/v2 v2.2.0 // indirect + github.com/nats-io/nats.go v1.10.1-0.20210228004050-ed743748acac github.com/peterh/liner v1.2.1 github.com/prometheus/client_golang v1.10.0 github.com/streadway/amqp v1.0.0 @@ -29,7 +29,7 @@ require ( github.com/tidwall/sjson v1.1.6 github.com/xdg/scram v1.0.3 github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da - golang.org/x/net v0.0.0-20210119194325-5f4716e94777 + golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 google.golang.org/api v0.20.0 google.golang.org/grpc v1.35.0 diff --git a/go.sum b/go.sum index 543979fe..0edf71ee 100644 --- a/go.sum +++ b/go.sum @@ -235,8 +235,10 @@ 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/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.11.0 h1:wJbzvpYMVGG9iTI9VxpnNZfd4DzMPoCWze3GgSqz8yg= github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.12 h1:famVnQVu7QwryBN4jNseQdUKES71ZAOnB6UQQJPZvqk= +github.com/klauspost/compress v1.11.12/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 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= @@ -259,6 +261,9 @@ github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp 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/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc= +github.com/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz/0= +github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= @@ -276,18 +281,40 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/jwt v1.1.0 h1:+vOlgtM0ZsF46GbmUoadq0/2rChNS45gtxHEa3H1gqM= +github.com/nats-io/jwt v0.3.3-0.20200519195258-f2bf5ce574c7/go.mod h1:n3cvmLfBfnpV4JJRN7lRYCyZnw48ksGsbThGXEk4w9M= github.com/nats-io/jwt v1.1.0/go.mod h1:n3cvmLfBfnpV4JJRN7lRYCyZnw48ksGsbThGXEk4w9M= +github.com/nats-io/jwt v1.2.2 h1:w3GMTO969dFg+UOKTmmyuu7IGdusK+7Ytlt//OYH/uU= +github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q= +github.com/nats-io/jwt/v2 v2.0.0-20200916203241-1f8ce17dff02/go.mod h1:vs+ZEjP+XKy8szkBmQwCB7RjYdIlMaPsFPs4VdS4bTQ= +github.com/nats-io/jwt/v2 v2.0.0-20201015190852-e11ce317263c/go.mod h1:vs+ZEjP+XKy8szkBmQwCB7RjYdIlMaPsFPs4VdS4bTQ= +github.com/nats-io/jwt/v2 v2.0.0-20210125223648-1c24d462becc/go.mod h1:PuO5FToRL31ecdFqVjc794vK0Bj0CwzveQEDvkb7MoQ= +github.com/nats-io/jwt/v2 v2.0.0-20210208203759-ff814ca5f813/go.mod h1:PuO5FToRL31ecdFqVjc794vK0Bj0CwzveQEDvkb7MoQ= +github.com/nats-io/jwt/v2 v2.0.1 h1:SycklijeduR742i/1Y3nRhURYM7imDzZZ3+tuAQqhQA= +github.com/nats-io/jwt/v2 v2.0.1/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats-server/v2 v2.1.9 h1:Sxr2zpaapgpBT9ElTxTVe62W+qjnhPcKY/8W5cnA/Qk= -github.com/nats-io/nats-server/v2 v2.1.9/go.mod h1:9qVyoewoYXzG1ME9ox0HwkkzyYvnlBDugfR4Gg/8uHU= +github.com/nats-io/nats-server/v2 v2.1.8-0.20200524125952-51ebd92a9093/go.mod h1:rQnBf2Rv4P9adtAs/Ti6LfFmVtFG6HLhl/H7cVshcJU= +github.com/nats-io/nats-server/v2 v2.1.8-0.20200601203034-f8d6dd992b71/go.mod h1:Nan/1L5Sa1JRW+Thm4HNYcIDcVRFc5zK9OpSZeI2kk4= +github.com/nats-io/nats-server/v2 v2.1.8-0.20200929001935-7f44d075f7ad/go.mod h1:TkHpUIDETmTI7mrHN40D1pzxfzHZuGmtMbtb83TGVQw= +github.com/nats-io/nats-server/v2 v2.1.8-0.20201129161730-ebe63db3e3ed/go.mod h1:XD0zHR/jTXdZvWaQfS5mQgsXj6x12kMjKLyAk/cOGgY= +github.com/nats-io/nats-server/v2 v2.1.8-0.20210205154825-f7ab27f7dad4/go.mod h1:kauGd7hB5517KeSqspW2U1Mz/jhPbTrE8eOXzUPk1m0= +github.com/nats-io/nats-server/v2 v2.1.8-0.20210227190344-51550e242af8/go.mod h1:/QQ/dpqFavkNhVnjvMILSQ3cj5hlmhB66adlgNbjuoA= +github.com/nats-io/nats-server/v2 v2.2.0 h1:QNeFmJRBq+O2zF8EmsR/JSvtL2zXb3GwICloHgskYBU= +github.com/nats-io/nats-server/v2 v2.2.0/go.mod h1:eKlAaGmSQHZMFQA6x56AaP5/Bl9N3mWF4awyT2TTpzc= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nats.go v1.10.0 h1:L8qnKaofSfNFbXg0C5F71LdjPRnmQwSsA4ukmkt1TvY= github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE= +github.com/nats-io/nats.go v1.10.1-0.20200531124210-96f2130e4d55/go.mod h1:ARiFsjW9DVxk48WJbO3OSZ2DG8fjkMi7ecLmXoY/n9I= +github.com/nats-io/nats.go v1.10.1-0.20200606002146-fc6fed82929a/go.mod h1:8eAIv96Mo9QW6Or40jUHejS7e4VwZ3VRYD6Sf0BTDp4= +github.com/nats-io/nats.go v1.10.1-0.20201021145452-94be476ad6e0/go.mod h1:VU2zERjp8xmF+Lw2NH4u2t5qWZxwc7jB3+7HVMWQXPI= +github.com/nats-io/nats.go v1.10.1-0.20210127212649-5b4924938a9a/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI= +github.com/nats-io/nats.go v1.10.1-0.20210211000709-75ded9c77585/go.mod h1:uBWnCKg9luW1g7hgzPxUjHFRI40EuTSX7RCzgnc74Jk= +github.com/nats-io/nats.go v1.10.1-0.20210228004050-ed743748acac h1:/cF7DEtxQBcwRDhpFZ3J0XU4TFpJa9KQF/xDirRNNI0= +github.com/nats-io/nats.go v1.10.1-0.20210228004050-ed743748acac/go.mod h1:hxFvLNbNmT6UppX5B5Tr/r3g+XSwGjJzFn6mxPNJEHc= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.4 h1:aEsHIssIk6ETN5m2/MD8Y4B2X7FfXrBAUdkyRvbVYzA= github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= +github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= +github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= +github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -454,8 +481,10 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 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= @@ -516,8 +545,8 @@ golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 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= @@ -540,6 +569,7 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -552,6 +582,7 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -582,6 +613,8 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb 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-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/vendor/github.com/klauspost/compress/huff0/README.md b/vendor/github.com/klauspost/compress/huff0/README.md index e12da4db..8b6e5c66 100644 --- a/vendor/github.com/klauspost/compress/huff0/README.md +++ b/vendor/github.com/klauspost/compress/huff0/README.md @@ -14,7 +14,9 @@ but it can be used as a secondary step to compressors (like Snappy) that does no ## News - * Mar 2018: First implementation released. Consider this beta software for now. +This is used as part of the [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression package. + +This ensures that most functionality is well tested. # Usage diff --git a/vendor/github.com/klauspost/compress/zstd/README.md b/vendor/github.com/klauspost/compress/zstd/README.md index ea3e5108..7680bfe1 100644 --- a/vendor/github.com/klauspost/compress/zstd/README.md +++ b/vendor/github.com/klauspost/compress/zstd/README.md @@ -24,22 +24,21 @@ Godoc Documentation: https://godoc.org/github.com/klauspost/compress/zstd ### Status: STABLE - there may always be subtle bugs, a wide variety of content has been tested and the library is actively -used by several projects. This library is being continuously [fuzz-tested](https://github.com/klauspost/compress-fuzz), -kindly supplied by [fuzzit.dev](https://fuzzit.dev/). +used by several projects. This library is being [fuzz-tested](https://github.com/klauspost/compress-fuzz) for all updates. There may still be specific combinations of data types/size/settings that could lead to edge cases, so as always, testing is recommended. For now, a high speed (fastest) and medium-fast (default) compressor has been implemented. -The "Fastest" compression ratio is roughly equivalent to zstd level 1. -The "Default" compression ratio is roughly equivalent to zstd level 3 (default). +* The "Fastest" compression ratio is roughly equivalent to zstd level 1. +* The "Default" compression ratio is roughly equivalent to zstd level 3 (default). +* The "Better" compression ratio is roughly equivalent to zstd level 7. +* The "Best" compression ratio is roughly equivalent to zstd level 11. In terms of speed, it is typically 2x as fast as the stdlib deflate/gzip in its fastest mode. The compression ratio compared to stdlib is around level 3, but usually 3x as fast. -Compared to cgo zstd, the speed is around level 3 (default), but compression slightly worse, between level 1&2. - ### Usage @@ -54,11 +53,11 @@ To create a writer with default options, do like this: ```Go // Compress input to output. func Compress(in io.Reader, out io.Writer) error { - w, err := NewWriter(output) + enc, err := zstd.NewWriter(out) if err != nil { return err } - _, err := io.Copy(w, input) + _, err = io.Copy(enc, in) if err != nil { enc.Close() return err @@ -140,7 +139,7 @@ I have collected some speed examples to compare speed and compression against ot * `file` is the input file. * `out` is the compressor used. `zskp` is this package. `zstd` is the Datadog cgo library. `gzstd/gzkp` is gzip standard and this library. -* `level` is the compression level used. For `zskp` level 1 is "fastest", level 2 is "default". +* `level` is the compression level used. For `zskp` level 1 is "fastest", level 2 is "default"; 3 is "better", 4 is "best". * `insize`/`outsize` is the input/output size. * `millis` is the number of milliseconds used for compression. * `mb/s` is megabytes (2^20 bytes) per second. @@ -154,11 +153,13 @@ file out level insize outsize millis mb/s silesia.tar zskp 1 211947520 73101992 643 313.87 silesia.tar zskp 2 211947520 67504318 969 208.38 silesia.tar zskp 3 211947520 65177448 1899 106.44 +silesia.tar zskp 4 211947520 61381950 8115 24.91 cgo zstd: silesia.tar zstd 1 211947520 73605392 543 371.56 silesia.tar zstd 3 211947520 66793289 864 233.68 silesia.tar zstd 6 211947520 62916450 1913 105.66 +silesia.tar zstd 9 211947520 60212393 5063 39.92 gzip, stdlib/this package: silesia.tar gzstd 1 211947520 80007735 1654 122.21 @@ -171,9 +172,11 @@ file out level insize outsize millis mb/s gob-stream zskp 1 1911399616 235022249 3088 590.30 gob-stream zskp 2 1911399616 205669791 3786 481.34 gob-stream zskp 3 1911399616 185792019 9324 195.48 +gob-stream zskp 4 1911399616 171537212 32113 56.76 gob-stream zstd 1 1911399616 249810424 2637 691.26 gob-stream zstd 3 1911399616 208192146 3490 522.31 gob-stream zstd 6 1911399616 193632038 6687 272.56 +gob-stream zstd 9 1911399616 177620386 16175 112.70 gob-stream gzstd 1 1911399616 357382641 10251 177.82 gob-stream gzkp 1 1911399616 362156523 5695 320.08 @@ -185,9 +188,11 @@ file out level insize outsize millis mb/s enwik9 zskp 1 1000000000 343848582 3609 264.18 enwik9 zskp 2 1000000000 317276632 5746 165.97 enwik9 zskp 3 1000000000 294540704 11725 81.34 +enwik9 zskp 4 1000000000 276609671 44029 21.66 enwik9 zstd 1 1000000000 358072021 3110 306.65 enwik9 zstd 3 1000000000 313734672 4784 199.35 enwik9 zstd 6 1000000000 295138875 10290 92.68 +enwik9 zstd 9 1000000000 278348700 28549 33.40 enwik9 gzstd 1 1000000000 382578136 9604 99.30 enwik9 gzkp 1 1000000000 383825945 6544 145.73 @@ -198,9 +203,11 @@ file out level insize outsize millis mb/s github-june-2days-2019.json zskp 1 6273951764 699045015 10620 563.40 github-june-2days-2019.json zskp 2 6273951764 617881763 11687 511.96 github-june-2days-2019.json zskp 3 6273951764 537511906 29252 204.54 +github-june-2days-2019.json zskp 4 6273951764 512796117 97791 61.18 github-june-2days-2019.json zstd 1 6273951764 766284037 8450 708.00 github-june-2days-2019.json zstd 3 6273951764 661889476 10927 547.57 github-june-2days-2019.json zstd 6 6273951764 642756859 22996 260.18 +github-june-2days-2019.json zstd 9 6273951764 601974523 52413 114.16 github-june-2days-2019.json gzstd 1 6273951764 1164400847 29948 199.79 github-june-2days-2019.json gzkp 1 6273951764 1128755542 19236 311.03 @@ -211,9 +218,11 @@ file out level insize outsize millis mb/s rawstudio-mint14.tar zskp 1 8558382592 3667489370 20210 403.84 rawstudio-mint14.tar zskp 2 8558382592 3364592300 31873 256.07 rawstudio-mint14.tar zskp 3 8558382592 3224594213 71751 113.75 +rawstudio-mint14.tar zskp 4 8558382592 3027332295 486243 16.79 rawstudio-mint14.tar zstd 1 8558382592 3609250104 17136 476.27 rawstudio-mint14.tar zstd 3 8558382592 3341679997 29262 278.92 rawstudio-mint14.tar zstd 6 8558382592 3235846406 77904 104.77 +rawstudio-mint14.tar zstd 9 8558382592 3160778861 140946 57.91 rawstudio-mint14.tar gzstd 1 8558382592 3926257486 57722 141.40 rawstudio-mint14.tar gzkp 1 8558382592 3970463184 41749 195.49 @@ -224,9 +233,11 @@ file out level insize outsize millis mb/s nyc-taxi-data-10M.csv zskp 1 3325605752 641339945 8925 355.35 nyc-taxi-data-10M.csv zskp 2 3325605752 591748091 11268 281.44 nyc-taxi-data-10M.csv zskp 3 3325605752 538490114 19880 159.53 +nyc-taxi-data-10M.csv zskp 4 3325605752 495986829 89368 35.49 nyc-taxi-data-10M.csv zstd 1 3325605752 687399637 8233 385.18 nyc-taxi-data-10M.csv zstd 3 3325605752 598514411 10065 315.07 nyc-taxi-data-10M.csv zstd 6 3325605752 570522953 20038 158.27 +nyc-taxi-data-10M.csv zstd 9 3325605752 517554797 64565 49.12 nyc-taxi-data-10M.csv gzstd 1 3325605752 928656485 23876 132.83 nyc-taxi-data-10M.csv gzkp 1 3325605752 924718719 16388 193.53 ``` @@ -251,14 +262,14 @@ For streaming use a simple setup could look like this: import "github.com/klauspost/compress/zstd" func Decompress(in io.Reader, out io.Writer) error { - d, err := zstd.NewReader(input) + d, err := zstd.NewReader(in) if err != nil { return err } defer d.Close() // Copy content... - _, err := io.Copy(out, d) + _, err = io.Copy(out, d) return err } ``` diff --git a/vendor/github.com/klauspost/compress/zstd/blockdec.go b/vendor/github.com/klauspost/compress/zstd/blockdec.go index 4733ea87..b51d922b 100644 --- a/vendor/github.com/klauspost/compress/zstd/blockdec.go +++ b/vendor/github.com/klauspost/compress/zstd/blockdec.go @@ -613,7 +613,7 @@ func (b *blockDec) decodeCompressed(hist *history) error { // Decode treeless literal block. if litType == literalsBlockTreeless { // TODO: We could send the history early WITHOUT the stream history. - // This would allow decoding treeless literials before the byte history is available. + // This would allow decoding treeless literals before the byte history is available. // Silencia stats: Treeless 4393, with: 32775, total: 37168, 11% treeless. // So not much obvious gain here. diff --git a/vendor/github.com/klauspost/compress/zstd/blockenc.go b/vendor/github.com/klauspost/compress/zstd/blockenc.go index 083fbb50..c85c4025 100644 --- a/vendor/github.com/klauspost/compress/zstd/blockenc.go +++ b/vendor/github.com/klauspost/compress/zstd/blockenc.go @@ -76,6 +76,7 @@ func (b *blockEnc) reset(prev *blockEnc) { if prev != nil { b.recentOffsets = prev.prevRecentOffsets } + b.dictLitEnc = nil } // reset will reset the block for a new encode, but in the same stream, diff --git a/vendor/github.com/klauspost/compress/zstd/decodeheader.go b/vendor/github.com/klauspost/compress/zstd/decodeheader.go new file mode 100644 index 00000000..87896c5e --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/decodeheader.go @@ -0,0 +1,202 @@ +// Copyright 2020+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. + +package zstd + +import ( + "bytes" + "errors" + "io" +) + +// HeaderMaxSize is the maximum size of a Frame and Block Header. +// If less is sent to Header.Decode it *may* still contain enough information. +const HeaderMaxSize = 14 + 3 + +// Header contains information about the first frame and block within that. +type Header struct { + // Window Size the window of data to keep while decoding. + // Will only be set if HasFCS is false. + WindowSize uint64 + + // Frame content size. + // Expected size of the entire frame. + FrameContentSize uint64 + + // Dictionary ID. + // If 0, no dictionary. + DictionaryID uint32 + + // First block information. + FirstBlock struct { + // OK will be set if first block could be decoded. + OK bool + + // Is this the last block of a frame? + Last bool + + // Is the data compressed? + // If true CompressedSize will be populated. + // Unfortunately DecompressedSize cannot be determined + // without decoding the blocks. + Compressed bool + + // DecompressedSize is the expected decompressed size of the block. + // Will be 0 if it cannot be determined. + DecompressedSize int + + // CompressedSize of the data in the block. + // Does not include the block header. + // Will be equal to DecompressedSize if not Compressed. + CompressedSize int + } + + // Skippable will be true if the frame is meant to be skipped. + // No other information will be populated. + Skippable bool + + // If set there is a checksum present for the block content. + HasCheckSum bool + + // If this is true FrameContentSize will have a valid value + HasFCS bool + + SingleSegment bool +} + +// Decode the header from the beginning of the stream. +// This will decode the frame header and the first block header if enough bytes are provided. +// It is recommended to provide at least HeaderMaxSize bytes. +// If the frame header cannot be read an error will be returned. +// If there isn't enough input, io.ErrUnexpectedEOF is returned. +// The FirstBlock.OK will indicate if enough information was available to decode the first block header. +func (h *Header) Decode(in []byte) error { + if len(in) < 4 { + return io.ErrUnexpectedEOF + } + b, in := in[:4], in[4:] + if !bytes.Equal(b, frameMagic) { + if !bytes.Equal(b[1:4], skippableFrameMagic) || b[0]&0xf0 != 0x50 { + return ErrMagicMismatch + } + *h = Header{Skippable: true} + return nil + } + if len(in) < 1 { + return io.ErrUnexpectedEOF + } + + // Clear output + *h = Header{} + fhd, in := in[0], in[1:] + h.SingleSegment = fhd&(1<<5) != 0 + h.HasCheckSum = fhd&(1<<2) != 0 + + if fhd&(1<<3) != 0 { + return errors.New("Reserved bit set on frame header") + } + + // Read Window_Descriptor + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#window_descriptor + if !h.SingleSegment { + if len(in) < 1 { + return io.ErrUnexpectedEOF + } + var wd byte + wd, in = in[0], in[1:] + windowLog := 10 + (wd >> 3) + windowBase := uint64(1) << windowLog + windowAdd := (windowBase / 8) * uint64(wd&0x7) + h.WindowSize = windowBase + windowAdd + } + + // Read Dictionary_ID + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary_id + if size := fhd & 3; size != 0 { + if size == 3 { + size = 4 + } + if len(in) < int(size) { + return io.ErrUnexpectedEOF + } + b, in = in[:size], in[size:] + if b == nil { + return io.ErrUnexpectedEOF + } + switch size { + case 1: + h.DictionaryID = uint32(b[0]) + case 2: + h.DictionaryID = uint32(b[0]) | (uint32(b[1]) << 8) + case 4: + h.DictionaryID = uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24) + } + } + + // Read Frame_Content_Size + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#frame_content_size + var fcsSize int + v := fhd >> 6 + switch v { + case 0: + if h.SingleSegment { + fcsSize = 1 + } + default: + fcsSize = 1 << v + } + + if fcsSize > 0 { + h.HasFCS = true + if len(in) < fcsSize { + return io.ErrUnexpectedEOF + } + b, in = in[:fcsSize], in[fcsSize:] + if b == nil { + return io.ErrUnexpectedEOF + } + switch fcsSize { + case 1: + h.FrameContentSize = uint64(b[0]) + case 2: + // When FCS_Field_Size is 2, the offset of 256 is added. + h.FrameContentSize = uint64(b[0]) | (uint64(b[1]) << 8) + 256 + case 4: + h.FrameContentSize = uint64(b[0]) | (uint64(b[1]) << 8) | (uint64(b[2]) << 16) | (uint64(b[3]) << 24) + case 8: + d1 := uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24) + d2 := uint32(b[4]) | (uint32(b[5]) << 8) | (uint32(b[6]) << 16) | (uint32(b[7]) << 24) + h.FrameContentSize = uint64(d1) | (uint64(d2) << 32) + } + } + + // Frame Header done, we will not fail from now on. + if len(in) < 3 { + return nil + } + tmp, in := in[:3], in[3:] + bh := uint32(tmp[0]) | (uint32(tmp[1]) << 8) | (uint32(tmp[2]) << 16) + h.FirstBlock.Last = bh&1 != 0 + blockType := blockType((bh >> 1) & 3) + // find size. + cSize := int(bh >> 3) + switch blockType { + case blockTypeReserved: + return nil + case blockTypeRLE: + h.FirstBlock.Compressed = true + h.FirstBlock.DecompressedSize = cSize + h.FirstBlock.CompressedSize = 1 + case blockTypeCompressed: + h.FirstBlock.Compressed = true + h.FirstBlock.CompressedSize = cSize + case blockTypeRaw: + h.FirstBlock.DecompressedSize = cSize + h.FirstBlock.CompressedSize = cSize + default: + panic("Invalid block type") + } + + h.FirstBlock.OK = true + return nil +} diff --git a/vendor/github.com/klauspost/compress/zstd/decoder.go b/vendor/github.com/klauspost/compress/zstd/decoder.go index d78be6d4..1d41c25d 100644 --- a/vendor/github.com/klauspost/compress/zstd/decoder.go +++ b/vendor/github.com/klauspost/compress/zstd/decoder.go @@ -5,7 +5,6 @@ package zstd import ( - "bytes" "errors" "io" "sync" @@ -85,6 +84,10 @@ func NewReader(r io.Reader, opts ...DOption) (*Decoder, error) { d.current.output = make(chan decodeOutput, d.o.concurrent) d.current.flushed = true + if r == nil { + d.current.err = ErrDecoderNilInput + } + // Transfer option dicts. d.dicts = make(map[uint32]dict, len(d.o.dicts)) for _, dc := range d.o.dicts { @@ -111,7 +114,7 @@ func NewReader(r io.Reader, opts ...DOption) (*Decoder, error) { // When the stream is done, io.EOF will be returned. func (d *Decoder) Read(p []byte) (int, error) { if d.stream == nil { - return 0, errors.New("no input has been initialized") + return 0, ErrDecoderNilInput } var n int for { @@ -152,12 +155,20 @@ func (d *Decoder) Read(p []byte) (int, error) { // Reset will reset the decoder the supplied stream after the current has finished processing. // Note that this functionality cannot be used after Close has been called. +// Reset can be called with a nil reader to release references to the previous reader. +// After being called with a nil reader, no other operations than Reset or DecodeAll or Close +// should be used. func (d *Decoder) Reset(r io.Reader) error { if d.current.err == ErrDecoderClosed { return d.current.err } + + d.drainOutput() + if r == nil { - return errors.New("nil Reader sent as input") + d.current.err = ErrDecoderNilInput + d.current.flushed = true + return nil } if d.stream == nil { @@ -166,14 +177,14 @@ func (d *Decoder) Reset(r io.Reader) error { go d.startStreamDecoder(d.stream) } - d.drainOutput() - // If bytes buffer and < 1MB, do sync decoding anyway. - if bb, ok := r.(*bytes.Buffer); ok && bb.Len() < 1<<20 { + if bb, ok := r.(byter); ok && bb.Len() < 1<<20 { + var bb2 byter + bb2 = bb if debug { println("*bytes.Buffer detected, doing sync decode, len:", bb.Len()) } - b := bb.Bytes() + b := bb2.Bytes() var dst []byte if cap(d.current.b) > 0 { dst = d.current.b @@ -249,7 +260,7 @@ func (d *Decoder) drainOutput() { // Any error encountered during the write is also returned. func (d *Decoder) WriteTo(w io.Writer) (int64, error) { if d.stream == nil { - return 0, errors.New("no input has been initialized") + return 0, ErrDecoderNilInput } var n int64 for { @@ -323,19 +334,23 @@ func (d *Decoder) DecodeAll(input, dst []byte) ([]byte, error) { } if frame.FrameContentSize > 0 && frame.FrameContentSize < 1<<30 { // Never preallocate moe than 1 GB up front. - if uint64(cap(dst)) < frame.FrameContentSize { + if cap(dst)-len(dst) < int(frame.FrameContentSize) { dst2 := make([]byte, len(dst), len(dst)+int(frame.FrameContentSize)) copy(dst2, dst) dst = dst2 } } if cap(dst) == 0 { - // Allocate window size * 2 by default if nothing is provided and we didn't get frame content size. - size := frame.WindowSize * 2 + // Allocate len(input) * 2 by default if nothing is provided + // and we didn't get frame content size. + size := len(input) * 2 // Cap to 1 MB. if size > 1<<20 { size = 1 << 20 } + if uint64(size) > d.o.maxDecodedSize { + size = int(d.o.maxDecodedSize) + } dst = make([]byte, 0, size) } diff --git a/vendor/github.com/klauspost/compress/zstd/enc_best.go b/vendor/github.com/klauspost/compress/zstd/enc_best.go new file mode 100644 index 00000000..bb71d1ee --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/enc_best.go @@ -0,0 +1,486 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "fmt" + "math/bits" +) + +const ( + bestLongTableBits = 20 // Bits used in the long match table + bestLongTableSize = 1 << bestLongTableBits // Size of the table + + // Note: Increasing the short table bits or making the hash shorter + // can actually lead to compression degradation since it will 'steal' more from the + // long match table and match offsets are quite big. + // This greatly depends on the type of input. + bestShortTableBits = 16 // Bits used in the short match table + bestShortTableSize = 1 << bestShortTableBits // Size of the table +) + +// bestFastEncoder uses 2 tables, one for short matches (5 bytes) and one for long matches. +// The long match table contains the previous entry with the same hash, +// effectively making it a "chain" of length 2. +// When we find a long match we choose between the two values and select the longest. +// When we find a short match, after checking the long, we check if we can find a long at n+1 +// and that it is longer (lazy matching). +type bestFastEncoder struct { + fastBase + table [bestShortTableSize]prevEntry + longTable [bestLongTableSize]prevEntry + dictTable []prevEntry + dictLongTable []prevEntry +} + +// Encode improves compression... +func (e *bestFastEncoder) Encode(blk *blockEnc, src []byte) { + const ( + // Input margin is the number of bytes we read (8) + // and the maximum we will read ahead (2) + inputMargin = 8 + 4 + minNonLiteralBlockSize = 16 + ) + + // Protect against e.cur wraparound. + for e.cur >= bufferReset { + if len(e.hist) == 0 { + for i := range e.table[:] { + e.table[i] = prevEntry{} + } + for i := range e.longTable[:] { + e.longTable[i] = prevEntry{} + } + e.cur = e.maxMatchOff + break + } + // Shift down everything in the table that isn't already too far away. + minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff + for i := range e.table[:] { + v := e.table[i].offset + v2 := e.table[i].prev + if v < minOff { + v = 0 + v2 = 0 + } else { + v = v - e.cur + e.maxMatchOff + if v2 < minOff { + v2 = 0 + } else { + v2 = v2 - e.cur + e.maxMatchOff + } + } + e.table[i] = prevEntry{ + offset: v, + prev: v2, + } + } + for i := range e.longTable[:] { + v := e.longTable[i].offset + v2 := e.longTable[i].prev + if v < minOff { + v = 0 + v2 = 0 + } else { + v = v - e.cur + e.maxMatchOff + if v2 < minOff { + v2 = 0 + } else { + v2 = v2 - e.cur + e.maxMatchOff + } + } + e.longTable[i] = prevEntry{ + offset: v, + prev: v2, + } + } + e.cur = e.maxMatchOff + break + } + + s := e.addBlock(src) + blk.size = len(src) + if len(src) < minNonLiteralBlockSize { + blk.extraLits = len(src) + blk.literals = blk.literals[:len(src)] + copy(blk.literals, src) + return + } + + // Override src + src = e.hist + sLimit := int32(len(src)) - inputMargin + const kSearchStrength = 10 + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := s + cv := load6432(src, s) + + // Relative offsets + offset1 := int32(blk.recentOffsets[0]) + offset2 := int32(blk.recentOffsets[1]) + offset3 := int32(blk.recentOffsets[2]) + + addLiterals := func(s *seq, until int32) { + if until == nextEmit { + return + } + blk.literals = append(blk.literals, src[nextEmit:until]...) + s.litLen = uint32(until - nextEmit) + } + _ = addLiterals + + if debug { + println("recent offsets:", blk.recentOffsets) + } + +encodeLoop: + for { + // We allow the encoder to optionally turn off repeat offsets across blocks + canRepeat := len(blk.sequences) > 2 + + if debugAsserts && canRepeat && offset1 == 0 { + panic("offset0 was 0") + } + + type match struct { + offset int32 + s int32 + length int32 + rep int32 + } + matchAt := func(offset int32, s int32, first uint32, rep int32) match { + if s-offset >= e.maxMatchOff || load3232(src, offset) != first { + return match{offset: offset, s: s} + } + return match{offset: offset, s: s, length: 4 + e.matchlen(s+4, offset+4, src), rep: rep} + } + + bestOf := func(a, b match) match { + aScore := b.s - a.s + a.length + bScore := a.s - b.s + b.length + if a.rep < 0 { + aScore = aScore - int32(bits.Len32(uint32(a.offset)))/8 + } + if b.rep < 0 { + bScore = bScore - int32(bits.Len32(uint32(b.offset)))/8 + } + if aScore >= bScore { + return a + } + return b + } + const goodEnough = 100 + + nextHashL := hash8(cv, bestLongTableBits) + nextHashS := hash4x64(cv, bestShortTableBits) + candidateL := e.longTable[nextHashL] + candidateS := e.table[nextHashS] + + best := bestOf(matchAt(candidateL.offset-e.cur, s, uint32(cv), -1), matchAt(candidateL.prev-e.cur, s, uint32(cv), -1)) + best = bestOf(best, matchAt(candidateS.offset-e.cur, s, uint32(cv), -1)) + best = bestOf(best, matchAt(candidateS.prev-e.cur, s, uint32(cv), -1)) + if canRepeat && best.length < goodEnough { + best = bestOf(best, matchAt(s-offset1+1, s+1, uint32(cv>>8), 1)) + best = bestOf(best, matchAt(s-offset2+1, s+1, uint32(cv>>8), 2)) + best = bestOf(best, matchAt(s-offset3+1, s+1, uint32(cv>>8), 3)) + if best.length > 0 { + best = bestOf(best, matchAt(s-offset1+3, s+3, uint32(cv>>24), 1)) + best = bestOf(best, matchAt(s-offset2+3, s+3, uint32(cv>>24), 2)) + best = bestOf(best, matchAt(s-offset3+3, s+3, uint32(cv>>24), 3)) + } + } + // Load next and check... + e.longTable[nextHashL] = prevEntry{offset: s + e.cur, prev: candidateL.offset} + e.table[nextHashS] = prevEntry{offset: s + e.cur, prev: candidateS.offset} + + // Look far ahead, unless we have a really long match already... + if best.length < goodEnough { + // No match found, move forward on input, no need to check forward... + if best.length < 4 { + s += 1 + (s-nextEmit)>>(kSearchStrength-1) + if s >= sLimit { + break encodeLoop + } + cv = load6432(src, s) + continue + } + + s++ + candidateS = e.table[hash4x64(cv>>8, bestShortTableBits)] + cv = load6432(src, s) + cv2 := load6432(src, s+1) + candidateL = e.longTable[hash8(cv, bestLongTableBits)] + candidateL2 := e.longTable[hash8(cv2, bestLongTableBits)] + + best = bestOf(best, matchAt(candidateS.offset-e.cur, s, uint32(cv), -1)) + best = bestOf(best, matchAt(candidateL.offset-e.cur, s, uint32(cv), -1)) + best = bestOf(best, matchAt(candidateL.prev-e.cur, s, uint32(cv), -1)) + best = bestOf(best, matchAt(candidateL2.offset-e.cur, s+1, uint32(cv2), -1)) + best = bestOf(best, matchAt(candidateL2.prev-e.cur, s+1, uint32(cv2), -1)) + } + + // We have a match, we can store the forward value + if best.rep > 0 { + s = best.s + var seq seq + seq.matchLen = uint32(best.length - zstdMinMatch) + + // We might be able to match backwards. + // Extend as long as we can. + start := best.s + // We end the search early, so we don't risk 0 literals + // and have to do special offset treatment. + startLimit := nextEmit + 1 + + tMin := s - e.maxMatchOff + if tMin < 0 { + tMin = 0 + } + repIndex := best.offset + for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 { + repIndex-- + start-- + seq.matchLen++ + } + addLiterals(&seq, start) + + // rep 0 + seq.offset = uint32(best.rep) + if debugSequences { + println("repeat sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + + // Index match start+1 (long) -> s - 1 + index0 := s + s = best.s + best.length + + nextEmit = s + if s >= sLimit { + if debug { + println("repeat ended", s, best.length) + + } + break encodeLoop + } + // Index skipped... + off := index0 + e.cur + for index0 < s-1 { + cv0 := load6432(src, index0) + h0 := hash8(cv0, bestLongTableBits) + h1 := hash4x64(cv0, bestShortTableBits) + e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} + e.table[h1] = prevEntry{offset: off, prev: e.table[h1].offset} + off++ + index0++ + } + switch best.rep { + case 2: + offset1, offset2 = offset2, offset1 + case 3: + offset1, offset2, offset3 = offset3, offset1, offset2 + } + cv = load6432(src, s) + continue + } + + // A 4-byte match has been found. Update recent offsets. + // We'll later see if more than 4 bytes. + s = best.s + t := best.offset + offset1, offset2, offset3 = s-t, offset1, offset2 + + if debugAsserts && s <= t { + panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) + } + + if debugAsserts && canRepeat && int(offset1) > len(src) { + panic("invalid offset") + } + + // Extend the n-byte match as long as possible. + l := best.length + + // Extend backwards + tMin := s - e.maxMatchOff + if tMin < 0 { + tMin = 0 + } + for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength { + s-- + t-- + l++ + } + + // Write our sequence + var seq seq + seq.litLen = uint32(s - nextEmit) + seq.matchLen = uint32(l - zstdMinMatch) + if seq.litLen > 0 { + blk.literals = append(blk.literals, src[nextEmit:s]...) + } + seq.offset = uint32(s-t) + 3 + s += l + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + nextEmit = s + if s >= sLimit { + break encodeLoop + } + + // Index match start+1 (long) -> s - 1 + index0 := s - l + 1 + // every entry + for index0 < s-1 { + cv0 := load6432(src, index0) + h0 := hash8(cv0, bestLongTableBits) + h1 := hash4x64(cv0, bestShortTableBits) + off := index0 + e.cur + e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} + e.table[h1] = prevEntry{offset: off, prev: e.table[h1].offset} + index0++ + } + + cv = load6432(src, s) + if !canRepeat { + continue + } + + // Check offset 2 + for { + o2 := s - offset2 + if load3232(src, o2) != uint32(cv) { + // Do regular search + break + } + + // Store this, since we have it. + nextHashS := hash4x64(cv, bestShortTableBits) + nextHashL := hash8(cv, bestLongTableBits) + + // We have at least 4 byte match. + // No need to check backwards. We come straight from a match + l := 4 + e.matchlen(s+4, o2+4, src) + + e.longTable[nextHashL] = prevEntry{offset: s + e.cur, prev: e.longTable[nextHashL].offset} + e.table[nextHashS] = prevEntry{offset: s + e.cur, prev: e.table[nextHashS].offset} + seq.matchLen = uint32(l) - zstdMinMatch + seq.litLen = 0 + + // Since litlen is always 0, this is offset 1. + seq.offset = 1 + s += l + nextEmit = s + if debugSequences { + println("sequence", seq, "next s:", s) + } + blk.sequences = append(blk.sequences, seq) + + // Swap offset 1 and 2. + offset1, offset2 = offset2, offset1 + if s >= sLimit { + // Finished + break encodeLoop + } + cv = load6432(src, s) + } + } + + if int(nextEmit) < len(src) { + blk.literals = append(blk.literals, src[nextEmit:]...) + blk.extraLits = len(src) - int(nextEmit) + } + blk.recentOffsets[0] = uint32(offset1) + blk.recentOffsets[1] = uint32(offset2) + blk.recentOffsets[2] = uint32(offset3) + if debug { + println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) + } +} + +// EncodeNoHist will encode a block with no history and no following blocks. +// Most notable difference is that src will not be copied for history and +// we do not need to check for max match length. +func (e *bestFastEncoder) EncodeNoHist(blk *blockEnc, src []byte) { + e.Encode(blk, src) +} + +// ResetDict will reset and set a dictionary if not nil +func (e *bestFastEncoder) Reset(d *dict, singleBlock bool) { + e.resetBase(d, singleBlock) + if d == nil { + return + } + // Init or copy dict table + if len(e.dictTable) != len(e.table) || d.id != e.lastDictID { + if len(e.dictTable) != len(e.table) { + e.dictTable = make([]prevEntry, len(e.table)) + } + end := int32(len(d.content)) - 8 + e.maxMatchOff + for i := e.maxMatchOff; i < end; i += 4 { + const hashLog = bestShortTableBits + + cv := load6432(d.content, i-e.maxMatchOff) + nextHash := hash4x64(cv, hashLog) // 0 -> 4 + nextHash1 := hash4x64(cv>>8, hashLog) // 1 -> 5 + nextHash2 := hash4x64(cv>>16, hashLog) // 2 -> 6 + nextHash3 := hash4x64(cv>>24, hashLog) // 3 -> 7 + e.dictTable[nextHash] = prevEntry{ + prev: e.dictTable[nextHash].offset, + offset: i, + } + e.dictTable[nextHash1] = prevEntry{ + prev: e.dictTable[nextHash1].offset, + offset: i + 1, + } + e.dictTable[nextHash2] = prevEntry{ + prev: e.dictTable[nextHash2].offset, + offset: i + 2, + } + e.dictTable[nextHash3] = prevEntry{ + prev: e.dictTable[nextHash3].offset, + offset: i + 3, + } + } + e.lastDictID = d.id + } + + // Init or copy dict table + if len(e.dictLongTable) != len(e.longTable) || d.id != e.lastDictID { + if len(e.dictLongTable) != len(e.longTable) { + e.dictLongTable = make([]prevEntry, len(e.longTable)) + } + if len(d.content) >= 8 { + cv := load6432(d.content, 0) + h := hash8(cv, bestLongTableBits) + e.dictLongTable[h] = prevEntry{ + offset: e.maxMatchOff, + prev: e.dictLongTable[h].offset, + } + + end := int32(len(d.content)) - 8 + e.maxMatchOff + off := 8 // First to read + for i := e.maxMatchOff + 1; i < end; i++ { + cv = cv>>8 | (uint64(d.content[off]) << 56) + h := hash8(cv, bestLongTableBits) + e.dictLongTable[h] = prevEntry{ + offset: i, + prev: e.dictLongTable[h].offset, + } + off++ + } + } + e.lastDictID = d.id + } + // Reset table to initial state + copy(e.longTable[:], e.dictLongTable) + + e.cur = e.maxMatchOff + // Reset table to initial state + copy(e.table[:], e.dictTable) +} diff --git a/vendor/github.com/klauspost/compress/zstd/enc_fast.go b/vendor/github.com/klauspost/compress/zstd/enc_fast.go index 0b301df4..0045016d 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_fast.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_fast.go @@ -78,7 +78,7 @@ func (e *fastEncoder) Encode(blk *blockEnc, src []byte) { // TEMPLATE const hashLog = tableBits // seems global, but would be nice to tweak. - const kSearchStrength = 8 + const kSearchStrength = 7 // nextEmit is where in src the next emitLiteral should start from. nextEmit := s diff --git a/vendor/github.com/klauspost/compress/zstd/encoder_options.go b/vendor/github.com/klauspost/compress/zstd/encoder_options.go index 57920616..a7312f42 100644 --- a/vendor/github.com/klauspost/compress/zstd/encoder_options.go +++ b/vendor/github.com/klauspost/compress/zstd/encoder_options.go @@ -30,12 +30,13 @@ type encoderOptions struct { func (o *encoderOptions) setDefault() { *o = encoderOptions{ // use less ram: true for now, but may change. - concurrent: runtime.GOMAXPROCS(0), - crc: true, - single: nil, - blockSize: 1 << 16, - windowSize: 8 << 20, - level: SpeedDefault, + concurrent: runtime.GOMAXPROCS(0), + crc: true, + single: nil, + blockSize: 1 << 16, + windowSize: 8 << 20, + level: SpeedDefault, + allLitEntropy: true, } } @@ -46,6 +47,8 @@ func (o encoderOptions) encoder() encoder { return &doubleFastEncoder{fastEncoder: fastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize)}}} case SpeedBetterCompression: return &betterFastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize)}} + case SpeedBestCompression: + return &bestFastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize)}} case SpeedFastest: return &fastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize)}} } @@ -142,20 +145,20 @@ const ( // By using this, notice that CPU usage may go up in the future. SpeedBetterCompression + // SpeedBestCompression will choose the best available compression option. + // This will offer the best compression no matter the CPU cost. + SpeedBestCompression + // speedLast should be kept as the last actual compression option. // The is not for external usage, but is used to keep track of the valid options. speedLast - - // SpeedBestCompression will choose the best available compression option. - // For now this is not implemented. - SpeedBestCompression = SpeedBetterCompression ) // EncoderLevelFromString will convert a string representation of an encoding level back // to a compression level. The compare is not case sensitive. // If the string wasn't recognized, (false, SpeedDefault) will be returned. func EncoderLevelFromString(s string) (bool, EncoderLevel) { - for l := EncoderLevel(speedNotSet + 1); l < speedLast; l++ { + for l := speedNotSet + 1; l < speedLast; l++ { if strings.EqualFold(s, l.String()) { return true, l } @@ -172,7 +175,9 @@ func EncoderLevelFromZstd(level int) EncoderLevel { return SpeedFastest case level >= 3 && level < 6: return SpeedDefault - case level > 5: + case level >= 6 && level < 10: + return SpeedBetterCompression + case level >= 10: return SpeedBetterCompression } return SpeedDefault @@ -187,6 +192,8 @@ func (e EncoderLevel) String() string { return "default" case SpeedBetterCompression: return "better" + case SpeedBestCompression: + return "best" default: return "invalid" } @@ -208,6 +215,8 @@ func WithEncoderLevel(l EncoderLevel) EOption { o.windowSize = 8 << 20 case SpeedBetterCompression: o.windowSize = 16 << 20 + case SpeedBestCompression: + o.windowSize = 32 << 20 } } if !o.customALEntropy { diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec.go b/vendor/github.com/klauspost/compress/zstd/seqdec.go index b5c8ef13..1dd39e63 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec.go +++ b/vendor/github.com/klauspost/compress/zstd/seqdec.go @@ -181,11 +181,18 @@ func (s *sequenceDecs) decode(seqs int, br *bitReader, hist []byte) error { return fmt.Errorf("output (%d) bigger than max block size", size) } if size > cap(s.out) { - // Not enough size, will be extremely rarely triggered, + // Not enough size, which can happen under high volume block streaming conditions // but could be if destination slice is too small for sync operations. - // We add maxBlockSize to the capacity. - s.out = append(s.out, make([]byte, maxBlockSize)...) - s.out = s.out[:len(s.out)-maxBlockSize] + // over-allocating here can create a large amount of GC pressure so we try to keep + // it as contained as possible + used := len(s.out) - startSize + addBytes := 256 + ll + ml + used>>2 + // Clamp to max block size. + if used+addBytes > maxBlockSize { + addBytes = maxBlockSize - used + } + s.out = append(s.out, make([]byte, addBytes)...) + s.out = s.out[:len(s.out)-addBytes] } if ml > maxMatchLen { return fmt.Errorf("match len (%d) bigger than max allowed length", ml) diff --git a/vendor/github.com/klauspost/compress/zstd/zstd.go b/vendor/github.com/klauspost/compress/zstd/zstd.go index 0807719c..9056beef 100644 --- a/vendor/github.com/klauspost/compress/zstd/zstd.go +++ b/vendor/github.com/klauspost/compress/zstd/zstd.go @@ -4,6 +4,7 @@ package zstd import ( + "bytes" "errors" "log" "math" @@ -73,6 +74,10 @@ var ( // ErrDecoderClosed will be returned if the Decoder was used after // Close has been called. ErrDecoderClosed = errors.New("decoder used after Close") + + // ErrDecoderNilInput is returned when a nil Reader was provided + // and an operation other than Reset/DecodeAll/Close was attempted. + ErrDecoderNilInput = errors.New("nil input provided as reader") ) func println(a ...interface{}) { @@ -142,3 +147,10 @@ func load64(b []byte, i int) uint64 { return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 } + +type byter interface { + Bytes() []byte + Len() int +} + +var _ byter = &bytes.Buffer{} diff --git a/vendor/github.com/nats-io/jwt/.gitignore b/vendor/github.com/nats-io/jwt/.gitignore deleted file mode 100644 index 7117a678..00000000 --- a/vendor/github.com/nats-io/jwt/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, build with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# IDE Files -.vscode -.idea/ \ No newline at end of file diff --git a/vendor/github.com/nats-io/jwt/.travis.yml b/vendor/github.com/nats-io/jwt/.travis.yml deleted file mode 100644 index 198213d2..00000000 --- a/vendor/github.com/nats-io/jwt/.travis.yml +++ /dev/null @@ -1,21 +0,0 @@ -language: go -go: -- 1.14.x -- 1.13.x - -install: -- go get -t ./... -- go get github.com/mattn/goveralls -- go get github.com/wadey/gocovmerge -- go get -u honnef.co/go/tools/cmd/staticcheck -- go get -u github.com/client9/misspell/cmd/misspell - -before_script: -- $(exit $(go fmt ./... | wc -l)) -- go vet ./... -- misspell -error -locale US . -- staticcheck ./... - -script: -- go test -v -race ./... -- if [[ "$TRAVIS_GO_VERSION" =~ 1.13 ]]; then ./scripts/cov.sh TRAVIS; fi diff --git a/vendor/github.com/nats-io/jwt/LICENSE b/vendor/github.com/nats-io/jwt/LICENSE deleted file mode 100644 index 261eeb9e..00000000 --- a/vendor/github.com/nats-io/jwt/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/nats-io/jwt/Makefile b/vendor/github.com/nats-io/jwt/Makefile deleted file mode 100644 index 95e468e1..00000000 --- a/vendor/github.com/nats-io/jwt/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -.PHONY: test cover - -build: - go build - -test: - gofmt -s -w *.go - goimports -w *.go - go vet ./... - go test -v - go test -v --race - staticcheck ./... - -fmt: - gofmt -w -s *.go - -cover: - go test -v -covermode=count -coverprofile=coverage.out - go tool cover -html=coverage.out diff --git a/vendor/github.com/nats-io/jwt/README.md b/vendor/github.com/nats-io/jwt/README.md deleted file mode 100644 index d3cb88ac..00000000 --- a/vendor/github.com/nats-io/jwt/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# JWT -A [JWT](https://jwt.io/) implementation that uses [nkeys](https://github.com/nats-io/nkeys) to digitally sign JWT tokens. -Nkeys use [Ed25519](https://ed25519.cr.yp.to/) to provide authentication of JWT claims. - - -[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) -[![ReportCard](http://goreportcard.com/badge/nats-io/jwt)](http://goreportcard.com/report/nats-io/jwt) -[![Build Status](https://travis-ci.org/nats-io/jwt.svg?branch=master)](http://travis-ci.org/nats-io/jwt) -[![GoDoc](http://godoc.org/github.com/nats-io/jwt?status.png)](http://godoc.org/github.com/nats-io/jwt) -[![Coverage Status](https://coveralls.io/repos/github/nats-io/jwt/badge.svg?branch=master&t=NmEFup)](https://coveralls.io/github/nats-io/jwt?branch=master) - -```go -// Need a private key to sign the claim, nkeys makes it easy to create -kp, err := nkeys.CreateAccount() -if err != nil { - t.Fatal("unable to create account key", err) -} - -pk, err := kp.PublicKey() -if err != nil { - t.Fatal("error getting public key", err) -} - -// create a new claim -claims := NewAccountClaims(pk) -claims.Expires = time.Now().Add(time.Duration(time.Hour)).Unix() - - -// add details by modifying claims.Account - -// serialize the claim to a JWT token -token, err := claims.Encode(kp) -if err != nil { - t.Fatal("error encoding token", err) -} - -// on the receiving side, decode the token -c, err := DecodeAccountClaims(token) -if err != nil { - t.Fatal(err) -} - -// if the token was decoded, it means that it -// validated and it wasn't tampered. the remaining and -// required test is to insure the issuer is trusted -pk, err := kp.PublicKey() -if err != nil { - t.Fatalf("unable to read public key: %v", err) -} - -if c.Issuer != pk { - t.Fatalf("the public key is not trusted") -} -``` \ No newline at end of file diff --git a/vendor/github.com/nats-io/jwt/ReleaseNotes.md b/vendor/github.com/nats-io/jwt/ReleaseNotes.md deleted file mode 100644 index 500965ea..00000000 --- a/vendor/github.com/nats-io/jwt/ReleaseNotes.md +++ /dev/null @@ -1,5 +0,0 @@ -# Release Notes - -## 0.3.0 - -* Removed revocation claims in favor of timestamp-based revocation maps in account and export claims. diff --git a/vendor/github.com/nats-io/jwt/account_claims.go b/vendor/github.com/nats-io/jwt/account_claims.go deleted file mode 100644 index 90841fa3..00000000 --- a/vendor/github.com/nats-io/jwt/account_claims.go +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2018-2019 The NATS Authors - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jwt - -import ( - "errors" - "sort" - "time" - - "github.com/nats-io/nkeys" -) - -// NoLimit is used to indicate a limit field is unlimited in value. -const NoLimit = -1 - -// OperatorLimits are used to limit access by an account -type OperatorLimits struct { - Subs int64 `json:"subs,omitempty"` // Max number of subscriptions - Conn int64 `json:"conn,omitempty"` // Max number of active connections - LeafNodeConn int64 `json:"leaf,omitempty"` // Max number of active leaf node connections - Imports int64 `json:"imports,omitempty"` // Max number of imports - Exports int64 `json:"exports,omitempty"` // Max number of exports - Data int64 `json:"data,omitempty"` // Max number of bytes - Payload int64 `json:"payload,omitempty"` // Max message payload - WildcardExports bool `json:"wildcards,omitempty"` // Are wildcards allowed in exports -} - -// IsEmpty returns true if all of the limits are 0/false. -func (o *OperatorLimits) IsEmpty() bool { - return *o == OperatorLimits{} -} - -// IsUnlimited returns true if all limits are -func (o *OperatorLimits) IsUnlimited() bool { - return *o == OperatorLimits{NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, true} -} - -// Validate checks that the operator limits contain valid values -func (o *OperatorLimits) Validate(vr *ValidationResults) { - // negative values mean unlimited, so all numbers are valid -} - -// Account holds account specific claims data -type Account struct { - Imports Imports `json:"imports,omitempty"` - Exports Exports `json:"exports,omitempty"` - Identities []Identity `json:"identity,omitempty"` - Limits OperatorLimits `json:"limits,omitempty"` - SigningKeys StringList `json:"signing_keys,omitempty"` - Revocations RevocationList `json:"revocations,omitempty"` -} - -// Validate checks if the account is valid, based on the wrapper -func (a *Account) Validate(acct *AccountClaims, vr *ValidationResults) { - a.Imports.Validate(acct.Subject, vr) - a.Exports.Validate(vr) - a.Limits.Validate(vr) - - for _, i := range a.Identities { - i.Validate(vr) - } - - if !a.Limits.IsEmpty() && a.Limits.Imports >= 0 && int64(len(a.Imports)) > a.Limits.Imports { - vr.AddError("the account contains more imports than allowed by the operator") - } - - // Check Imports and Exports for limit violations. - if a.Limits.Imports != NoLimit { - if int64(len(a.Imports)) > a.Limits.Imports { - vr.AddError("the account contains more imports than allowed by the operator") - } - } - if a.Limits.Exports != NoLimit { - if int64(len(a.Exports)) > a.Limits.Exports { - vr.AddError("the account contains more exports than allowed by the operator") - } - // Check for wildcard restrictions - if !a.Limits.WildcardExports { - for _, ex := range a.Exports { - if ex.Subject.HasWildCards() { - vr.AddError("the account contains wildcard exports that are not allowed by the operator") - } - } - } - } - - for _, k := range a.SigningKeys { - if !nkeys.IsValidPublicAccountKey(k) { - vr.AddError("%s is not an account public key", k) - } - } -} - -// AccountClaims defines the body of an account JWT -type AccountClaims struct { - ClaimsData - Account `json:"nats,omitempty"` -} - -// NewAccountClaims creates a new account JWT -func NewAccountClaims(subject string) *AccountClaims { - if subject == "" { - return nil - } - c := &AccountClaims{} - // Set to unlimited to start. We do it this way so we get compiler - // errors if we add to the OperatorLimits. - c.Limits = OperatorLimits{NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, true} - c.Subject = subject - return c -} - -// Encode converts account claims into a JWT string -func (a *AccountClaims) Encode(pair nkeys.KeyPair) (string, error) { - if !nkeys.IsValidPublicAccountKey(a.Subject) { - return "", errors.New("expected subject to be account public key") - } - sort.Sort(a.Exports) - sort.Sort(a.Imports) - a.ClaimsData.Type = AccountClaim - return a.ClaimsData.Encode(pair, a) -} - -// DecodeAccountClaims decodes account claims from a JWT string -func DecodeAccountClaims(token string) (*AccountClaims, error) { - v := AccountClaims{} - if err := Decode(token, &v); err != nil { - return nil, err - } - return &v, nil -} - -func (a *AccountClaims) String() string { - return a.ClaimsData.String(a) -} - -// Payload pulls the accounts specific payload out of the claims -func (a *AccountClaims) Payload() interface{} { - return &a.Account -} - -// Validate checks the accounts contents -func (a *AccountClaims) Validate(vr *ValidationResults) { - a.ClaimsData.Validate(vr) - a.Account.Validate(a, vr) - - if nkeys.IsValidPublicAccountKey(a.ClaimsData.Issuer) { - if len(a.Identities) > 0 { - vr.AddWarning("self-signed account JWTs shouldn't contain identity proofs") - } - if !a.Limits.IsEmpty() { - vr.AddWarning("self-signed account JWTs shouldn't contain operator limits") - } - } -} - -// ExpectedPrefixes defines the types that can encode an account jwt, account and operator -func (a *AccountClaims) ExpectedPrefixes() []nkeys.PrefixByte { - return []nkeys.PrefixByte{nkeys.PrefixByteAccount, nkeys.PrefixByteOperator} -} - -// Claims returns the accounts claims data -func (a *AccountClaims) Claims() *ClaimsData { - return &a.ClaimsData -} - -// DidSign checks the claims against the account's public key and its signing keys -func (a *AccountClaims) DidSign(op Claims) bool { - if op != nil { - issuer := op.Claims().Issuer - if issuer == a.Subject { - return true - } - return a.SigningKeys.Contains(issuer) - } - return false -} - -// Revoke enters a revocation by publickey using time.Now(). -func (a *AccountClaims) Revoke(pubKey string) { - a.RevokeAt(pubKey, time.Now()) -} - -// RevokeAt enters a revocation by public key and timestamp into this account -// This will revoke all jwt issued for pubKey, prior to timestamp -// If there is already a revocation for this public key that is newer, it is kept. -func (a *AccountClaims) RevokeAt(pubKey string, timestamp time.Time) { - if a.Revocations == nil { - a.Revocations = RevocationList{} - } - - a.Revocations.Revoke(pubKey, timestamp) -} - -// ClearRevocation removes any revocation for the public key -func (a *AccountClaims) ClearRevocation(pubKey string) { - a.Revocations.ClearRevocation(pubKey) -} - -// IsRevokedAt checks if the public key is in the revoked list with a timestamp later than the one passed in. -// Generally this method is called with the subject and issue time of the jwt to be tested. -// DO NOT pass time.Now(), it will not produce a stable/expected response. -func (a *AccountClaims) IsRevokedAt(pubKey string, timestamp time.Time) bool { - return a.Revocations.IsRevoked(pubKey, timestamp) -} - -// IsRevoked does not perform a valid check. Use IsRevokedAt instead. -func (a *AccountClaims) IsRevoked(_ string) bool { - return true -} - -// IsClaimRevoked checks if the account revoked the claim passed in. -// Invalid claims (nil, no Subject or IssuedAt) will return true. -func (a *AccountClaims) IsClaimRevoked(claim *UserClaims) bool { - if claim == nil || claim.IssuedAt == 0 || claim.Subject == "" { - return true - } - return a.Revocations.IsRevoked(claim.Subject, time.Unix(claim.IssuedAt, 0)) -} diff --git a/vendor/github.com/nats-io/jwt/activation_claims.go b/vendor/github.com/nats-io/jwt/activation_claims.go deleted file mode 100644 index 99228a75..00000000 --- a/vendor/github.com/nats-io/jwt/activation_claims.go +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2018 The NATS Authors - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jwt - -import ( - "crypto/sha256" - "encoding/base32" - "errors" - "fmt" - "strings" - - "github.com/nats-io/nkeys" -) - -// Activation defines the custom parts of an activation claim -type Activation struct { - ImportSubject Subject `json:"subject,omitempty"` - ImportType ExportType `json:"type,omitempty"` - Limits -} - -// IsService returns true if an Activation is for a service -func (a *Activation) IsService() bool { - return a.ImportType == Service -} - -// IsStream returns true if an Activation is for a stream -func (a *Activation) IsStream() bool { - return a.ImportType == Stream -} - -// Validate checks the exports and limits in an activation JWT -func (a *Activation) Validate(vr *ValidationResults) { - if !a.IsService() && !a.IsStream() { - vr.AddError("invalid export type: %q", a.ImportType) - } - - if a.IsService() { - if a.ImportSubject.HasWildCards() { - vr.AddError("services cannot have wildcard subject: %q", a.ImportSubject) - } - } - - a.ImportSubject.Validate(vr) - a.Limits.Validate(vr) -} - -// ActivationClaims holds the data specific to an activation JWT -type ActivationClaims struct { - ClaimsData - Activation `json:"nats,omitempty"` - // IssuerAccount stores the public key for the account the issuer represents. - // When set, the claim was issued by a signing key. - IssuerAccount string `json:"issuer_account,omitempty"` -} - -// NewActivationClaims creates a new activation claim with the provided sub -func NewActivationClaims(subject string) *ActivationClaims { - if subject == "" { - return nil - } - ac := &ActivationClaims{} - ac.Subject = subject - return ac -} - -// Encode turns an activation claim into a JWT strimg -func (a *ActivationClaims) Encode(pair nkeys.KeyPair) (string, error) { - if !nkeys.IsValidPublicAccountKey(a.ClaimsData.Subject) { - return "", errors.New("expected subject to be an account") - } - a.ClaimsData.Type = ActivationClaim - return a.ClaimsData.Encode(pair, a) -} - -// DecodeActivationClaims tries to create an activation claim from a JWT string -func DecodeActivationClaims(token string) (*ActivationClaims, error) { - v := ActivationClaims{} - if err := Decode(token, &v); err != nil { - return nil, err - } - return &v, nil -} - -// Payload returns the activation specific part of the JWT -func (a *ActivationClaims) Payload() interface{} { - return a.Activation -} - -// Validate checks the claims -func (a *ActivationClaims) Validate(vr *ValidationResults) { - a.ClaimsData.Validate(vr) - a.Activation.Validate(vr) - if a.IssuerAccount != "" && !nkeys.IsValidPublicAccountKey(a.IssuerAccount) { - vr.AddError("account_id is not an account public key") - } -} - -// ExpectedPrefixes defines the types that can sign an activation jwt, account and oeprator -func (a *ActivationClaims) ExpectedPrefixes() []nkeys.PrefixByte { - return []nkeys.PrefixByte{nkeys.PrefixByteAccount, nkeys.PrefixByteOperator} -} - -// Claims returns the generic part of the JWT -func (a *ActivationClaims) Claims() *ClaimsData { - return &a.ClaimsData -} - -func (a *ActivationClaims) String() string { - return a.ClaimsData.String(a) -} - -// HashID returns a hash of the claims that can be used to identify it. -// The hash is calculated by creating a string with -// issuerPubKey.subjectPubKey. and constructing the sha-256 hash and base32 encoding that. -// is the exported subject, minus any wildcards, so foo.* becomes foo. -// the one special case is that if the export start with "*" or is ">" the "_" -func (a *ActivationClaims) HashID() (string, error) { - - if a.Issuer == "" || a.Subject == "" || a.ImportSubject == "" { - return "", fmt.Errorf("not enough data in the activaion claims to create a hash") - } - - subject := cleanSubject(string(a.ImportSubject)) - base := fmt.Sprintf("%s.%s.%s", a.Issuer, a.Subject, subject) - h := sha256.New() - h.Write([]byte(base)) - sha := h.Sum(nil) - hash := base32.StdEncoding.EncodeToString(sha) - - return hash, nil -} - -func cleanSubject(subject string) string { - split := strings.Split(subject, ".") - cleaned := "" - - for i, tok := range split { - if tok == "*" || tok == ">" { - if i == 0 { - cleaned = "_" - break - } - - cleaned = strings.Join(split[:i], ".") - break - } - } - if cleaned == "" { - cleaned = subject - } - return cleaned -} diff --git a/vendor/github.com/nats-io/jwt/claims.go b/vendor/github.com/nats-io/jwt/claims.go deleted file mode 100644 index d402bcc5..00000000 --- a/vendor/github.com/nats-io/jwt/claims.go +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright 2018-2019 The NATS Authors - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jwt - -import ( - "crypto/sha512" - "encoding/base32" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "strings" - "time" - - "github.com/nats-io/nkeys" -) - -// ClaimType is used to indicate the type of JWT being stored in a Claim -type ClaimType string - -const ( - // AccountClaim is the type of an Account JWT - AccountClaim = "account" - //ActivationClaim is the type of an activation JWT - ActivationClaim = "activation" - //UserClaim is the type of an user JWT - UserClaim = "user" - //ServerClaim is the type of an server JWT - ServerClaim = "server" - //ClusterClaim is the type of an cluster JWT - ClusterClaim = "cluster" - //OperatorClaim is the type of an operator JWT - OperatorClaim = "operator" -) - -// Claims is a JWT claims -type Claims interface { - Claims() *ClaimsData - Encode(kp nkeys.KeyPair) (string, error) - ExpectedPrefixes() []nkeys.PrefixByte - Payload() interface{} - String() string - Validate(vr *ValidationResults) - Verify(payload string, sig []byte) bool -} - -// ClaimsData is the base struct for all claims -type ClaimsData struct { - Audience string `json:"aud,omitempty"` - Expires int64 `json:"exp,omitempty"` - ID string `json:"jti,omitempty"` - IssuedAt int64 `json:"iat,omitempty"` - Issuer string `json:"iss,omitempty"` - Name string `json:"name,omitempty"` - NotBefore int64 `json:"nbf,omitempty"` - Subject string `json:"sub,omitempty"` - Tags TagList `json:"tags,omitempty"` - Type ClaimType `json:"type,omitempty"` -} - -// Prefix holds the prefix byte for an NKey -type Prefix struct { - nkeys.PrefixByte -} - -func encodeToString(d []byte) string { - return base64.RawURLEncoding.EncodeToString(d) -} - -func decodeString(s string) ([]byte, error) { - return base64.RawURLEncoding.DecodeString(s) -} - -func serialize(v interface{}) (string, error) { - j, err := json.Marshal(v) - if err != nil { - return "", err - } - return encodeToString(j), nil -} - -func (c *ClaimsData) doEncode(header *Header, kp nkeys.KeyPair, claim Claims) (string, error) { - if header == nil { - return "", errors.New("header is required") - } - - if kp == nil { - return "", errors.New("keypair is required") - } - - if c.Subject == "" { - return "", errors.New("subject is not set") - } - - h, err := serialize(header) - if err != nil { - return "", err - } - - issuerBytes, err := kp.PublicKey() - if err != nil { - return "", err - } - - prefixes := claim.ExpectedPrefixes() - if prefixes != nil { - ok := false - for _, p := range prefixes { - switch p { - case nkeys.PrefixByteAccount: - if nkeys.IsValidPublicAccountKey(issuerBytes) { - ok = true - } - case nkeys.PrefixByteOperator: - if nkeys.IsValidPublicOperatorKey(issuerBytes) { - ok = true - } - case nkeys.PrefixByteServer: - if nkeys.IsValidPublicServerKey(issuerBytes) { - ok = true - } - case nkeys.PrefixByteCluster: - if nkeys.IsValidPublicClusterKey(issuerBytes) { - ok = true - } - case nkeys.PrefixByteUser: - if nkeys.IsValidPublicUserKey(issuerBytes) { - ok = true - } - } - } - if !ok { - return "", fmt.Errorf("unable to validate expected prefixes - %v", prefixes) - } - } - - c.Issuer = string(issuerBytes) - c.IssuedAt = time.Now().UTC().Unix() - - c.ID, err = c.hash() - if err != nil { - return "", err - } - - payload, err := serialize(claim) - if err != nil { - return "", err - } - - sig, err := kp.Sign([]byte(payload)) - if err != nil { - return "", err - } - eSig := encodeToString(sig) - return fmt.Sprintf("%s.%s.%s", h, payload, eSig), nil -} - -func (c *ClaimsData) hash() (string, error) { - j, err := json.Marshal(c) - if err != nil { - return "", err - } - h := sha512.New512_256() - h.Write(j) - return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(h.Sum(nil)), nil -} - -// Encode encodes a claim into a JWT token. The claim is signed with the -// provided nkey's private key -func (c *ClaimsData) Encode(kp nkeys.KeyPair, payload Claims) (string, error) { - return c.doEncode(&Header{TokenTypeJwt, AlgorithmNkey}, kp, payload) -} - -// Returns a JSON representation of the claim -func (c *ClaimsData) String(claim interface{}) string { - j, err := json.MarshalIndent(claim, "", " ") - if err != nil { - return "" - } - return string(j) -} - -func parseClaims(s string, target Claims) error { - h, err := decodeString(s) - if err != nil { - return err - } - return json.Unmarshal(h, &target) -} - -// Verify verifies that the encoded payload was signed by the -// provided public key. Verify is called automatically with -// the claims portion of the token and the public key in the claim. -// Client code need to insure that the public key in the -// claim is trusted. -func (c *ClaimsData) Verify(payload string, sig []byte) bool { - // decode the public key - kp, err := nkeys.FromPublicKey(c.Issuer) - if err != nil { - return false - } - if err := kp.Verify([]byte(payload), sig); err != nil { - return false - } - return true -} - -// Validate checks a claim to make sure it is valid. Validity checks -// include expiration and not before constraints. -func (c *ClaimsData) Validate(vr *ValidationResults) { - now := time.Now().UTC().Unix() - if c.Expires > 0 && now > c.Expires { - vr.AddTimeCheck("claim is expired") - } - - if c.NotBefore > 0 && c.NotBefore > now { - vr.AddTimeCheck("claim is not yet valid") - } -} - -// IsSelfSigned returns true if the claims issuer is the subject -func (c *ClaimsData) IsSelfSigned() bool { - return c.Issuer == c.Subject -} - -// Decode takes a JWT string decodes it and validates it -// and return the embedded Claims. If the token header -// doesn't match the expected algorithm, or the claim is -// not valid or verification fails an error is returned. -func Decode(token string, target Claims) error { - // must have 3 chunks - chunks := strings.Split(token, ".") - if len(chunks) != 3 { - return errors.New("expected 3 chunks") - } - - _, err := parseHeaders(chunks[0]) - if err != nil { - return err - } - - if err := parseClaims(chunks[1], target); err != nil { - return err - } - - sig, err := decodeString(chunks[2]) - if err != nil { - return err - } - - if !target.Verify(chunks[1], sig) { - return errors.New("claim failed signature verification") - } - - prefixes := target.ExpectedPrefixes() - if prefixes != nil { - ok := false - issuer := target.Claims().Issuer - for _, p := range prefixes { - switch p { - case nkeys.PrefixByteAccount: - if nkeys.IsValidPublicAccountKey(issuer) { - ok = true - } - case nkeys.PrefixByteOperator: - if nkeys.IsValidPublicOperatorKey(issuer) { - ok = true - } - case nkeys.PrefixByteServer: - if nkeys.IsValidPublicServerKey(issuer) { - ok = true - } - case nkeys.PrefixByteCluster: - if nkeys.IsValidPublicClusterKey(issuer) { - ok = true - } - case nkeys.PrefixByteUser: - if nkeys.IsValidPublicUserKey(issuer) { - ok = true - } - } - } - if !ok { - return fmt.Errorf("unable to validate expected prefixes - %v", prefixes) - } - } - - return nil -} diff --git a/vendor/github.com/nats-io/jwt/cluster_claims.go b/vendor/github.com/nats-io/jwt/cluster_claims.go deleted file mode 100644 index bbfcf06f..00000000 --- a/vendor/github.com/nats-io/jwt/cluster_claims.go +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2018 The NATS Authors - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jwt - -import ( - "errors" - - "github.com/nats-io/nkeys" -) - -// Cluster stores the cluster specific elements of a cluster JWT -type Cluster struct { - Trust []string `json:"identity,omitempty"` - Accounts []string `json:"accts,omitempty"` - AccountURL string `json:"accturl,omitempty"` - OperatorURL string `json:"opurl,omitempty"` -} - -// Validate checks the cluster and permissions for a cluster JWT -func (c *Cluster) Validate(vr *ValidationResults) { - // fixme validate cluster data -} - -// ClusterClaims defines the data in a cluster JWT -type ClusterClaims struct { - ClaimsData - Cluster `json:"nats,omitempty"` -} - -// NewClusterClaims creates a new cluster JWT with the specified subject/public key -func NewClusterClaims(subject string) *ClusterClaims { - if subject == "" { - return nil - } - c := &ClusterClaims{} - c.Subject = subject - return c -} - -// Encode tries to turn the cluster claims into a JWT string -func (c *ClusterClaims) Encode(pair nkeys.KeyPair) (string, error) { - if !nkeys.IsValidPublicClusterKey(c.Subject) { - return "", errors.New("expected subject to be a cluster public key") - } - c.ClaimsData.Type = ClusterClaim - return c.ClaimsData.Encode(pair, c) -} - -// DecodeClusterClaims tries to parse cluster claims from a JWT string -func DecodeClusterClaims(token string) (*ClusterClaims, error) { - v := ClusterClaims{} - if err := Decode(token, &v); err != nil { - return nil, err - } - return &v, nil -} - -func (c *ClusterClaims) String() string { - return c.ClaimsData.String(c) -} - -// Payload returns the cluster specific data -func (c *ClusterClaims) Payload() interface{} { - return &c.Cluster -} - -// Validate checks the generic and cluster data in the cluster claims -func (c *ClusterClaims) Validate(vr *ValidationResults) { - c.ClaimsData.Validate(vr) - c.Cluster.Validate(vr) -} - -// ExpectedPrefixes defines the types that can encode a cluster JWT, operator or cluster -func (c *ClusterClaims) ExpectedPrefixes() []nkeys.PrefixByte { - return []nkeys.PrefixByte{nkeys.PrefixByteOperator, nkeys.PrefixByteCluster} -} - -// Claims returns the generic data -func (c *ClusterClaims) Claims() *ClaimsData { - return &c.ClaimsData -} diff --git a/vendor/github.com/nats-io/jwt/creds_utils.go b/vendor/github.com/nats-io/jwt/creds_utils.go deleted file mode 100644 index 265057f1..00000000 --- a/vendor/github.com/nats-io/jwt/creds_utils.go +++ /dev/null @@ -1,203 +0,0 @@ -package jwt - -import ( - "bytes" - "errors" - "fmt" - "regexp" - "strings" - - "github.com/nats-io/nkeys" -) - -// DecorateJWT returns a decorated JWT that describes the kind of JWT -func DecorateJWT(jwtString string) ([]byte, error) { - gc, err := DecodeGeneric(jwtString) - if err != nil { - return nil, err - } - return formatJwt(string(gc.Type), jwtString) -} - -func formatJwt(kind string, jwtString string) ([]byte, error) { - templ := `-----BEGIN NATS %s JWT----- -%s -------END NATS %s JWT------ - -` - w := bytes.NewBuffer(nil) - kind = strings.ToUpper(kind) - _, err := fmt.Fprintf(w, templ, kind, jwtString, kind) - if err != nil { - return nil, err - } - return w.Bytes(), nil -} - -// DecorateSeed takes a seed and returns a string that wraps -// the seed in the form: -// ************************* IMPORTANT ************************* -// NKEY Seed printed below can be used sign and prove identity. -// NKEYs are sensitive and should be treated as secrets. -// -// -----BEGIN USER NKEY SEED----- -// SUAIO3FHUX5PNV2LQIIP7TZ3N4L7TX3W53MQGEIVYFIGA635OZCKEYHFLM -// ------END USER NKEY SEED------ -func DecorateSeed(seed []byte) ([]byte, error) { - w := bytes.NewBuffer(nil) - ts := bytes.TrimSpace(seed) - pre := string(ts[0:2]) - kind := "" - switch pre { - case "SU": - kind = "USER" - case "SA": - kind = "ACCOUNT" - case "SO": - kind = "OPERATOR" - default: - return nil, errors.New("seed is not an operator, account or user seed") - } - header := `************************* IMPORTANT ************************* -NKEY Seed printed below can be used to sign and prove identity. -NKEYs are sensitive and should be treated as secrets. - ------BEGIN %s NKEY SEED----- -` - _, err := fmt.Fprintf(w, header, kind) - if err != nil { - return nil, err - } - w.Write(ts) - - footer := ` -------END %s NKEY SEED------ - -************************************************************* -` - _, err = fmt.Fprintf(w, footer, kind) - if err != nil { - return nil, err - } - return w.Bytes(), nil -} - -var userConfigRE = regexp.MustCompile(`\s*(?:(?:[-]{3,}.*[-]{3,}\r?\n)([\w\-.=]+)(?:\r?\n[-]{3,}.*[-]{3,}\r?\n))`) - -// An user config file looks like this: -// -----BEGIN NATS USER JWT----- -// eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5... -// ------END NATS USER JWT------ -// -// ************************* IMPORTANT ************************* -// NKEY Seed printed below can be used sign and prove identity. -// NKEYs are sensitive and should be treated as secrets. -// -// -----BEGIN USER NKEY SEED----- -// SUAIO3FHUX5PNV2LQIIP7TZ3N4L7TX3W53MQGEIVYFIGA635OZCKEYHFLM -// ------END USER NKEY SEED------ - -// FormatUserConfig returns a decorated file with a decorated JWT and decorated seed -func FormatUserConfig(jwtString string, seed []byte) ([]byte, error) { - gc, err := DecodeGeneric(jwtString) - if err != nil { - return nil, err - } - if gc.Type != UserClaim { - return nil, fmt.Errorf("%q cannot be serialized as a user config", string(gc.Type)) - } - - w := bytes.NewBuffer(nil) - - jd, err := formatJwt(string(gc.Type), jwtString) - if err != nil { - return nil, err - } - _, err = w.Write(jd) - if err != nil { - return nil, err - } - if !bytes.HasPrefix(bytes.TrimSpace(seed), []byte("SU")) { - return nil, fmt.Errorf("nkey seed is not an user seed") - } - - d, err := DecorateSeed(seed) - if err != nil { - return nil, err - } - _, err = w.Write(d) - if err != nil { - return nil, err - } - - return w.Bytes(), nil -} - -// ParseDecoratedJWT takes a creds file and returns the JWT portion. -func ParseDecoratedJWT(contents []byte) (string, error) { - items := userConfigRE.FindAllSubmatch(contents, -1) - if len(items) == 0 { - return string(contents), nil - } - // First result should be the user JWT. - // We copy here so that if the file contained a seed file too we wipe appropriately. - raw := items[0][1] - tmp := make([]byte, len(raw)) - copy(tmp, raw) - return string(tmp), nil -} - -// ParseDecoratedNKey takes a creds file, finds the NKey portion and creates a -// key pair from it. -func ParseDecoratedNKey(contents []byte) (nkeys.KeyPair, error) { - var seed []byte - - items := userConfigRE.FindAllSubmatch(contents, -1) - if len(items) > 1 { - seed = items[1][1] - } else { - lines := bytes.Split(contents, []byte("\n")) - for _, line := range lines { - if bytes.HasPrefix(bytes.TrimSpace(line), []byte("SO")) || - bytes.HasPrefix(bytes.TrimSpace(line), []byte("SA")) || - bytes.HasPrefix(bytes.TrimSpace(line), []byte("SU")) { - seed = line - break - } - } - } - if seed == nil { - return nil, errors.New("no nkey seed found") - } - if !bytes.HasPrefix(seed, []byte("SO")) && - !bytes.HasPrefix(seed, []byte("SA")) && - !bytes.HasPrefix(seed, []byte("SU")) { - return nil, errors.New("doesn't contain a seed nkey") - } - kp, err := nkeys.FromSeed(seed) - if err != nil { - return nil, err - } - return kp, nil -} - -// ParseDecoratedUserNKey takes a creds file, finds the NKey portion and creates a -// key pair from it. Similar to ParseDecoratedNKey but fails for non-user keys. -func ParseDecoratedUserNKey(contents []byte) (nkeys.KeyPair, error) { - nk, err := ParseDecoratedNKey(contents) - if err != nil { - return nil, err - } - seed, err := nk.Seed() - if err != nil { - return nil, err - } - if !bytes.HasPrefix(seed, []byte("SU")) { - return nil, errors.New("doesn't contain an user seed nkey") - } - kp, err := nkeys.FromSeed(seed) - if err != nil { - return nil, err - } - return kp, nil -} diff --git a/vendor/github.com/nats-io/jwt/exports.go b/vendor/github.com/nats-io/jwt/exports.go deleted file mode 100644 index 02861743..00000000 --- a/vendor/github.com/nats-io/jwt/exports.go +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright 2018-2019 The NATS Authors - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jwt - -import ( - "fmt" - "time" -) - -// ResponseType is used to store an export response type -type ResponseType string - -const ( - // ResponseTypeSingleton is used for a service that sends a single response only - ResponseTypeSingleton = "Singleton" - - // ResponseTypeStream is used for a service that will send multiple responses - ResponseTypeStream = "Stream" - - // ResponseTypeChunked is used for a service that sends a single response in chunks (so not quite a stream) - ResponseTypeChunked = "Chunked" -) - -// ServiceLatency is used when observing and exported service for -// latency measurements. -// Sampling 1-100, represents sampling rate, defaults to 100. -// Results is the subject where the latency metrics are published. -// A metric will be defined by the nats-server's ServiceLatency. Time durations -// are in nanoseconds. -// see https://github.com/nats-io/nats-server/blob/master/server/accounts.go#L524 -// e.g. -// { -// "app": "dlc22", -// "start": "2019-09-16T21:46:23.636869585-07:00", -// "svc": 219732, -// "nats": { -// "req": 320415, -// "resp": 228268, -// "sys": 0 -// }, -// "total": 768415 -// } -// -type ServiceLatency struct { - Sampling int `json:"sampling,omitempty"` - Results Subject `json:"results"` -} - -func (sl *ServiceLatency) Validate(vr *ValidationResults) { - if sl.Sampling < 1 || sl.Sampling > 100 { - vr.AddError("sampling percentage needs to be between 1-100") - } - sl.Results.Validate(vr) - if sl.Results.HasWildCards() { - vr.AddError("results subject can not contain wildcards") - } -} - -// Export represents a single export -type Export struct { - Name string `json:"name,omitempty"` - Subject Subject `json:"subject,omitempty"` - Type ExportType `json:"type,omitempty"` - TokenReq bool `json:"token_req,omitempty"` - Revocations RevocationList `json:"revocations,omitempty"` - ResponseType ResponseType `json:"response_type,omitempty"` - Latency *ServiceLatency `json:"service_latency,omitempty"` -} - -// IsService returns true if an export is for a service -func (e *Export) IsService() bool { - return e.Type == Service -} - -// IsStream returns true if an export is for a stream -func (e *Export) IsStream() bool { - return e.Type == Stream -} - -// IsSingleResponse returns true if an export has a single response -// or no resopnse type is set, also checks that the type is service -func (e *Export) IsSingleResponse() bool { - return e.Type == Service && (e.ResponseType == ResponseTypeSingleton || e.ResponseType == "") -} - -// IsChunkedResponse returns true if an export has a chunked response -func (e *Export) IsChunkedResponse() bool { - return e.Type == Service && e.ResponseType == ResponseTypeChunked -} - -// IsStreamResponse returns true if an export has a chunked response -func (e *Export) IsStreamResponse() bool { - return e.Type == Service && e.ResponseType == ResponseTypeStream -} - -// Validate appends validation issues to the passed in results list -func (e *Export) Validate(vr *ValidationResults) { - if e == nil { - vr.AddError("null export is not allowed") - return - } - if !e.IsService() && !e.IsStream() { - vr.AddError("invalid export type: %q", e.Type) - } - if e.IsService() && !e.IsSingleResponse() && !e.IsChunkedResponse() && !e.IsStreamResponse() { - vr.AddError("invalid response type for service: %q", e.ResponseType) - } - if e.IsStream() && e.ResponseType != "" { - vr.AddError("invalid response type for stream: %q", e.ResponseType) - } - if e.Latency != nil { - if !e.IsService() { - vr.AddError("latency tracking only permitted for services") - } - e.Latency.Validate(vr) - } - e.Subject.Validate(vr) -} - -// Revoke enters a revocation by publickey using time.Now(). -func (e *Export) Revoke(pubKey string) { - e.RevokeAt(pubKey, time.Now()) -} - -// RevokeAt enters a revocation by publickey and timestamp into this export -// If there is already a revocation for this public key that is newer, it is kept. -func (e *Export) RevokeAt(pubKey string, timestamp time.Time) { - if e.Revocations == nil { - e.Revocations = RevocationList{} - } - - e.Revocations.Revoke(pubKey, timestamp) -} - -// ClearRevocation removes any revocation for the public key -func (e *Export) ClearRevocation(pubKey string) { - e.Revocations.ClearRevocation(pubKey) -} - -// IsRevokedAt checks if the public key is in the revoked list with a timestamp later than the one passed in. -// Generally this method is called with the subject and issue time of the jwt to be tested. -// DO NOT pass time.Now(), it will not produce a stable/expected response. -func (e *Export) IsRevokedAt(pubKey string, timestamp time.Time) bool { - return e.Revocations.IsRevoked(pubKey, timestamp) -} - -// IsRevoked does not perform a valid check. Use IsRevokedAt instead. -func (e *Export) IsRevoked(_ string) bool { - return true -} - -// Exports is a slice of exports -type Exports []*Export - -// Add appends exports to the list -func (e *Exports) Add(i ...*Export) { - *e = append(*e, i...) -} - -func isContainedIn(kind ExportType, subjects []Subject, vr *ValidationResults) { - m := make(map[string]string) - for i, ns := range subjects { - for j, s := range subjects { - if i == j { - continue - } - if ns.IsContainedIn(s) { - str := string(s) - _, ok := m[str] - if !ok { - m[str] = string(ns) - } - } - } - } - - if len(m) != 0 { - for k, v := range m { - var vi ValidationIssue - vi.Blocking = true - vi.Description = fmt.Sprintf("%s export subject %q already exports %q", kind, k, v) - vr.Add(&vi) - } - } -} - -// Validate calls validate on all of the exports -func (e *Exports) Validate(vr *ValidationResults) error { - var serviceSubjects []Subject - var streamSubjects []Subject - - for _, v := range *e { - if v == nil { - vr.AddError("null export is not allowed") - continue - } - if v.IsService() { - serviceSubjects = append(serviceSubjects, v.Subject) - } else { - streamSubjects = append(streamSubjects, v.Subject) - } - v.Validate(vr) - } - - isContainedIn(Service, serviceSubjects, vr) - isContainedIn(Stream, streamSubjects, vr) - - return nil -} - -// HasExportContainingSubject checks if the export list has an export with the provided subject -func (e *Exports) HasExportContainingSubject(subject Subject) bool { - for _, s := range *e { - if subject.IsContainedIn(s.Subject) { - return true - } - } - return false -} - -func (e Exports) Len() int { - return len(e) -} - -func (e Exports) Swap(i, j int) { - e[i], e[j] = e[j], e[i] -} - -func (e Exports) Less(i, j int) bool { - return e[i].Subject < e[j].Subject -} diff --git a/vendor/github.com/nats-io/jwt/genericlaims.go b/vendor/github.com/nats-io/jwt/genericlaims.go deleted file mode 100644 index 94cd86e0..00000000 --- a/vendor/github.com/nats-io/jwt/genericlaims.go +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2018 The NATS Authors - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jwt - -import "github.com/nats-io/nkeys" - -// GenericClaims can be used to read a JWT as a map for any non-generic fields -type GenericClaims struct { - ClaimsData - Data map[string]interface{} `json:"nats,omitempty"` -} - -// NewGenericClaims creates a map-based Claims -func NewGenericClaims(subject string) *GenericClaims { - if subject == "" { - return nil - } - c := GenericClaims{} - c.Subject = subject - c.Data = make(map[string]interface{}) - return &c -} - -// DecodeGeneric takes a JWT string and decodes it into a ClaimsData and map -func DecodeGeneric(token string) (*GenericClaims, error) { - v := GenericClaims{} - if err := Decode(token, &v); err != nil { - return nil, err - } - return &v, nil -} - -// Claims returns the standard part of the generic claim -func (gc *GenericClaims) Claims() *ClaimsData { - return &gc.ClaimsData -} - -// Payload returns the custom part of the claims data -func (gc *GenericClaims) Payload() interface{} { - return &gc.Data -} - -// Encode takes a generic claims and creates a JWT string -func (gc *GenericClaims) Encode(pair nkeys.KeyPair) (string, error) { - return gc.ClaimsData.Encode(pair, gc) -} - -// Validate checks the generic part of the claims data -func (gc *GenericClaims) Validate(vr *ValidationResults) { - gc.ClaimsData.Validate(vr) -} - -func (gc *GenericClaims) String() string { - return gc.ClaimsData.String(gc) -} - -// ExpectedPrefixes returns the types allowed to encode a generic JWT, which is nil for all -func (gc *GenericClaims) ExpectedPrefixes() []nkeys.PrefixByte { - return nil -} diff --git a/vendor/github.com/nats-io/jwt/go.mod b/vendor/github.com/nats-io/jwt/go.mod deleted file mode 100644 index eebea6c2..00000000 --- a/vendor/github.com/nats-io/jwt/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module github.com/nats-io/jwt - -require github.com/nats-io/nkeys v0.1.4 - -go 1.13 diff --git a/vendor/github.com/nats-io/jwt/go.sum b/vendor/github.com/nats-io/jwt/go.sum deleted file mode 100644 index 5e6e47e0..00000000 --- a/vendor/github.com/nats-io/jwt/go.sum +++ /dev/null @@ -1,9 +0,0 @@ -github.com/nats-io/nkeys v0.1.4 h1:aEsHIssIk6ETN5m2/MD8Y4B2X7FfXrBAUdkyRvbVYzA= -github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/vendor/github.com/nats-io/jwt/header.go b/vendor/github.com/nats-io/jwt/header.go deleted file mode 100644 index 413103ea..00000000 --- a/vendor/github.com/nats-io/jwt/header.go +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2018-2019 The NATS Authors - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jwt - -import ( - "encoding/json" - "fmt" - "strings" -) - -const ( - // Version is semantic version. - Version = "1.1.0" - - // TokenTypeJwt is the JWT token type supported JWT tokens - // encoded and decoded by this library - TokenTypeJwt = "jwt" - - // AlgorithmNkey is the algorithm supported by JWT tokens - // encoded and decoded by this library - AlgorithmNkey = "ed25519" -) - -// Header is a JWT Jose Header -type Header struct { - Type string `json:"typ"` - Algorithm string `json:"alg"` -} - -// Parses a header JWT token -func parseHeaders(s string) (*Header, error) { - h, err := decodeString(s) - if err != nil { - return nil, err - } - header := Header{} - if err := json.Unmarshal(h, &header); err != nil { - return nil, err - } - - if err := header.Valid(); err != nil { - return nil, err - } - return &header, nil -} - -// Valid validates the Header. It returns nil if the Header is -// a JWT header, and the algorithm used is the NKEY algorithm. -func (h *Header) Valid() error { - if TokenTypeJwt != strings.ToLower(h.Type) { - return fmt.Errorf("not supported type %q", h.Type) - } - - if AlgorithmNkey != strings.ToLower(h.Algorithm) { - return fmt.Errorf("unexpected %q algorithm", h.Algorithm) - } - return nil -} diff --git a/vendor/github.com/nats-io/jwt/imports.go b/vendor/github.com/nats-io/jwt/imports.go deleted file mode 100644 index 4f6cfb16..00000000 --- a/vendor/github.com/nats-io/jwt/imports.go +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2018-2019 The NATS Authors - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jwt - -import ( - "io/ioutil" - "net/http" - "net/url" - "time" -) - -// Import describes a mapping from another account into this one -type Import struct { - Name string `json:"name,omitempty"` - // Subject field in an import is always from the perspective of the - // initial publisher - in the case of a stream it is the account owning - // the stream (the exporter), and in the case of a service it is the - // account making the request (the importer). - Subject Subject `json:"subject,omitempty"` - Account string `json:"account,omitempty"` - Token string `json:"token,omitempty"` - // To field in an import is always from the perspective of the subscriber - // in the case of a stream it is the client of the stream (the importer), - // from the perspective of a service, it is the subscription waiting for - // requests (the exporter). If the field is empty, it will default to the - // value in the Subject field. - To Subject `json:"to,omitempty"` - Type ExportType `json:"type,omitempty"` -} - -// IsService returns true if the import is of type service -func (i *Import) IsService() bool { - return i.Type == Service -} - -// IsStream returns true if the import is of type stream -func (i *Import) IsStream() bool { - return i.Type == Stream -} - -// Validate checks if an import is valid for the wrapping account -func (i *Import) Validate(actPubKey string, vr *ValidationResults) { - if i == nil { - vr.AddError("null import is not allowed") - return - } - if !i.IsService() && !i.IsStream() { - vr.AddError("invalid import type: %q", i.Type) - } - - if i.Account == "" { - vr.AddWarning("account to import from is not specified") - } - - i.Subject.Validate(vr) - - if i.IsService() && i.Subject.HasWildCards() { - vr.AddError("services cannot have wildcard subject: %q", i.Subject) - } - if i.IsStream() && i.To.HasWildCards() { - vr.AddError("streams cannot have wildcard to subject: %q", i.Subject) - } - - var act *ActivationClaims - - if i.Token != "" { - // Check to see if its an embedded JWT or a URL. - if url, err := url.Parse(i.Token); err == nil && url.Scheme != "" { - c := &http.Client{Timeout: 5 * time.Second} - resp, err := c.Get(url.String()) - if err != nil { - vr.AddWarning("import %s contains an unreachable token URL %q", i.Subject, i.Token) - } - - if resp != nil { - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - vr.AddWarning("import %s contains an unreadable token URL %q", i.Subject, i.Token) - } else { - act, err = DecodeActivationClaims(string(body)) - if err != nil { - vr.AddWarning("import %s contains a url %q with an invalid activation token", i.Subject, i.Token) - } - } - } - } else { - var err error - act, err = DecodeActivationClaims(i.Token) - if err != nil { - vr.AddWarning("import %q contains an invalid activation token", i.Subject) - } - } - } - - if act != nil { - if act.Issuer != i.Account { - vr.AddWarning("activation token doesn't match account for import %q", i.Subject) - } - - if act.ClaimsData.Subject != actPubKey { - vr.AddWarning("activation token doesn't match account it is being included in, %q", i.Subject) - } - } else { - vr.AddWarning("no activation provided for import %s", i.Subject) - } - -} - -// Imports is a list of import structs -type Imports []*Import - -// Validate checks if an import is valid for the wrapping account -func (i *Imports) Validate(acctPubKey string, vr *ValidationResults) { - toSet := make(map[Subject]bool, len(*i)) - for _, v := range *i { - if v == nil { - vr.AddError("null import is not allowed") - continue - } - if v.Type == Service { - if _, ok := toSet[v.To]; ok { - vr.AddError("Duplicate To subjects for %q", v.To) - } - toSet[v.To] = true - } - v.Validate(acctPubKey, vr) - } -} - -// Add is a simple way to add imports -func (i *Imports) Add(a ...*Import) { - *i = append(*i, a...) -} - -func (i Imports) Len() int { - return len(i) -} - -func (i Imports) Swap(j, k int) { - i[j], i[k] = i[k], i[j] -} - -func (i Imports) Less(j, k int) bool { - return i[j].Subject < i[k].Subject -} diff --git a/vendor/github.com/nats-io/jwt/operator_claims.go b/vendor/github.com/nats-io/jwt/operator_claims.go deleted file mode 100644 index 3c4d4a17..00000000 --- a/vendor/github.com/nats-io/jwt/operator_claims.go +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2018 The NATS Authors - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jwt - -import ( - "errors" - "fmt" - "net/url" - "strings" - - "github.com/nats-io/nkeys" -) - -// Operator specific claims -type Operator struct { - // Slice of real identies (like websites) that can be used to identify the operator. - Identities []Identity `json:"identity,omitempty"` - // Slice of other operator NKeys that can be used to sign on behalf of the main - // operator identity. - SigningKeys StringList `json:"signing_keys,omitempty"` - // AccountServerURL is a partial URL like "https://host.domain.org:/jwt/v1" - // tools will use the prefix and build queries by appending /accounts/ - // or /operator to the path provided. Note this assumes that the account server - // can handle requests in a nats-account-server compatible way. See - // https://github.com/nats-io/nats-account-server. - AccountServerURL string `json:"account_server_url,omitempty"` - // A list of NATS urls (tls://host:port) where tools can connect to the server - // using proper credentials. - OperatorServiceURLs StringList `json:"operator_service_urls,omitempty"` - // Identity of the system account - SystemAccount string `json:"system_account,omitempty"` -} - -// Validate checks the validity of the operators contents -func (o *Operator) Validate(vr *ValidationResults) { - if err := o.validateAccountServerURL(); err != nil { - vr.AddError(err.Error()) - } - - for _, v := range o.validateOperatorServiceURLs() { - if v != nil { - vr.AddError(v.Error()) - } - } - - for _, i := range o.Identities { - i.Validate(vr) - } - - for _, k := range o.SigningKeys { - if !nkeys.IsValidPublicOperatorKey(k) { - vr.AddError("%s is not an operator public key", k) - } - } - - if o.SystemAccount != "" { - if !nkeys.IsValidPublicAccountKey(o.SystemAccount) { - vr.AddError("%s is not an account public key", o.SystemAccount) - } - } -} - -func (o *Operator) validateAccountServerURL() error { - if o.AccountServerURL != "" { - // We don't care what kind of URL it is so long as it parses - // and has a protocol. The account server may impose additional - // constraints on the type of URLs that it is able to notify to - u, err := url.Parse(o.AccountServerURL) - if err != nil { - return fmt.Errorf("error parsing account server url: %v", err) - } - if u.Scheme == "" { - return fmt.Errorf("account server url %q requires a protocol", o.AccountServerURL) - } - } - return nil -} - -// ValidateOperatorServiceURL returns an error if the URL is not a valid NATS or TLS url. -func ValidateOperatorServiceURL(v string) error { - // should be possible for the service url to not be expressed - if v == "" { - return nil - } - u, err := url.Parse(v) - if err != nil { - return fmt.Errorf("error parsing operator service url %q: %v", v, err) - } - - if u.User != nil { - return fmt.Errorf("operator service url %q - credentials are not supported", v) - } - - if u.Path != "" { - return fmt.Errorf("operator service url %q - paths are not supported", v) - } - - lcs := strings.ToLower(u.Scheme) - switch lcs { - case "nats": - return nil - case "tls": - return nil - default: - return fmt.Errorf("operator service url %q - protocol not supported (only 'nats' or 'tls' only)", v) - } -} - -func (o *Operator) validateOperatorServiceURLs() []error { - var errors []error - for _, v := range o.OperatorServiceURLs { - if v != "" { - if err := ValidateOperatorServiceURL(v); err != nil { - errors = append(errors, err) - } - } - } - return errors -} - -// OperatorClaims define the data for an operator JWT -type OperatorClaims struct { - ClaimsData - Operator `json:"nats,omitempty"` -} - -// NewOperatorClaims creates a new operator claim with the specified subject, which should be an operator public key -func NewOperatorClaims(subject string) *OperatorClaims { - if subject == "" { - return nil - } - c := &OperatorClaims{} - c.Subject = subject - return c -} - -// DidSign checks the claims against the operator's public key and its signing keys -func (oc *OperatorClaims) DidSign(op Claims) bool { - if op == nil { - return false - } - issuer := op.Claims().Issuer - if issuer == oc.Subject { - return true - } - return oc.SigningKeys.Contains(issuer) -} - -// Deprecated: AddSigningKey, use claim.SigningKeys.Add() -func (oc *OperatorClaims) AddSigningKey(pk string) { - oc.SigningKeys.Add(pk) -} - -// Encode the claims into a JWT string -func (oc *OperatorClaims) Encode(pair nkeys.KeyPair) (string, error) { - if !nkeys.IsValidPublicOperatorKey(oc.Subject) { - return "", errors.New("expected subject to be an operator public key") - } - err := oc.validateAccountServerURL() - if err != nil { - return "", err - } - oc.ClaimsData.Type = OperatorClaim - return oc.ClaimsData.Encode(pair, oc) -} - -// DecodeOperatorClaims tries to create an operator claims from a JWt string -func DecodeOperatorClaims(token string) (*OperatorClaims, error) { - v := OperatorClaims{} - if err := Decode(token, &v); err != nil { - return nil, err - } - return &v, nil -} - -func (oc *OperatorClaims) String() string { - return oc.ClaimsData.String(oc) -} - -// Payload returns the operator specific data for an operator JWT -func (oc *OperatorClaims) Payload() interface{} { - return &oc.Operator -} - -// Validate the contents of the claims -func (oc *OperatorClaims) Validate(vr *ValidationResults) { - oc.ClaimsData.Validate(vr) - oc.Operator.Validate(vr) -} - -// ExpectedPrefixes defines the nkey types that can sign operator claims, operator -func (oc *OperatorClaims) ExpectedPrefixes() []nkeys.PrefixByte { - return []nkeys.PrefixByte{nkeys.PrefixByteOperator} -} - -// Claims returns the generic claims data -func (oc *OperatorClaims) Claims() *ClaimsData { - return &oc.ClaimsData -} diff --git a/vendor/github.com/nats-io/jwt/revocation_list.go b/vendor/github.com/nats-io/jwt/revocation_list.go deleted file mode 100644 index e016b416..00000000 --- a/vendor/github.com/nats-io/jwt/revocation_list.go +++ /dev/null @@ -1,32 +0,0 @@ -package jwt - -import ( - "time" -) - -// RevocationList is used to store a mapping of public keys to unix timestamps -type RevocationList map[string]int64 - -// Revoke enters a revocation by publickey and timestamp into this export -// If there is already a revocation for this public key that is newer, it is kept. -func (r RevocationList) Revoke(pubKey string, timestamp time.Time) { - newTS := timestamp.Unix() - if ts, ok := r[pubKey]; ok && ts > newTS { - return - } - - r[pubKey] = newTS -} - -// ClearRevocation removes any revocation for the public key -func (r RevocationList) ClearRevocation(pubKey string) { - delete(r, pubKey) -} - -// IsRevoked checks if the public key is in the revoked list with a timestamp later than -// the one passed in. Generally this method is called with an issue time but other time's can -// be used for testing. -func (r RevocationList) IsRevoked(pubKey string, timestamp time.Time) bool { - ts, ok := r[pubKey] - return ok && ts >= timestamp.Unix() -} diff --git a/vendor/github.com/nats-io/jwt/server_claims.go b/vendor/github.com/nats-io/jwt/server_claims.go deleted file mode 100644 index c18f167f..00000000 --- a/vendor/github.com/nats-io/jwt/server_claims.go +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2018 The NATS Authors - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jwt - -import ( - "errors" - - "github.com/nats-io/nkeys" -) - -// Server defines the custom part of a server jwt -type Server struct { - Permissions - Cluster string `json:"cluster,omitempty"` -} - -// Validate checks the cluster and permissions for a server JWT -func (s *Server) Validate(vr *ValidationResults) { - if s.Cluster == "" { - vr.AddError("servers can't contain an empty cluster") - } -} - -// ServerClaims defines the data in a server JWT -type ServerClaims struct { - ClaimsData - Server `json:"nats,omitempty"` -} - -// NewServerClaims creates a new server JWT with the specified subject/public key -func NewServerClaims(subject string) *ServerClaims { - if subject == "" { - return nil - } - c := &ServerClaims{} - c.Subject = subject - return c -} - -// Encode tries to turn the server claims into a JWT string -func (s *ServerClaims) Encode(pair nkeys.KeyPair) (string, error) { - if !nkeys.IsValidPublicServerKey(s.Subject) { - return "", errors.New("expected subject to be a server public key") - } - s.ClaimsData.Type = ServerClaim - return s.ClaimsData.Encode(pair, s) -} - -// DecodeServerClaims tries to parse server claims from a JWT string -func DecodeServerClaims(token string) (*ServerClaims, error) { - v := ServerClaims{} - if err := Decode(token, &v); err != nil { - return nil, err - } - return &v, nil -} - -func (s *ServerClaims) String() string { - return s.ClaimsData.String(s) -} - -// Payload returns the server specific data -func (s *ServerClaims) Payload() interface{} { - return &s.Server -} - -// Validate checks the generic and server data in the server claims -func (s *ServerClaims) Validate(vr *ValidationResults) { - s.ClaimsData.Validate(vr) - s.Server.Validate(vr) -} - -// ExpectedPrefixes defines the types that can encode a server JWT, operator or cluster -func (s *ServerClaims) ExpectedPrefixes() []nkeys.PrefixByte { - return []nkeys.PrefixByte{nkeys.PrefixByteOperator, nkeys.PrefixByteCluster} -} - -// Claims returns the generic data -func (s *ServerClaims) Claims() *ClaimsData { - return &s.ClaimsData -} diff --git a/vendor/github.com/nats-io/jwt/types.go b/vendor/github.com/nats-io/jwt/types.go deleted file mode 100644 index e729c7eb..00000000 --- a/vendor/github.com/nats-io/jwt/types.go +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright 2018-2019 The NATS Authors - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jwt - -import ( - "encoding/json" - "fmt" - "net" - "strings" - "time" -) - -// ExportType defines the type of import/export. -type ExportType int - -const ( - // Unknown is used if we don't know the type - Unknown ExportType = iota - // Stream defines the type field value for a stream "stream" - Stream - // Service defines the type field value for a service "service" - Service -) - -func (t ExportType) String() string { - switch t { - case Stream: - return "stream" - case Service: - return "service" - } - return "unknown" -} - -// MarshalJSON marshals the enum as a quoted json string -func (t *ExportType) MarshalJSON() ([]byte, error) { - switch *t { - case Stream: - return []byte("\"stream\""), nil - case Service: - return []byte("\"service\""), nil - } - return nil, fmt.Errorf("unknown export type") -} - -// UnmarshalJSON unmashals a quoted json string to the enum value -func (t *ExportType) UnmarshalJSON(b []byte) error { - var j string - err := json.Unmarshal(b, &j) - if err != nil { - return err - } - switch j { - case "stream": - *t = Stream - return nil - case "service": - *t = Service - return nil - } - return fmt.Errorf("unknown export type") -} - -// Subject is a string that represents a NATS subject -type Subject string - -// Validate checks that a subject string is valid, ie not empty and without spaces -func (s Subject) Validate(vr *ValidationResults) { - v := string(s) - if v == "" { - vr.AddError("subject cannot be empty") - } - if strings.Contains(v, " ") { - vr.AddError("subject %q cannot have spaces", v) - } -} - -// HasWildCards is used to check if a subject contains a > or * -func (s Subject) HasWildCards() bool { - v := string(s) - return strings.HasSuffix(v, ".>") || - strings.Contains(v, ".*.") || - strings.HasSuffix(v, ".*") || - strings.HasPrefix(v, "*.") || - v == "*" || - v == ">" -} - -// IsContainedIn does a simple test to see if the subject is contained in another subject -func (s Subject) IsContainedIn(other Subject) bool { - otherArray := strings.Split(string(other), ".") - myArray := strings.Split(string(s), ".") - - if len(myArray) > len(otherArray) && otherArray[len(otherArray)-1] != ">" { - return false - } - - if len(myArray) < len(otherArray) { - return false - } - - for ind, tok := range otherArray { - myTok := myArray[ind] - - if ind == len(otherArray)-1 && tok == ">" { - return true - } - - if tok != myTok && tok != "*" { - return false - } - } - - return true -} - -// NamedSubject is the combination of a subject and a name for it -type NamedSubject struct { - Name string `json:"name,omitempty"` - Subject Subject `json:"subject,omitempty"` -} - -// Validate checks the subject -func (ns *NamedSubject) Validate(vr *ValidationResults) { - ns.Subject.Validate(vr) -} - -// TimeRange is used to represent a start and end time -type TimeRange struct { - Start string `json:"start,omitempty"` - End string `json:"end,omitempty"` -} - -// Validate checks the values in a time range struct -func (tr *TimeRange) Validate(vr *ValidationResults) { - format := "15:04:05" - - if tr.Start == "" { - vr.AddError("time ranges start must contain a start") - } else { - _, err := time.Parse(format, tr.Start) - if err != nil { - vr.AddError("start in time range is invalid %q", tr.Start) - } - } - - if tr.End == "" { - vr.AddError("time ranges end must contain an end") - } else { - _, err := time.Parse(format, tr.End) - if err != nil { - vr.AddError("end in time range is invalid %q", tr.End) - } - } -} - -// Limits are used to control acccess for users and importing accounts -// Src is a comma separated list of CIDR specifications -type Limits struct { - Max int64 `json:"max,omitempty"` - Payload int64 `json:"payload,omitempty"` - Src string `json:"src,omitempty"` - Times []TimeRange `json:"times,omitempty"` -} - -// Validate checks the values in a limit struct -func (l *Limits) Validate(vr *ValidationResults) { - if l.Max < 0 { - vr.AddError("limits cannot contain a negative maximum, %d", l.Max) - } - if l.Payload < 0 { - vr.AddError("limits cannot contain a negative payload, %d", l.Payload) - } - - if l.Src != "" { - elements := strings.Split(l.Src, ",") - - for _, cidr := range elements { - cidr = strings.TrimSpace(cidr) - _, ipNet, err := net.ParseCIDR(cidr) - if err != nil || ipNet == nil { - vr.AddError("invalid cidr %q in user src limits", cidr) - } - } - } - - if l.Times != nil && len(l.Times) > 0 { - for _, t := range l.Times { - t.Validate(vr) - } - } -} - -// Permission defines allow/deny subjects -type Permission struct { - Allow StringList `json:"allow,omitempty"` - Deny StringList `json:"deny,omitempty"` -} - -// Validate the allow, deny elements of a permission -func (p *Permission) Validate(vr *ValidationResults) { - for _, subj := range p.Allow { - Subject(subj).Validate(vr) - } - for _, subj := range p.Deny { - Subject(subj).Validate(vr) - } -} - -// ResponsePermission can be used to allow responses to any reply subject -// that is received on a valid subscription. -type ResponsePermission struct { - MaxMsgs int `json:"max"` - Expires time.Duration `json:"ttl"` -} - -// Validate the response permission. -func (p *ResponsePermission) Validate(vr *ValidationResults) { - // Any values can be valid for now. -} - -// Permissions are used to restrict subject access, either on a user or for everyone on a server by default -type Permissions struct { - Pub Permission `json:"pub,omitempty"` - Sub Permission `json:"sub,omitempty"` - Resp *ResponsePermission `json:"resp,omitempty"` -} - -// Validate the pub and sub fields in the permissions list -func (p *Permissions) Validate(vr *ValidationResults) { - if p.Resp != nil { - p.Resp.Validate(vr) - } -} - -// StringList is a wrapper for an array of strings -type StringList []string - -// Contains returns true if the list contains the string -func (u *StringList) Contains(p string) bool { - for _, t := range *u { - if t == p { - return true - } - } - return false -} - -// Add appends 1 or more strings to a list -func (u *StringList) Add(p ...string) { - for _, v := range p { - if !u.Contains(v) && v != "" { - *u = append(*u, v) - } - } -} - -// Remove removes 1 or more strings from a list -func (u *StringList) Remove(p ...string) { - for _, v := range p { - for i, t := range *u { - if t == v { - a := *u - *u = append(a[:i], a[i+1:]...) - break - } - } - } -} - -// TagList is a unique array of lower case strings -// All tag list methods lower case the strings in the arguments -type TagList []string - -// Contains returns true if the list contains the tags -func (u *TagList) Contains(p string) bool { - p = strings.ToLower(p) - for _, t := range *u { - if t == p { - return true - } - } - return false -} - -// Add appends 1 or more tags to a list -func (u *TagList) Add(p ...string) { - for _, v := range p { - v = strings.ToLower(v) - if !u.Contains(v) && v != "" { - *u = append(*u, v) - } - } -} - -// Remove removes 1 or more tags from a list -func (u *TagList) Remove(p ...string) { - for _, v := range p { - v = strings.ToLower(v) - for i, t := range *u { - if t == v { - a := *u - *u = append(a[:i], a[i+1:]...) - break - } - } - } -} - -// Identity is used to associate an account or operator with a real entity -type Identity struct { - ID string `json:"id,omitempty"` - Proof string `json:"proof,omitempty"` -} - -// Validate checks the values in an Identity -func (u *Identity) Validate(vr *ValidationResults) { - //Fixme identity validation -} diff --git a/vendor/github.com/nats-io/jwt/user_claims.go b/vendor/github.com/nats-io/jwt/user_claims.go deleted file mode 100644 index 78fe6a95..00000000 --- a/vendor/github.com/nats-io/jwt/user_claims.go +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2018-2019 The NATS Authors - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jwt - -import ( - "errors" - - "github.com/nats-io/nkeys" -) - -// User defines the user specific data in a user JWT -type User struct { - Permissions - Limits - BearerToken bool `json:"bearer_token,omitempty"` -} - -// Validate checks the permissions and limits in a User jwt -func (u *User) Validate(vr *ValidationResults) { - u.Permissions.Validate(vr) - u.Limits.Validate(vr) - // When BearerToken is true server will ignore any nonce-signing verification -} - -// UserClaims defines a user JWT -type UserClaims struct { - ClaimsData - User `json:"nats,omitempty"` - // IssuerAccount stores the public key for the account the issuer represents. - // When set, the claim was issued by a signing key. - IssuerAccount string `json:"issuer_account,omitempty"` -} - -// NewUserClaims creates a user JWT with the specific subject/public key -func NewUserClaims(subject string) *UserClaims { - if subject == "" { - return nil - } - c := &UserClaims{} - c.Subject = subject - return c -} - -// Encode tries to turn the user claims into a JWT string -func (u *UserClaims) Encode(pair nkeys.KeyPair) (string, error) { - if !nkeys.IsValidPublicUserKey(u.Subject) { - return "", errors.New("expected subject to be user public key") - } - u.ClaimsData.Type = UserClaim - return u.ClaimsData.Encode(pair, u) -} - -// DecodeUserClaims tries to parse a user claims from a JWT string -func DecodeUserClaims(token string) (*UserClaims, error) { - v := UserClaims{} - if err := Decode(token, &v); err != nil { - return nil, err - } - return &v, nil -} - -// Validate checks the generic and specific parts of the user jwt -func (u *UserClaims) Validate(vr *ValidationResults) { - u.ClaimsData.Validate(vr) - u.User.Validate(vr) - if u.IssuerAccount != "" && !nkeys.IsValidPublicAccountKey(u.IssuerAccount) { - vr.AddError("account_id is not an account public key") - } -} - -// ExpectedPrefixes defines the types that can encode a user JWT, account -func (u *UserClaims) ExpectedPrefixes() []nkeys.PrefixByte { - return []nkeys.PrefixByte{nkeys.PrefixByteAccount} -} - -// Claims returns the generic data from a user jwt -func (u *UserClaims) Claims() *ClaimsData { - return &u.ClaimsData -} - -// Payload returns the user specific data from a user JWT -func (u *UserClaims) Payload() interface{} { - return &u.User -} - -func (u *UserClaims) String() string { - return u.ClaimsData.String(u) -} - -// IsBearerToken returns true if nonce-signing requirements should be skipped -func (u *UserClaims) IsBearerToken() bool { - return u.BearerToken -} diff --git a/vendor/github.com/nats-io/jwt/validation.go b/vendor/github.com/nats-io/jwt/validation.go deleted file mode 100644 index c87a9922..00000000 --- a/vendor/github.com/nats-io/jwt/validation.go +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2018 The NATS Authors - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jwt - -import ( - "errors" - "fmt" -) - -// ValidationIssue represents an issue during JWT validation, it may or may not be a blocking error -type ValidationIssue struct { - Description string - Blocking bool - TimeCheck bool -} - -func (ve *ValidationIssue) Error() string { - return ve.Description -} - -// ValidationResults is a list of ValidationIssue pointers -type ValidationResults struct { - Issues []*ValidationIssue -} - -// CreateValidationResults creates an empty list of validation issues -func CreateValidationResults() *ValidationResults { - issues := []*ValidationIssue{} - return &ValidationResults{ - Issues: issues, - } -} - -//Add appends an issue to the list -func (v *ValidationResults) Add(vi *ValidationIssue) { - v.Issues = append(v.Issues, vi) -} - -// AddError creates a new validation error and adds it to the list -func (v *ValidationResults) AddError(format string, args ...interface{}) { - v.Add(&ValidationIssue{ - Description: fmt.Sprintf(format, args...), - Blocking: true, - TimeCheck: false, - }) -} - -// AddTimeCheck creates a new validation issue related to a time check and adds it to the list -func (v *ValidationResults) AddTimeCheck(format string, args ...interface{}) { - v.Add(&ValidationIssue{ - Description: fmt.Sprintf(format, args...), - Blocking: false, - TimeCheck: true, - }) -} - -// AddWarning creates a new validation warning and adds it to the list -func (v *ValidationResults) AddWarning(format string, args ...interface{}) { - v.Add(&ValidationIssue{ - Description: fmt.Sprintf(format, args...), - Blocking: false, - TimeCheck: false, - }) -} - -// IsBlocking returns true if the list contains a blocking error -func (v *ValidationResults) IsBlocking(includeTimeChecks bool) bool { - for _, i := range v.Issues { - if i.Blocking { - return true - } - - if includeTimeChecks && i.TimeCheck { - return true - } - } - return false -} - -// IsEmpty returns true if the list is empty -func (v *ValidationResults) IsEmpty() bool { - return len(v.Issues) == 0 -} - -// Errors returns only blocking issues as errors -func (v *ValidationResults) Errors() []error { - var errs []error - for _, v := range v.Issues { - if v.Blocking { - errs = append(errs, errors.New(v.Description)) - } - } - return errs -} diff --git a/vendor/github.com/nats-io/nats.go/.travis.yml b/vendor/github.com/nats-io/nats.go/.travis.yml index 3b00ae19..f16a2c1b 100644 --- a/vendor/github.com/nats-io/nats.go/.travis.yml +++ b/vendor/github.com/nats-io/nats.go/.travis.yml @@ -1,13 +1,10 @@ language: go go: +- 1.15.x - 1.14.x -- 1.13.x -env: -- GO111MODULE=off go_import_path: github.com/nats-io/nats.go install: - go get -t ./... -- go get github.com/nats-io/nats-server - go get github.com/mattn/goveralls - go get github.com/wadey/gocovmerge - go get -u honnef.co/go/tools/cmd/staticcheck @@ -20,4 +17,4 @@ before_script: script: - go test -i -race ./... - go test -v -run=TestNoRace -p=1 ./... -- if [[ "$TRAVIS_GO_VERSION" =~ 1.14 ]]; then ./scripts/cov.sh TRAVIS; else go test -race -v -p=1 ./... --failfast; fi +- if [[ "$TRAVIS_GO_VERSION" =~ 1.15 ]]; then ./scripts/cov.sh TRAVIS; else go test -race -v -p=1 ./... --failfast; fi diff --git a/vendor/github.com/nats-io/nats.go/MAINTAINERS.md b/vendor/github.com/nats-io/nats.go/MAINTAINERS.md index 323faa8e..23214655 100644 --- a/vendor/github.com/nats-io/nats.go/MAINTAINERS.md +++ b/vendor/github.com/nats-io/nats.go/MAINTAINERS.md @@ -2,9 +2,7 @@ Maintainership is on a per project basis. -### Core-maintainers +### Maintainers - Derek Collison [@derekcollison](https://github.com/derekcollison) - Ivan Kozlovic [@kozlovic](https://github.com/kozlovic) - -### Maintainers - - Waldemar Quevedo [@wallyqs](https://github.com/wallyqs) \ No newline at end of file + - Waldemar Quevedo [@wallyqs](https://github.com/wallyqs) diff --git a/vendor/github.com/nats-io/nats.go/README.md b/vendor/github.com/nats-io/nats.go/README.md index 5eb7c968..17746568 100644 --- a/vendor/github.com/nats-io/nats.go/README.md +++ b/vendor/github.com/nats-io/nats.go/README.md @@ -3,7 +3,8 @@ A [Go](http://golang.org) client for the [NATS messaging system](https://nats.io [![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fnats-io%2Fgo-nats.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fnats-io%2Fgo-nats?ref=badge_shield) -[![Go Report Card](https://goreportcard.com/badge/github.com/nats-io/nats.go)](https://goreportcard.com/report/github.com/nats-io/nats.go) [![Build Status](https://travis-ci.org/nats-io/nats.go.svg?branch=master)](http://travis-ci.org/nats-io/nats.go) [![GoDoc](https://godoc.org/github.com/nats-io/nats.go?status.svg)](http://godoc.org/github.com/nats-io/nats.go) [![Coverage Status](https://coveralls.io/repos/nats-io/nats.go/badge.svg?branch=master)](https://coveralls.io/r/nats-io/nats.go?branch=master) +[![Go Report Card](https://goreportcard.com/badge/github.com/nats-io/nats.go)](https://goreportcard.com/report/github.com/nats-io/nats.go) [![Build Status](https://travis-ci.com/nats-io/nats.go.svg?branch=master)](http://travis-ci.com/nats-io/nats.go) [![GoDoc](https://img.shields.io/badge/GoDoc-reference-007d9c)](https://pkg.go.dev/github.com/nats-io/nats.go) + [![Coverage Status](https://coveralls.io/repos/nats-io/nats.go/badge.svg?branch=master)](https://coveralls.io/r/nats-io/nats.go?branch=master) ## Installation @@ -284,6 +285,21 @@ nc.QueueSubscribe("foo", "job_workers", func(_ *Msg) { ```go +// Normally, the library will return an error when trying to connect and +// there is no server running. The RetryOnFailedConnect option will set +// the connection in reconnecting state if it failed to connect right away. +nc, err := nats.Connect(nats.DefaultURL, + nats.RetryOnFailedConnect(true), + nats.MaxReconnects(10), + nats.ReconnectWait(time.Second), + nats.ReconnectHandler(func(_ *nats.Conn) { + // Note that this will be invoked for the first asynchronous connect. + })) +if err != nil { + // Should not return an error even if it can't connect, but you still + // need to check in case there are some configuration errors. +} + // Flush connection to server, returns when all messages have been processed. nc.Flush() fmt.Println("All clear!") diff --git a/vendor/github.com/nats-io/nats.go/context.go b/vendor/github.com/nats-io/nats.go/context.go index c921d6be..666a483a 100644 --- a/vendor/github.com/nats-io/nats.go/context.go +++ b/vendor/github.com/nats-io/nats.go/context.go @@ -11,9 +11,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// +build go1.7 - -// A Go client for the NATS messaging system (https://nats.io). package nats import ( @@ -21,9 +18,33 @@ import ( "reflect" ) +// RequestMsgWithContext takes a context, a subject and payload +// in bytes and request expecting a single response. +func (nc *Conn) RequestMsgWithContext(ctx context.Context, msg *Msg) (*Msg, error) { + var hdr []byte + var err error + + if len(msg.Header) > 0 { + if !nc.info.Headers { + return nil, ErrHeadersNotSupported + } + + hdr, err = msg.headerBytes() + if err != nil { + return nil, err + } + } + + return nc.requestWithContext(ctx, msg.Subject, hdr, msg.Data) +} + // RequestWithContext takes a context, a subject and payload // in bytes and request expecting a single response. func (nc *Conn) RequestWithContext(ctx context.Context, subj string, data []byte) (*Msg, error) { + return nc.requestWithContext(ctx, subj, nil, data) +} + +func (nc *Conn) requestWithContext(ctx context.Context, subj string, hdr, data []byte) (*Msg, error) { if ctx == nil { return nil, ErrInvalidContext } @@ -36,49 +57,52 @@ func (nc *Conn) RequestWithContext(ctx context.Context, subj string, data []byte return nil, ctx.Err() } - nc.mu.Lock() + var m *Msg + var err error + // If user wants the old style. - if nc.Opts.UseOldRequestStyle { - nc.mu.Unlock() - return nc.oldRequestWithContext(ctx, subj, data) - } - - mch, token, err := nc.createNewRequestAndSend(subj, data) - if err != nil { - return nil, err - } - - var ok bool - var msg *Msg - - select { - case msg, ok = <-mch: - if !ok { - return nil, ErrConnectionClosed + if nc.useOldRequestStyle() { + m, err = nc.oldRequestWithContext(ctx, subj, hdr, data) + } else { + mch, token, err := nc.createNewRequestAndSend(subj, hdr, data) + if err != nil { + return nil, err } - case <-ctx.Done(): - nc.mu.Lock() - delete(nc.respMap, token) - nc.mu.Unlock() - return nil, ctx.Err() - } - return msg, nil + var ok bool + + select { + case m, ok = <-mch: + if !ok { + return nil, ErrConnectionClosed + } + case <-ctx.Done(): + nc.mu.Lock() + delete(nc.respMap, token) + nc.mu.Unlock() + return nil, ctx.Err() + } + } + // Check for no responder status. + if err == nil && len(m.Data) == 0 && m.Header.Get(statusHdr) == noResponders { + m, err = nil, ErrNoResponders + } + return m, err } // oldRequestWithContext utilizes inbox and subscription per request. -func (nc *Conn) oldRequestWithContext(ctx context.Context, subj string, data []byte) (*Msg, error) { +func (nc *Conn) oldRequestWithContext(ctx context.Context, subj string, hdr, data []byte) (*Msg, error) { inbox := NewInbox() ch := make(chan *Msg, RequestChanLen) - s, err := nc.subscribe(inbox, _EMPTY_, nil, ch, true) + s, err := nc.subscribe(inbox, _EMPTY_, nil, ch, true, nil) if err != nil { return nil, err } s.AutoUnsubscribe(1) defer s.Unsubscribe() - err = nc.PublishRequest(subj, inbox, data) + err = nc.publish(subj, inbox, hdr, data) if err != nil { return nil, err } diff --git a/vendor/github.com/nats-io/nats.go/dependencies.md b/vendor/github.com/nats-io/nats.go/dependencies.md new file mode 100644 index 00000000..cc986b27 --- /dev/null +++ b/vendor/github.com/nats-io/nats.go/dependencies.md @@ -0,0 +1,13 @@ +# External Dependencies + +This file lists the dependencies used in this repository. + +| Dependency | License | +|-|-| +| Go | BSD 3-Clause "New" or "Revised" License | +| github.com/nats-io/nats.go | Apache License 2.0 | +| github.com/golang/protobuf v1.4.2 | BSD 3-Clause "New" or "Revised" License | +| github.com/nats-io/nats-server/v2 v2.1.8-0.20201115145023-f61fa8529a0f | Apache License 2.0 | +| github.com/nats-io/nkeys v0.2.0 | Apache License 2.0 | +| github.com/nats-io/nuid v1.0.1 | Apache License 2.0 | +| google.golang.org/protobuf v1.23.0 | BSD 3-Clause License | diff --git a/vendor/github.com/nats-io/nats.go/enc.go b/vendor/github.com/nats-io/nats.go/enc.go index 0ed71a2c..181ef5aa 100644 --- a/vendor/github.com/nats-io/nats.go/enc.go +++ b/vendor/github.com/nats-io/nats.go/enc.go @@ -93,7 +93,7 @@ func (c *EncodedConn) Publish(subject string, v interface{}) error { if err != nil { return err } - return c.Conn.publish(subject, _EMPTY_, b) + return c.Conn.publish(subject, _EMPTY_, nil, b) } // PublishRequest will perform a Publish() expecting a response on the @@ -104,7 +104,7 @@ func (c *EncodedConn) PublishRequest(subject, reply string, v interface{}) error if err != nil { return err } - return c.Conn.publish(subject, reply, b) + return c.Conn.publish(subject, reply, nil, b) } // Request will create an Inbox and perform a Request() call @@ -130,7 +130,7 @@ func (c *EncodedConn) Request(subject string, v interface{}, vPtr interface{}, t // Handler is a specific callback used for Subscribe. It is generalized to // an interface{}, but we will discover its format and arguments at runtime -// and perform the correct callback, including de-marshaling JSON strings +// and perform the correct callback, including de-marshaling encoded data // back into the appropriate struct based on the signature of the Handler. // // Handlers are expected to have one of four signatures. @@ -234,7 +234,7 @@ func (c *EncodedConn) subscribe(subject, queue string, cb Handler) (*Subscriptio cbValue.Call(oV) } - return c.Conn.subscribe(subject, queue, natsCB, nil, false) + return c.Conn.subscribe(subject, queue, natsCB, nil, false, nil) } // FlushTimeout allows a Flush operation to have an associated timeout. diff --git a/vendor/github.com/nats-io/nats.go/go.mod b/vendor/github.com/nats-io/nats.go/go.mod index bd7d44a2..b60123e7 100644 --- a/vendor/github.com/nats-io/nats.go/go.mod +++ b/vendor/github.com/nats-io/nats.go/go.mod @@ -1,7 +1,11 @@ module github.com/nats-io/nats.go +go 1.15 + require ( - github.com/nats-io/jwt v0.3.2 - github.com/nats-io/nkeys v0.1.4 + github.com/golang/protobuf v1.4.2 + github.com/nats-io/nats-server/v2 v2.1.8-0.20210227190344-51550e242af8 + github.com/nats-io/nkeys v0.2.0 github.com/nats-io/nuid v1.0.1 + google.golang.org/protobuf v1.23.0 ) diff --git a/vendor/github.com/nats-io/nats.go/go.sum b/vendor/github.com/nats-io/nats.go/go.sum index 70e81d40..a2f355de 100644 --- a/vendor/github.com/nats-io/nats.go/go.sum +++ b/vendor/github.com/nats-io/nats.go/go.sum @@ -1,15 +1,69 @@ -github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg= +github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/minio/highwayhash v1.0.0 h1:iMSDhgUILCr0TNm8LWlSjF8N0ZIj2qbO8WHp6Q/J2BA= +github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/jwt v0.3.3-0.20200519195258-f2bf5ce574c7/go.mod h1:n3cvmLfBfnpV4JJRN7lRYCyZnw48ksGsbThGXEk4w9M= +github.com/nats-io/jwt v1.1.0 h1:+vOlgtM0ZsF46GbmUoadq0/2rChNS45gtxHEa3H1gqM= +github.com/nats-io/jwt v1.1.0/go.mod h1:n3cvmLfBfnpV4JJRN7lRYCyZnw48ksGsbThGXEk4w9M= +github.com/nats-io/jwt/v2 v2.0.0-20200916203241-1f8ce17dff02/go.mod h1:vs+ZEjP+XKy8szkBmQwCB7RjYdIlMaPsFPs4VdS4bTQ= +github.com/nats-io/jwt/v2 v2.0.0-20201015190852-e11ce317263c/go.mod h1:vs+ZEjP+XKy8szkBmQwCB7RjYdIlMaPsFPs4VdS4bTQ= +github.com/nats-io/jwt/v2 v2.0.0-20210125223648-1c24d462becc h1:pu+s4XC+bYnI0iD2vDtOl83zjCYUau/q6c83pEvsGZc= +github.com/nats-io/jwt/v2 v2.0.0-20210125223648-1c24d462becc/go.mod h1:PuO5FToRL31ecdFqVjc794vK0Bj0CwzveQEDvkb7MoQ= +github.com/nats-io/jwt/v2 v2.0.0-20210208203759-ff814ca5f813 h1:km4lLzT86NyJRhO++VqfP/vn5cbfm+E05i2bGdqDbrY= +github.com/nats-io/jwt/v2 v2.0.0-20210208203759-ff814ca5f813/go.mod h1:PuO5FToRL31ecdFqVjc794vK0Bj0CwzveQEDvkb7MoQ= +github.com/nats-io/nats-server/v2 v2.1.8-0.20200524125952-51ebd92a9093/go.mod h1:rQnBf2Rv4P9adtAs/Ti6LfFmVtFG6HLhl/H7cVshcJU= +github.com/nats-io/nats-server/v2 v2.1.8-0.20200601203034-f8d6dd992b71/go.mod h1:Nan/1L5Sa1JRW+Thm4HNYcIDcVRFc5zK9OpSZeI2kk4= +github.com/nats-io/nats-server/v2 v2.1.8-0.20200929001935-7f44d075f7ad/go.mod h1:TkHpUIDETmTI7mrHN40D1pzxfzHZuGmtMbtb83TGVQw= +github.com/nats-io/nats-server/v2 v2.1.8-0.20201129161730-ebe63db3e3ed/go.mod h1:XD0zHR/jTXdZvWaQfS5mQgsXj6x12kMjKLyAk/cOGgY= +github.com/nats-io/nats-server/v2 v2.1.8-0.20210205154825-f7ab27f7dad4 h1:GStuc0W1rK45FSlpt3+7UTLzmRys2/6WSDuJFyzZ6Xg= +github.com/nats-io/nats-server/v2 v2.1.8-0.20210205154825-f7ab27f7dad4/go.mod h1:kauGd7hB5517KeSqspW2U1Mz/jhPbTrE8eOXzUPk1m0= +github.com/nats-io/nats-server/v2 v2.1.8-0.20210227190344-51550e242af8 h1:jPZZofsCevE2oJl3YexVw3drWOFdo8H4AWMb/1WcVoc= +github.com/nats-io/nats-server/v2 v2.1.8-0.20210227190344-51550e242af8/go.mod h1:/QQ/dpqFavkNhVnjvMILSQ3cj5hlmhB66adlgNbjuoA= +github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE= +github.com/nats-io/nats.go v1.10.1-0.20200531124210-96f2130e4d55/go.mod h1:ARiFsjW9DVxk48WJbO3OSZ2DG8fjkMi7ecLmXoY/n9I= +github.com/nats-io/nats.go v1.10.1-0.20200606002146-fc6fed82929a/go.mod h1:8eAIv96Mo9QW6Or40jUHejS7e4VwZ3VRYD6Sf0BTDp4= +github.com/nats-io/nats.go v1.10.1-0.20201021145452-94be476ad6e0/go.mod h1:VU2zERjp8xmF+Lw2NH4u2t5qWZxwc7jB3+7HVMWQXPI= +github.com/nats-io/nats.go v1.10.1-0.20210127212649-5b4924938a9a/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI= +github.com/nats-io/nats.go v1.10.1-0.20210211000709-75ded9c77585/go.mod h1:uBWnCKg9luW1g7hgzPxUjHFRI40EuTSX7RCzgnc74Jk= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.4 h1:aEsHIssIk6ETN5m2/MD8Y4B2X7FfXrBAUdkyRvbVYzA= github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= +github.com/nats-io/nkeys v0.2.0 h1:WXKF7diOaPU9cJdLD7nuzwasQy9vT1tBqzXZZf3AMJM= +github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= diff --git a/vendor/github.com/nats-io/nats.go/js.go b/vendor/github.com/nats-io/nats.go/js.go new file mode 100644 index 00000000..429959d9 --- /dev/null +++ b/vendor/github.com/nats-io/nats.go/js.go @@ -0,0 +1,1346 @@ +// Copyright 2020 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nats + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "strconv" + "strings" + "sync/atomic" + "time" +) + +// Request API subjects for JetStream. +const ( + // defaultAPIPrefix is the default prefix for the JetStream API. + defaultAPIPrefix = "$JS.API." + + // apiAccountInfo is for obtaining general information about JetStream. + apiAccountInfo = "INFO" + + // apiConsumerCreateT is used to create consumers. + apiConsumerCreateT = "CONSUMER.CREATE.%s" + + // apiDurableCreateT is used to create durable consumers. + apiDurableCreateT = "CONSUMER.DURABLE.CREATE.%s.%s" + + // apiConsumerInfoT is used to create consumers. + apiConsumerInfoT = "CONSUMER.INFO.%s.%s" + + // apiRequestNextT is the prefix for the request next message(s) for a consumer in worker/pull mode. + apiRequestNextT = "CONSUMER.MSG.NEXT.%s.%s" + + // apiDeleteConsumerT is used to delete consumers. + apiConsumerDeleteT = "CONSUMER.DELETE.%s.%s" + + // apiConsumerListT is used to return all detailed consumer information + apiConsumerListT = "CONSUMER.LIST.%s" + + // apiStreams can lookup a stream by subject. + apiStreams = "STREAM.NAMES" + + // apiStreamCreateT is the endpoint to create new streams. + apiStreamCreateT = "STREAM.CREATE.%s" + + // apiStreamInfoT is the endpoint to get information on a stream. + apiStreamInfoT = "STREAM.INFO.%s" + + // apiStreamUpdate is the endpoint to update existing streams. + apiStreamUpdateT = "STREAM.UPDATE.%s" + + // apiStreamDeleteT is the endpoint to delete streams. + apiStreamDeleteT = "STREAM.DELETE.%s" + + // apiPurgeStreamT is the endpoint to purge streams. + apiStreamPurgeT = "STREAM.PURGE.%s" + + // apiStreamListT is the endpoint that will return all detailed stream information + apiStreamList = "STREAM.LIST" + + // apiMsgGetT is the endpoint to get a message. + apiMsgGetT = "STREAM.MSG.GET.%s" + + // apiMsgDeleteT is the endpoint to remove a message. + apiMsgDeleteT = "STREAM.MSG.DELETE.%s" +) + +// JetStream is the public interface for JetStream. +type JetStream interface { + // Publish publishes a message to JetStream. + Publish(subj string, data []byte, opts ...PubOpt) (*PubAck, error) + + // Publish publishes a Msg to JetStream. + PublishMsg(m *Msg, opts ...PubOpt) (*PubAck, error) + + // Subscribe creates an async Subscription for JetStream. + Subscribe(subj string, cb MsgHandler, opts ...SubOpt) (*Subscription, error) + + // SubscribeSync creates a Subscription that can be used to process messages synchronously. + SubscribeSync(subj string, opts ...SubOpt) (*Subscription, error) + + // ChanSubscribe creates channel based Subscription. + ChanSubscribe(subj string, ch chan *Msg, opts ...SubOpt) (*Subscription, error) + + // QueueSubscribe creates a Subscription with a queue group. + QueueSubscribe(subj, queue string, cb MsgHandler, opts ...SubOpt) (*Subscription, error) + + // QueueSubscribeSync creates a Subscription with a queue group that can be used to process messages synchronously. + QueueSubscribeSync(subj, queue string, opts ...SubOpt) (*Subscription, error) +} + +// JetStreamContext is the public interface for JetStream. +type JetStreamContext interface { + JetStream + JetStreamManager +} + +// js is an internal struct from a JetStreamContext. +type js struct { + nc *Conn + // For importing JetStream from other accounts. + pre string + // Amount of time to wait for API requests. + wait time.Duration + // Signals only direct access and no API access. + direct bool +} + +const defaultRequestWait = 5 * time.Second + +// JetStream returns a JetStream context for pub/sub interactions. +func (nc *Conn) JetStream(opts ...JSOpt) (JetStreamContext, error) { + js := &js{nc: nc, pre: defaultAPIPrefix, wait: defaultRequestWait} + + for _, opt := range opts { + if err := opt.configureJSContext(js); err != nil { + return nil, err + } + } + + if js.direct { + return js, nil + } + + if _, err := js.AccountInfo(); err != nil { + if err == ErrNoResponders { + err = ErrJetStreamNotEnabled + } + return nil, err + } + + return js, nil +} + +// JSOpt configures a JetStream context. +type JSOpt interface { + configureJSContext(opts *js) error +} + +// jsOptFn configures an option for the JetStream context. +type jsOptFn func(opts *js) error + +func (opt jsOptFn) configureJSContext(opts *js) error { + return opt(opts) +} + +// APIPrefix changes the default prefix used for the JetStream API. +func APIPrefix(pre string) JSOpt { + return jsOptFn(func(js *js) error { + js.pre = pre + if !strings.HasSuffix(js.pre, ".") { + js.pre = js.pre + "." + } + return nil + }) +} + +// DirectOnly makes a JetStream context avoid using the JetStream API altogether. +func DirectOnly() JSOpt { + return jsOptFn(func(js *js) error { + js.direct = true + return nil + }) +} + +func (js *js) apiSubj(subj string) string { + if js.pre == _EMPTY_ { + return subj + } + var b strings.Builder + b.WriteString(js.pre) + b.WriteString(subj) + return b.String() +} + +// PubOpt configures options for publishing JetStream messages. +type PubOpt interface { + configurePublish(opts *pubOpts) error +} + +// pubOptFn is a function option used to configure JetStream Publish. +type pubOptFn func(opts *pubOpts) error + +func (opt pubOptFn) configurePublish(opts *pubOpts) error { + return opt(opts) +} + +type pubOpts struct { + ctx context.Context + ttl time.Duration + id string + lid string // Expected last msgId + str string // Expected stream name + seq uint64 // Expected last sequence +} + +// pubAckResponse is the ack response from the JetStream API when of publishing a message. +type pubAckResponse struct { + apiResponse + *PubAck +} + +// PubAck is an ack received after successfully publishing a message. +type PubAck struct { + Stream string `json:"stream"` + Sequence uint64 `json:"seq"` + Duplicate bool `json:"duplicate,omitempty"` +} + +// Headers for published messages. +const ( + MsgIdHdr = "Nats-Msg-Id" + ExpectedStreamHdr = "Nats-Expected-Stream" + ExpectedLastSeqHdr = "Nats-Expected-Last-Sequence" + ExpectedLastMsgIdHdr = "Nats-Expected-Last-Msg-Id" +) + +// PublishMsg publishes a Msg to a stream from JetStream. +func (js *js) PublishMsg(m *Msg, opts ...PubOpt) (*PubAck, error) { + var o pubOpts + if len(opts) > 0 { + if m.Header == nil { + m.Header = http.Header{} + } + for _, opt := range opts { + if err := opt.configurePublish(&o); err != nil { + return nil, err + } + } + } + // Check for option collisions. Right now just timeout and context. + if o.ctx != nil && o.ttl != 0 { + return nil, ErrContextAndTimeout + } + if o.ttl == 0 && o.ctx == nil { + o.ttl = js.wait + } + + if o.id != _EMPTY_ { + m.Header.Set(MsgIdHdr, o.id) + } + if o.lid != _EMPTY_ { + m.Header.Set(ExpectedLastMsgIdHdr, o.lid) + } + if o.str != _EMPTY_ { + m.Header.Set(ExpectedStreamHdr, o.str) + } + if o.seq > 0 { + m.Header.Set(ExpectedLastSeqHdr, strconv.FormatUint(o.seq, 10)) + } + + var resp *Msg + var err error + + if o.ttl > 0 { + resp, err = js.nc.RequestMsg(m, time.Duration(o.ttl)) + } else { + resp, err = js.nc.RequestMsgWithContext(o.ctx, m) + } + + if err != nil { + if err == ErrNoResponders { + err = ErrNoStreamResponse + } + return nil, err + } + var pa pubAckResponse + if err := json.Unmarshal(resp.Data, &pa); err != nil { + return nil, ErrInvalidJSAck + } + if pa.Error != nil { + return nil, errors.New(pa.Error.Description) + } + if pa.PubAck == nil || pa.PubAck.Stream == _EMPTY_ { + return nil, ErrInvalidJSAck + } + return pa.PubAck, nil +} + +// Publish publishes a message to a stream from JetStream. +func (js *js) Publish(subj string, data []byte, opts ...PubOpt) (*PubAck, error) { + return js.PublishMsg(&Msg{Subject: subj, Data: data}, opts...) +} + +// MsgId sets the message ID used for de-duplication. +func MsgId(id string) PubOpt { + return pubOptFn(func(opts *pubOpts) error { + opts.id = id + return nil + }) +} + +// ExpectStream sets the expected stream to respond from the publish. +func ExpectStream(stream string) PubOpt { + return pubOptFn(func(opts *pubOpts) error { + opts.str = stream + return nil + }) +} + +// ExpectLastSequence sets the expected sequence in the response from the publish. +func ExpectLastSequence(seq uint64) PubOpt { + return pubOptFn(func(opts *pubOpts) error { + opts.seq = seq + return nil + }) +} + +// ExpectLastSequence sets the expected sequence in the response from the publish. +func ExpectLastMsgId(id string) PubOpt { + return pubOptFn(func(opts *pubOpts) error { + opts.lid = id + return nil + }) +} + +// MaxWait sets the maximum amount of time we will wait for a response. +type MaxWait time.Duration + +func (ttl MaxWait) configurePublish(opts *pubOpts) error { + opts.ttl = time.Duration(ttl) + return nil +} + +func (ttl MaxWait) configureJSContext(js *js) error { + js.wait = time.Duration(ttl) + return nil +} + +// ContextOpt is an option used to set a context.Context. +type ContextOpt struct { + context.Context +} + +func (ctx ContextOpt) configurePublish(opts *pubOpts) error { + opts.ctx = ctx + return nil +} + +// Context returns an option that can be used to configure a context. +func Context(ctx context.Context) ContextOpt { + return ContextOpt{ctx} +} + +// Subscribe + +// ConsumerConfig is the configuration of a JetStream consumer. +type ConsumerConfig struct { + Durable string `json:"durable_name,omitempty"` + DeliverSubject string `json:"deliver_subject,omitempty"` + DeliverPolicy DeliverPolicy `json:"deliver_policy"` + OptStartSeq uint64 `json:"opt_start_seq,omitempty"` + OptStartTime *time.Time `json:"opt_start_time,omitempty"` + AckPolicy AckPolicy `json:"ack_policy"` + AckWait time.Duration `json:"ack_wait,omitempty"` + MaxDeliver int `json:"max_deliver,omitempty"` + FilterSubject string `json:"filter_subject,omitempty"` + ReplayPolicy ReplayPolicy `json:"replay_policy"` + RateLimit uint64 `json:"rate_limit_bps,omitempty"` // Bits per sec + SampleFrequency string `json:"sample_freq,omitempty"` + MaxWaiting int `json:"max_waiting,omitempty"` + MaxAckPending int `json:"max_ack_pending,omitempty"` +} + +// ConsumerInfo is the info from a JetStream consumer. +type ConsumerInfo struct { + Stream string `json:"stream_name"` + Name string `json:"name"` + Created time.Time `json:"created"` + Config ConsumerConfig `json:"config"` + Delivered SequencePair `json:"delivered"` + AckFloor SequencePair `json:"ack_floor"` + NumAckPending int `json:"num_ack_pending"` + NumRedelivered int `json:"num_redelivered"` + NumWaiting int `json:"num_waiting"` + NumPending uint64 `json:"num_pending"` + Cluster *ClusterInfo `json:"cluster,omitempty"` +} + +// SequencePair includes the consumer and stream sequence info from a JetStream consumer. +type SequencePair struct { + Consumer uint64 `json:"consumer_seq"` + Stream uint64 `json:"stream_seq"` +} + +// nextRequest is for getting next messages for pull based consumers from JetStream. +type nextRequest struct { + Expires *time.Time `json:"expires,omitempty"` + Batch int `json:"batch,omitempty"` + NoWait bool `json:"no_wait,omitempty"` +} + +// jsSub includes JetStream subscription info. +type jsSub struct { + js *js + consumer string + stream string + deliver string + pull int + durable bool + attached bool +} + +func (jsi *jsSub) unsubscribe(drainMode bool) error { + if drainMode && (jsi.durable || jsi.attached) { + // Skip deleting consumer for durables/attached + // consumers when using drain mode. + return nil + } + + // Skip if in direct mode as well. + js := jsi.js + if js.direct { + return nil + } + + return js.DeleteConsumer(jsi.stream, jsi.consumer) +} + +// SubOpt configures options for subscribing to JetStream consumers. +type SubOpt interface { + configureSubscribe(opts *subOpts) error +} + +// subOptFn is a function option used to configure a JetStream Subscribe. +type subOptFn func(opts *subOpts) error + +func (opt subOptFn) configureSubscribe(opts *subOpts) error { + return opt(opts) +} + +// Subscribe will create a subscription to the appropriate stream and consumer. +func (js *js) Subscribe(subj string, cb MsgHandler, opts ...SubOpt) (*Subscription, error) { + return js.subscribe(subj, _EMPTY_, cb, nil, opts) +} + +// SubscribeSync will create a sync subscription to the appropriate stream and consumer. +func (js *js) SubscribeSync(subj string, opts ...SubOpt) (*Subscription, error) { + mch := make(chan *Msg, js.nc.Opts.SubChanLen) + return js.subscribe(subj, _EMPTY_, nil, mch, opts) +} + +// QueueSubscribe will create a subscription to the appropriate stream and consumer with queue semantics. +func (js *js) QueueSubscribe(subj, queue string, cb MsgHandler, opts ...SubOpt) (*Subscription, error) { + return js.subscribe(subj, queue, cb, nil, opts) +} + +// QueueSubscribeSync will create a sync subscription to the appropriate stream and consumer with queue semantics. +func (js *js) QueueSubscribeSync(subj, queue string, opts ...SubOpt) (*Subscription, error) { + mch := make(chan *Msg, js.nc.Opts.SubChanLen) + return js.subscribe(subj, queue, nil, mch, opts) +} + +// Subscribe will create a subscription to the appropriate stream and consumer. +func (js *js) ChanSubscribe(subj string, ch chan *Msg, opts ...SubOpt) (*Subscription, error) { + return js.subscribe(subj, _EMPTY_, nil, ch, opts) +} + +func (js *js) subscribe(subj, queue string, cb MsgHandler, ch chan *Msg, opts []SubOpt) (*Subscription, error) { + cfg := ConsumerConfig{AckPolicy: ackPolicyNotSet} + o := subOpts{cfg: &cfg} + if len(opts) > 0 { + for _, opt := range opts { + if err := opt.configureSubscribe(&o); err != nil { + return nil, err + } + } + } + + isPullMode := o.pull > 0 + if cb != nil && isPullMode { + return nil, ErrPullModeNotAllowed + } + + var ( + err error + shouldCreate bool + ccfg *ConsumerConfig + deliver string + attached bool + stream = o.stream + consumer = o.consumer + requiresAPI = (stream == _EMPTY_ && consumer == _EMPTY_) && o.cfg.DeliverSubject == _EMPTY_ + ) + + if js.direct && requiresAPI { + return nil, ErrDirectModeRequired + } + + if js.direct { + if o.cfg.DeliverSubject != _EMPTY_ { + deliver = o.cfg.DeliverSubject + } else { + deliver = NewInbox() + } + } else { + // Find the stream mapped to the subject. + stream, err = js.lookupStreamBySubject(subj) + if err != nil { + return nil, err + } + + // With an explicit durable name, then can lookup + // the consumer to which it should be attaching to. + var info *ConsumerInfo + consumer = o.cfg.Durable + if consumer != _EMPTY_ { + // Only create in case there is no consumer already. + info, err = js.ConsumerInfo(stream, consumer) + if err != nil && err.Error() != `consumer not found` { + return nil, err + } + } + + if info != nil { + // Attach using the found consumer config. + ccfg = &info.Config + attached = true + + // Make sure this new subject matches or is a subset. + if ccfg.FilterSubject != _EMPTY_ && subj != ccfg.FilterSubject { + return nil, ErrSubjectMismatch + } + + if ccfg.DeliverSubject != _EMPTY_ { + deliver = ccfg.DeliverSubject + } else { + deliver = NewInbox() + } + } else { + shouldCreate = true + deliver = NewInbox() + if !isPullMode { + cfg.DeliverSubject = deliver + } + // Do filtering always, server will clear as needed. + cfg.FilterSubject = subj + } + } + + var sub *Subscription + + // Check if we are manual ack. + if cb != nil && !o.mack { + ocb := cb + cb = func(m *Msg) { ocb(m); m.Ack() } + } + sub, err = js.nc.subscribe(deliver, queue, cb, ch, cb == nil, &jsSub{js: js}) + if err != nil { + return nil, err + } + + // If we are creating or updating let's process that request. + if shouldCreate { + // If not set default to ack explicit. + if cfg.AckPolicy == ackPolicyNotSet { + cfg.AckPolicy = AckExplicitPolicy + } + // If we have acks at all and the MaxAckPending is not set go ahead + // and set to the internal max. + // TODO(dlc) - We should be able to update this if client updates PendingLimits. + if cfg.MaxAckPending == 0 && cfg.AckPolicy != AckNonePolicy { + maxMsgs, _, _ := sub.PendingLimits() + cfg.MaxAckPending = maxMsgs + } + + req := &createConsumerRequest{ + Stream: stream, + Config: &cfg, + } + + j, err := json.Marshal(req) + if err != nil { + return nil, err + } + + var ccSubj string + isDurable := cfg.Durable != _EMPTY_ + if isDurable { + ccSubj = fmt.Sprintf(apiDurableCreateT, stream, cfg.Durable) + } else { + ccSubj = fmt.Sprintf(apiConsumerCreateT, stream) + } + + resp, err := js.nc.Request(js.apiSubj(ccSubj), j, js.wait) + if err != nil { + if err == ErrNoResponders { + err = ErrJetStreamNotEnabled + } + sub.Unsubscribe() + return nil, err + } + + var info consumerResponse + err = json.Unmarshal(resp.Data, &info) + if err != nil { + sub.Unsubscribe() + return nil, err + } + if info.Error != nil { + sub.Unsubscribe() + return nil, errors.New(info.Error.Description) + } + + // Hold onto these for later. + sub.jsi.stream = info.Stream + sub.jsi.consumer = info.Name + sub.jsi.deliver = info.Config.DeliverSubject + sub.jsi.durable = isDurable + } else { + sub.jsi.stream = stream + sub.jsi.consumer = consumer + if js.direct { + sub.jsi.deliver = o.cfg.DeliverSubject + } else { + sub.jsi.deliver = ccfg.DeliverSubject + } + } + sub.jsi.attached = attached + + // If we are pull based go ahead and fire off the first request to populate. + if isPullMode { + sub.jsi.pull = o.pull + sub.Poll() + } + + return sub, nil +} + +type streamRequest struct { + Subject string `json:"subject,omitempty"` +} + +type streamNamesResponse struct { + apiResponse + apiPaged + Streams []string `json:"streams"` +} + +func (js *js) lookupStreamBySubject(subj string) (string, error) { + var slr streamNamesResponse + req := &streamRequest{subj} + j, err := json.Marshal(req) + if err != nil { + return _EMPTY_, err + } + resp, err := js.nc.Request(js.apiSubj(apiStreams), j, js.wait) + if err != nil { + if err == ErrNoResponders { + err = ErrJetStreamNotEnabled + } + return _EMPTY_, err + } + if err := json.Unmarshal(resp.Data, &slr); err != nil { + return _EMPTY_, err + } + if slr.Error != nil || len(slr.Streams) != 1 { + return _EMPTY_, ErrNoMatchingStream + } + return slr.Streams[0], nil +} + +type subOpts struct { + // For attaching. + stream, consumer string + // For pull based consumers, batch size for pull + pull int + // For manual ack + mack bool + // For creating or updating. + cfg *ConsumerConfig +} + +// Durable defines the consumer name for JetStream durable subscribers. +func Durable(name string) SubOpt { + return subOptFn(func(opts *subOpts) error { + if strings.Contains(name, ".") { + return ErrInvalidDurableName + } + + opts.cfg.Durable = name + return nil + }) +} + +// Pull defines the batch size of messages that will be received +// when using pull based JetStream consumers. +func Pull(batchSize int) SubOpt { + return subOptFn(func(opts *subOpts) error { + if batchSize == 0 { + return errors.New("nats: batch size of 0 not valid") + } + opts.pull = batchSize + return nil + }) +} + +func PullDirect(stream, consumer string, batchSize int) SubOpt { + return subOptFn(func(opts *subOpts) error { + if batchSize == 0 { + return errors.New("nats: batch size of 0 not valid") + } + opts.stream = stream + opts.consumer = consumer + opts.pull = batchSize + return nil + }) +} + +// ManualAck disables auto ack functionality for async subscriptions. +func ManualAck() SubOpt { + return subOptFn(func(opts *subOpts) error { + opts.mack = true + return nil + }) +} + +// DeliverAll will configure a Consumer to receive all the +// messages from a Stream. +func DeliverAll() SubOpt { + return subOptFn(func(opts *subOpts) error { + opts.cfg.DeliverPolicy = DeliverAllPolicy + return nil + }) +} + +// DeliverLast configures a Consumer to receive messages +// starting with the latest one. +func DeliverLast() SubOpt { + return subOptFn(func(opts *subOpts) error { + opts.cfg.DeliverPolicy = DeliverLastPolicy + return nil + }) +} + +// DeliverNew configures a Consumer to receive messages +// published after the subscription. +func DeliverNew() SubOpt { + return subOptFn(func(opts *subOpts) error { + opts.cfg.DeliverPolicy = DeliverNewPolicy + return nil + }) +} + +// StartSequence configures a Consumer to receive +// messages from a start sequence. +func StartSequence(seq uint64) SubOpt { + return subOptFn(func(opts *subOpts) error { + opts.cfg.DeliverPolicy = DeliverByStartSequencePolicy + opts.cfg.OptStartSeq = seq + return nil + }) +} + +// StartTime configures a Consumer to receive +// messages from a start time. +func StartTime(startTime time.Time) SubOpt { + return subOptFn(func(opts *subOpts) error { + opts.cfg.DeliverPolicy = DeliverByStartTimePolicy + opts.cfg.OptStartTime = &startTime + return nil + }) +} + +func AckNone() SubOpt { + return subOptFn(func(opts *subOpts) error { + opts.cfg.AckPolicy = AckNonePolicy + return nil + }) +} + +func AckAll() SubOpt { + return subOptFn(func(opts *subOpts) error { + opts.cfg.AckPolicy = AckAllPolicy + return nil + }) +} + +func AckExplicit() SubOpt { + return subOptFn(func(opts *subOpts) error { + opts.cfg.AckPolicy = AckExplicitPolicy + return nil + }) +} + +func (sub *Subscription) ConsumerInfo() (*ConsumerInfo, error) { + sub.mu.Lock() + // TODO(dlc) - Better way to mark especially if we attach. + if sub.jsi.consumer == _EMPTY_ { + sub.mu.Unlock() + return nil, ErrTypeSubscription + } + + // Consumer info lookup should fail if in direct mode. + js := sub.jsi.js + if js.direct { + sub.mu.Unlock() + return nil, ErrDirectModeRequired + } + + stream, consumer := sub.jsi.stream, sub.jsi.consumer + sub.mu.Unlock() + + return js.getConsumerInfo(stream, consumer) +} + +func (sub *Subscription) Poll() error { + sub.mu.Lock() + if sub.jsi == nil || sub.jsi.deliver != _EMPTY_ || sub.jsi.pull == 0 { + sub.mu.Unlock() + return ErrTypeSubscription + } + batch := sub.jsi.pull + nc, reply := sub.conn, sub.Subject + stream, consumer := sub.jsi.stream, sub.jsi.consumer + js := sub.jsi.js + sub.mu.Unlock() + + req, _ := json.Marshal(&nextRequest{Batch: batch}) + reqNext := js.apiSubj(fmt.Sprintf(apiRequestNextT, stream, consumer)) + return nc.PublishRequest(reqNext, reply, req) +} + +func (js *js) getConsumerInfo(stream, consumer string) (*ConsumerInfo, error) { + ccInfoSubj := fmt.Sprintf(apiConsumerInfoT, stream, consumer) + resp, err := js.nc.Request(js.apiSubj(ccInfoSubj), nil, js.wait) + if err != nil { + if err == ErrNoResponders { + err = ErrJetStreamNotEnabled + } + return nil, err + } + + var info consumerResponse + if err := json.Unmarshal(resp.Data, &info); err != nil { + return nil, err + } + if info.Error != nil { + return nil, errors.New(info.Error.Description) + } + return info.ConsumerInfo, nil +} + +func (m *Msg) checkReply() (*js, bool, error) { + if m == nil || m.Sub == nil { + return nil, false, ErrMsgNotBound + } + if m.Reply == "" { + return nil, false, ErrMsgNoReply + } + sub := m.Sub + sub.mu.Lock() + if sub.jsi == nil { + sub.mu.Unlock() + + // Not using a JS context. + return nil, false, nil + } + js := sub.jsi.js + isPullMode := sub.jsi.pull > 0 + sub.mu.Unlock() + + return js, isPullMode, nil +} + +// ackReply handles all acks. Will do the right thing for pull and sync mode. +// It ensures that an ack is only sent a single time, regardless of +// how many times it is being called to avoid duplicated acks. +func (m *Msg) ackReply(ackType []byte, sync bool, opts ...PubOpt) error { + var o pubOpts + for _, opt := range opts { + if err := opt.configurePublish(&o); err != nil { + return err + } + } + js, isPullMode, err := m.checkReply() + if err != nil { + return err + } + + // Skip if already acked. + if atomic.LoadUint32(&m.ackd) == 1 { + return ErrInvalidJSAck + } + + m.Sub.mu.Lock() + nc := m.Sub.conn + m.Sub.mu.Unlock() + + ctx := o.ctx + wait := defaultRequestWait + if o.ttl > 0 { + wait = o.ttl + } else if js != nil { + wait = js.wait + } + + if isPullMode { + if bytes.Equal(ackType, AckAck) { + err = nc.PublishRequest(m.Reply, m.Sub.Subject, AckNext) + } else if bytes.Equal(ackType, AckNak) || bytes.Equal(ackType, AckTerm) { + err = nc.PublishRequest(m.Reply, m.Sub.Subject, []byte("+NXT {\"batch\":1}")) + } + if sync && err == nil { + if ctx != nil { + _, err = nc.RequestWithContext(ctx, m.Reply, nil) + } else { + _, err = nc.Request(m.Reply, nil, wait) + } + } + } else if sync { + if ctx != nil { + _, err = nc.RequestWithContext(ctx, m.Reply, ackType) + } else { + _, err = nc.Request(m.Reply, ackType, wait) + } + } else { + err = nc.Publish(m.Reply, ackType) + } + + // Mark that the message has been acked unless it is AckProgress + // which can be sent many times. + if err == nil && !bytes.Equal(ackType, AckProgress) { + atomic.StoreUint32(&m.ackd, 1) + } + + return err +} + +// Acks for messages + +// Ack a message, this will do the right thing with pull based consumers. +func (m *Msg) Ack() error { + return m.ackReply(AckAck, false) +} + +// Ack a message and wait for a response from the server. +func (m *Msg) AckSync(opts ...PubOpt) error { + return m.ackReply(AckAck, true, opts...) +} + +// Nak this message, indicating we can not process. +func (m *Msg) Nak() error { + return m.ackReply(AckNak, false) +} + +// Term this message from ever being delivered regardless of MaxDeliverCount. +func (m *Msg) Term() error { + return m.ackReply(AckTerm, false) +} + +// InProgress indicates that this message is being worked on +// and reset the redelivery timer in the server. +func (m *Msg) InProgress() error { + return m.ackReply(AckProgress, false) +} + +// MsgMetadata is the JetStream metadata associated with received messages. +type MsgMetaData struct { + Consumer uint64 + Stream uint64 + Delivered uint64 + Pending uint64 + Timestamp time.Time +} + +// MetaData retrieves the metadata from a JetStream message. +func (m *Msg) MetaData() (*MsgMetaData, error) { + if _, _, err := m.checkReply(); err != nil { + return nil, err + } + + const expectedTokens = 9 + const btsep = '.' + + tsa := [expectedTokens]string{} + start, tokens := 0, tsa[:0] + subject := m.Reply + for i := 0; i < len(subject); i++ { + if subject[i] == btsep { + tokens = append(tokens, subject[start:i]) + start = i + 1 + } + } + tokens = append(tokens, subject[start:]) + if len(tokens) != expectedTokens || tokens[0] != "$JS" || tokens[1] != "ACK" { + return nil, ErrNotJSMessage + } + + meta := &MsgMetaData{ + Delivered: uint64(parseNum(tokens[4])), + Stream: uint64(parseNum(tokens[5])), + Consumer: uint64(parseNum(tokens[6])), + Timestamp: time.Unix(0, parseNum(tokens[7])), + Pending: uint64(parseNum(tokens[8])), + } + + return meta, nil +} + +// Quick parser for positive numbers in ack reply encoding. +func parseNum(d string) (n int64) { + if len(d) == 0 { + return -1 + } + + // Ascii numbers 0-9 + const ( + asciiZero = 48 + asciiNine = 57 + ) + + for _, dec := range d { + if dec < asciiZero || dec > asciiNine { + return -1 + } + n = n*10 + (int64(dec) - asciiZero) + } + return n +} + +// AckPolicy determines how the consumer should acknowledge delivered messages. +type AckPolicy int + +const ( + // AckNonePolicy requires no acks for delivered messages. + AckNonePolicy AckPolicy = iota + + // AckAllPolicy when acking a sequence number, this implicitly acks all sequences below this one as well. + AckAllPolicy + + // AckExplicit requires ack or nack for all messages. + AckExplicitPolicy + + // For setting + ackPolicyNotSet = 99 +) + +func jsonString(s string) string { + return "\"" + s + "\"" +} + +func (p *AckPolicy) UnmarshalJSON(data []byte) error { + switch string(data) { + case jsonString("none"): + *p = AckNonePolicy + case jsonString("all"): + *p = AckAllPolicy + case jsonString("explicit"): + *p = AckExplicitPolicy + default: + return fmt.Errorf("can not unmarshal %q", data) + } + + return nil +} + +func (p AckPolicy) MarshalJSON() ([]byte, error) { + switch p { + case AckNonePolicy: + return json.Marshal("none") + case AckAllPolicy: + return json.Marshal("all") + case AckExplicitPolicy: + return json.Marshal("explicit") + default: + return nil, fmt.Errorf("unknown acknowlegement policy %v", p) + } +} + +func (p AckPolicy) String() string { + switch p { + case AckNonePolicy: + return "AckNone" + case AckAllPolicy: + return "AckAll" + case AckExplicitPolicy: + return "AckExplicit" + case ackPolicyNotSet: + return "Not Initialized" + default: + return "Unknown AckPolicy" + } +} + +type ReplayPolicy int + +const ( + ReplayInstant ReplayPolicy = iota + ReplayOriginal +) + +func (p *ReplayPolicy) UnmarshalJSON(data []byte) error { + switch string(data) { + case jsonString("instant"): + *p = ReplayInstant + case jsonString("original"): + *p = ReplayOriginal + default: + return fmt.Errorf("can not unmarshal %q", data) + } + + return nil +} + +func (p ReplayPolicy) MarshalJSON() ([]byte, error) { + switch p { + case ReplayOriginal: + return json.Marshal("original") + case ReplayInstant: + return json.Marshal("instant") + default: + return nil, fmt.Errorf("unknown replay policy %v", p) + } +} + +var ( + AckAck = []byte("+ACK") + AckNak = []byte("-NAK") + AckProgress = []byte("+WPI") + AckNext = []byte("+NXT") + AckTerm = []byte("+TERM") +) + +// DeliverPolicy determines how the consumer should select the first message to deliver. +type DeliverPolicy int + +const ( + // DeliverAllPolicy will be the default so can be omitted from the request. + DeliverAllPolicy DeliverPolicy = iota + + // DeliverLastPolicy will start the consumer with the last sequence received. + DeliverLastPolicy + + // DeliverNewPolicy will only deliver new messages that are sent + // after the consumer is created. + DeliverNewPolicy + + // DeliverByStartSequencePolicy will look for a defined starting sequence to start. + DeliverByStartSequencePolicy + + // StartTime will select the first messsage with a timestamp >= to StartTime. + DeliverByStartTimePolicy +) + +func (p *DeliverPolicy) UnmarshalJSON(data []byte) error { + switch string(data) { + case jsonString("all"), jsonString("undefined"): + *p = DeliverAllPolicy + case jsonString("last"): + *p = DeliverLastPolicy + case jsonString("new"): + *p = DeliverNewPolicy + case jsonString("by_start_sequence"): + *p = DeliverByStartSequencePolicy + case jsonString("by_start_time"): + *p = DeliverByStartTimePolicy + } + + return nil +} + +func (p DeliverPolicy) MarshalJSON() ([]byte, error) { + switch p { + case DeliverAllPolicy: + return json.Marshal("all") + case DeliverLastPolicy: + return json.Marshal("last") + case DeliverNewPolicy: + return json.Marshal("new") + case DeliverByStartSequencePolicy: + return json.Marshal("by_start_sequence") + case DeliverByStartTimePolicy: + return json.Marshal("by_start_time") + default: + return nil, fmt.Errorf("unknown deliver policy %v", p) + } +} + +// RetentionPolicy determines how messages in a set are retained. +type RetentionPolicy int + +const ( + // LimitsPolicy (default) means that messages are retained until any given limit is reached. + // This could be one of MaxMsgs, MaxBytes, or MaxAge. + LimitsPolicy RetentionPolicy = iota + // InterestPolicy specifies that when all known observables have acknowledged a message it can be removed. + InterestPolicy + // WorkQueuePolicy specifies that when the first worker or subscriber acknowledges the message it can be removed. + WorkQueuePolicy +) + +// DiscardPolicy determines how we proceed when limits of messages or bytes are hit. The default, DiscardOld will +// remove older messages. DiscardNew will fail to store the new message. +type DiscardPolicy int + +const ( + // DiscardOld will remove older messages to return to the limits. + DiscardOld = iota + //DiscardNew will error on a StoreMsg call + DiscardNew +) + +const ( + limitsPolicyString = "limits" + interestPolicyString = "interest" + workQueuePolicyString = "workqueue" +) + +func (rp RetentionPolicy) String() string { + switch rp { + case LimitsPolicy: + return "Limits" + case InterestPolicy: + return "Interest" + case WorkQueuePolicy: + return "WorkQueue" + default: + return "Unknown Retention Policy" + } +} + +func (rp RetentionPolicy) MarshalJSON() ([]byte, error) { + switch rp { + case LimitsPolicy: + return json.Marshal(limitsPolicyString) + case InterestPolicy: + return json.Marshal(interestPolicyString) + case WorkQueuePolicy: + return json.Marshal(workQueuePolicyString) + default: + return nil, fmt.Errorf("can not marshal %v", rp) + } +} + +func (rp *RetentionPolicy) UnmarshalJSON(data []byte) error { + switch string(data) { + case jsonString(limitsPolicyString): + *rp = LimitsPolicy + case jsonString(interestPolicyString): + *rp = InterestPolicy + case jsonString(workQueuePolicyString): + *rp = WorkQueuePolicy + default: + return fmt.Errorf("can not unmarshal %q", data) + } + return nil +} + +func (dp DiscardPolicy) String() string { + switch dp { + case DiscardOld: + return "DiscardOld" + case DiscardNew: + return "DiscardNew" + default: + return "Unknown Discard Policy" + } +} + +func (dp DiscardPolicy) MarshalJSON() ([]byte, error) { + switch dp { + case DiscardOld: + return json.Marshal("old") + case DiscardNew: + return json.Marshal("new") + default: + return nil, fmt.Errorf("can not marshal %v", dp) + } +} + +func (dp *DiscardPolicy) UnmarshalJSON(data []byte) error { + switch strings.ToLower(string(data)) { + case jsonString("old"): + *dp = DiscardOld + case jsonString("new"): + *dp = DiscardNew + default: + return fmt.Errorf("can not unmarshal %q", data) + } + return nil +} + +// StorageType determines how messages are stored for retention. +type StorageType int + +const ( + // FileStorage specifies on disk storage. It's the default. + FileStorage StorageType = iota + // MemoryStorage specifies in memory only. + MemoryStorage +) + +const ( + memoryStorageString = "memory" + fileStorageString = "file" +) + +func (st StorageType) String() string { + switch st { + case MemoryStorage: + return strings.Title(memoryStorageString) + case FileStorage: + return strings.Title(fileStorageString) + default: + return "Unknown Storage Type" + } +} + +func (st StorageType) MarshalJSON() ([]byte, error) { + switch st { + case MemoryStorage: + return json.Marshal(memoryStorageString) + case FileStorage: + return json.Marshal(fileStorageString) + default: + return nil, fmt.Errorf("can not marshal %v", st) + } +} + +func (st *StorageType) UnmarshalJSON(data []byte) error { + switch string(data) { + case jsonString(memoryStorageString): + *st = MemoryStorage + case jsonString(fileStorageString): + *st = FileStorage + default: + return fmt.Errorf("can not unmarshal %q", data) + } + return nil +} diff --git a/vendor/github.com/nats-io/nats.go/jsm.go b/vendor/github.com/nats-io/nats.go/jsm.go new file mode 100644 index 00000000..fb7851e4 --- /dev/null +++ b/vendor/github.com/nats-io/nats.go/jsm.go @@ -0,0 +1,700 @@ +// Copyright 2021 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nats + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "strings" + "time" +) + +// JetStreamManager is the public interface for managing JetStream streams & consumers. +type JetStreamManager interface { + // AddStream creates a stream. + AddStream(cfg *StreamConfig) (*StreamInfo, error) + + // UpdateStream updates a stream. + UpdateStream(cfg *StreamConfig) (*StreamInfo, error) + + // DeleteStream deletes a stream. + DeleteStream(name string) error + + // StreamInfo retrieves information from a stream. + StreamInfo(stream string) (*StreamInfo, error) + + // Purge stream messages. + PurgeStream(name string) error + + // NewStreamLister is used to return pages of StreamInfo objects. + NewStreamLister() *StreamLister + + // GetMsg retrieves a raw stream message stored in JetStream by sequence number. + GetMsg(name string, seq uint64) (*RawStreamMsg, error) + + // DeleteMsg erases a message from a stream. + DeleteMsg(name string, seq uint64) error + + // AddConsumer adds a consumer to a stream. + AddConsumer(stream string, cfg *ConsumerConfig) (*ConsumerInfo, error) + + // DeleteConsumer deletes a consumer. + DeleteConsumer(stream, consumer string) error + + // ConsumerInfo retrieves consumer information. + ConsumerInfo(stream, name string) (*ConsumerInfo, error) + + // NewConsumerLister is used to return pages of ConsumerInfo objects. + NewConsumerLister(stream string) *ConsumerLister + + // AccountInfo retrieves info about the JetStream usage from an account. + AccountInfo() (*AccountInfo, error) +} + +// StreamConfig will determine the properties for a stream. +// There are sensible defaults for most. If no subjects are +// given the name will be used as the only subject. +type StreamConfig struct { + Name string `json:"name"` + Subjects []string `json:"subjects,omitempty"` + Retention RetentionPolicy `json:"retention"` + MaxConsumers int `json:"max_consumers"` + MaxMsgs int64 `json:"max_msgs"` + MaxBytes int64 `json:"max_bytes"` + Discard DiscardPolicy `json:"discard"` + MaxAge time.Duration `json:"max_age"` + MaxMsgSize int32 `json:"max_msg_size,omitempty"` + Storage StorageType `json:"storage"` + Replicas int `json:"num_replicas"` + NoAck bool `json:"no_ack,omitempty"` + Template string `json:"template_owner,omitempty"` + Duplicates time.Duration `json:"duplicate_window,omitempty"` + Placement *Placement `json:"placement,omitempty"` + Mirror *StreamSource `json:"mirror,omitempty"` + Sources []*StreamSource `json:"sources,omitempty"` +} + +// Placement is used to guide placement of streams in clustered JetStream. +type Placement struct { + Cluster string `json:"cluster"` + Tags []string `json:"tags,omitempty"` +} + +// StreamSource dictates how streams can source from other streams. +type StreamSource struct { + Name string `json:"name"` + OptStartSeq uint64 `json:"opt_start_seq,omitempty"` + OptStartTime *time.Time `json:"opt_start_time,omitempty"` + FilterSubject string `json:"filter_subject,omitempty"` +} + +// apiError is included in all API responses if there was an error. +type apiError struct { + Code int `json:"code"` + Description string `json:"description,omitempty"` +} + +// apiResponse is a standard response from the JetStream JSON API +type apiResponse struct { + Type string `json:"type"` + Error *apiError `json:"error,omitempty"` +} + +// apiPaged includes variables used to create paged responses from the JSON API +type apiPaged struct { + Total int `json:"total"` + Offset int `json:"offset"` + Limit int `json:"limit"` +} + +// apiPagedRequest includes parameters allowing specific pages to be requested +// from APIs responding with apiPaged. +type apiPagedRequest struct { + Offset int `json:"offset"` +} + +// AccountInfo contains info about the JetStream usage from the current account. +type AccountInfo struct { + Memory uint64 `json:"memory"` + Store uint64 `json:"storage"` + Streams int `json:"streams"` + Consumers int `json:"consumers"` + API APIStats `json:"api"` + Limits AccountLimits `json:"limits"` +} + +// APIStats reports on API calls to JetStream for this account. +type APIStats struct { + Total uint64 `json:"total"` + Errors uint64 `json:"errors"` +} + +// AccountLimits includes the JetStream limits of the current account. +type AccountLimits struct { + MaxMemory int64 `json:"max_memory"` + MaxStore int64 `json:"max_storage"` + MaxStreams int `json:"max_streams"` + MaxConsumers int `json:"max_consumers"` +} + +type accountInfoResponse struct { + apiResponse + AccountInfo +} + +// AccountInfo retrieves info about the JetStream usage from the current account. +func (js *js) AccountInfo() (*AccountInfo, error) { + resp, err := js.nc.Request(js.apiSubj(apiAccountInfo), nil, js.wait) + if err != nil { + return nil, err + } + var info accountInfoResponse + if err := json.Unmarshal(resp.Data, &info); err != nil { + return nil, err + } + if info.Error != nil { + var err error + if strings.Contains(info.Error.Description, "not enabled for") { + err = ErrJetStreamNotEnabled + } else { + err = errors.New(info.Error.Description) + } + return nil, err + } + + return &info.AccountInfo, nil +} + +type createConsumerRequest struct { + Stream string `json:"stream_name"` + Config *ConsumerConfig `json:"config"` +} + +type consumerResponse struct { + apiResponse + *ConsumerInfo +} + +// AddConsumer will add a JetStream consumer. +func (js *js) AddConsumer(stream string, cfg *ConsumerConfig) (*ConsumerInfo, error) { + if stream == _EMPTY_ { + return nil, ErrStreamNameRequired + } + req, err := json.Marshal(&createConsumerRequest{Stream: stream, Config: cfg}) + if err != nil { + return nil, err + } + + var ccSubj string + if cfg != nil && cfg.Durable != _EMPTY_ { + if strings.Contains(cfg.Durable, ".") { + return nil, ErrInvalidDurableName + } + ccSubj = fmt.Sprintf(apiDurableCreateT, stream, cfg.Durable) + } else { + ccSubj = fmt.Sprintf(apiConsumerCreateT, stream) + } + + resp, err := js.nc.Request(js.apiSubj(ccSubj), req, js.wait) + if err != nil { + if err == ErrNoResponders { + err = ErrJetStreamNotEnabled + } + return nil, err + } + var info consumerResponse + err = json.Unmarshal(resp.Data, &info) + if err != nil { + return nil, err + } + if info.Error != nil { + return nil, errors.New(info.Error.Description) + } + return info.ConsumerInfo, nil +} + +// consumerDeleteResponse is the response for a Consumer delete request. +type consumerDeleteResponse struct { + apiResponse + Success bool `json:"success,omitempty"` +} + +// DeleteConsumer deletes a Consumer. +func (js *js) DeleteConsumer(stream, consumer string) error { + if stream == _EMPTY_ { + return ErrStreamNameRequired + } + + dcSubj := js.apiSubj(fmt.Sprintf(apiConsumerDeleteT, stream, consumer)) + r, err := js.nc.Request(dcSubj, nil, js.wait) + if err != nil { + return err + } + var resp consumerDeleteResponse + if err := json.Unmarshal(r.Data, &resp); err != nil { + return err + } + if resp.Error != nil { + return errors.New(resp.Error.Description) + } + return nil +} + +// ConsumerInfo returns information about a Consumer. +func (js *js) ConsumerInfo(stream, consumer string) (*ConsumerInfo, error) { + return js.getConsumerInfo(stream, consumer) +} + +// ConsumerLister fetches pages of ConsumerInfo objects. This object is not +// safe to use for multiple threads. +type ConsumerLister struct { + stream string + js *js + + err error + offset int + page []*ConsumerInfo + pageInfo *apiPaged +} + +// consumersRequest is the type used for Consumers requests. +type consumersRequest struct { + apiPagedRequest +} + +// consumerListResponse is the response for a Consumers List request. +type consumerListResponse struct { + apiResponse + apiPaged + Consumers []*ConsumerInfo `json:"consumers"` +} + +// Next fetches the next ConsumerInfo page. +func (c *ConsumerLister) Next() bool { + if c.err != nil { + return false + } + if c.stream == _EMPTY_ { + c.err = ErrStreamNameRequired + return false + } + if c.pageInfo != nil && c.offset >= c.pageInfo.Total { + return false + } + + req, err := json.Marshal(consumersRequest{ + apiPagedRequest: apiPagedRequest{Offset: c.offset}, + }) + if err != nil { + c.err = err + return false + } + clSubj := c.js.apiSubj(fmt.Sprintf(apiConsumerListT, c.stream)) + r, err := c.js.nc.Request(clSubj, req, c.js.wait) + if err != nil { + c.err = err + return false + } + var resp consumerListResponse + if err := json.Unmarshal(r.Data, &resp); err != nil { + c.err = err + return false + } + if resp.Error != nil { + c.err = errors.New(resp.Error.Description) + return false + } + + c.pageInfo = &resp.apiPaged + c.page = resp.Consumers + c.offset += len(c.page) + return true +} + +// Page returns the current ConsumerInfo page. +func (c *ConsumerLister) Page() []*ConsumerInfo { + return c.page +} + +// Err returns any errors found while fetching pages. +func (c *ConsumerLister) Err() error { + return c.err +} + +// NewConsumerLister is used to return pages of ConsumerInfo objects. +func (js *js) NewConsumerLister(stream string) *ConsumerLister { + return &ConsumerLister{stream: stream, js: js} +} + +// streamCreateResponse stream creation. +type streamCreateResponse struct { + apiResponse + *StreamInfo +} + +func (js *js) AddStream(cfg *StreamConfig) (*StreamInfo, error) { + if cfg == nil || cfg.Name == _EMPTY_ { + return nil, ErrStreamNameRequired + } + + req, err := json.Marshal(cfg) + if err != nil { + return nil, err + } + + csSubj := js.apiSubj(fmt.Sprintf(apiStreamCreateT, cfg.Name)) + r, err := js.nc.Request(csSubj, req, js.wait) + if err != nil { + return nil, err + } + var resp streamCreateResponse + if err := json.Unmarshal(r.Data, &resp); err != nil { + return nil, err + } + if resp.Error != nil { + return nil, errors.New(resp.Error.Description) + } + return resp.StreamInfo, nil +} + +type streamInfoResponse = streamCreateResponse + +func (js *js) StreamInfo(stream string) (*StreamInfo, error) { + csSubj := js.apiSubj(fmt.Sprintf(apiStreamInfoT, stream)) + r, err := js.nc.Request(csSubj, nil, js.wait) + if err != nil { + return nil, err + } + var resp streamInfoResponse + if err := json.Unmarshal(r.Data, &resp); err != nil { + return nil, err + } + if resp.Error != nil { + return nil, errors.New(resp.Error.Description) + } + return resp.StreamInfo, nil +} + +// StreamInfo shows config and current state for this stream. +type StreamInfo struct { + Config StreamConfig `json:"config"` + Created time.Time `json:"created"` + State StreamState `json:"state"` + Cluster *ClusterInfo `json:"cluster,omitempty"` + Mirror *StreamSourceInfo `json:"mirror,omitempty"` + Sources []*StreamSourceInfo `json:"sources,omitempty"` +} + +// StreamSourceInfo shows information about an upstream stream source. +type StreamSourceInfo struct { + Name string `json:"name"` + Lag uint64 `json:"lag"` + Active time.Duration `json:"active"` +} + +// StreamState is information about the given stream. +type StreamState struct { + Msgs uint64 `json:"messages"` + Bytes uint64 `json:"bytes"` + FirstSeq uint64 `json:"first_seq"` + FirstTime time.Time `json:"first_ts"` + LastSeq uint64 `json:"last_seq"` + LastTime time.Time `json:"last_ts"` + Consumers int `json:"consumer_count"` +} + +// ClusterInfo shows information about the underlying set of servers +// that make up the stream or consumer. +type ClusterInfo struct { + Name string `json:"name,omitempty"` + Leader string `json:"leader,omitempty"` + Replicas []*PeerInfo `json:"replicas,omitempty"` +} + +// PeerInfo shows information about all the peers in the cluster that +// are supporting the stream or consumer. +type PeerInfo struct { + Name string `json:"name"` + Current bool `json:"current"` + Offline bool `json:"offline,omitempty"` + Active time.Duration `json:"active"` + Lag uint64 `json:"lag,omitempty"` +} + +// UpdateStream updates a Stream. +func (js *js) UpdateStream(cfg *StreamConfig) (*StreamInfo, error) { + if cfg == nil || cfg.Name == _EMPTY_ { + return nil, ErrStreamNameRequired + } + + req, err := json.Marshal(cfg) + if err != nil { + return nil, err + } + + usSubj := js.apiSubj(fmt.Sprintf(apiStreamUpdateT, cfg.Name)) + r, err := js.nc.Request(usSubj, req, js.wait) + if err != nil { + return nil, err + } + var resp streamInfoResponse + if err := json.Unmarshal(r.Data, &resp); err != nil { + return nil, err + } + if resp.Error != nil { + return nil, errors.New(resp.Error.Description) + } + return resp.StreamInfo, nil +} + +// streamDeleteResponse is the response for a Stream delete request. +type streamDeleteResponse struct { + apiResponse + Success bool `json:"success,omitempty"` +} + +// DeleteStream deletes a Stream. +func (js *js) DeleteStream(name string) error { + if name == _EMPTY_ { + return ErrStreamNameRequired + } + + dsSubj := js.apiSubj(fmt.Sprintf(apiStreamDeleteT, name)) + r, err := js.nc.Request(dsSubj, nil, js.wait) + if err != nil { + return err + } + var resp streamDeleteResponse + if err := json.Unmarshal(r.Data, &resp); err != nil { + return err + } + if resp.Error != nil { + return errors.New(resp.Error.Description) + } + return nil +} + +type apiMsgGetRequest struct { + Seq uint64 `json:"seq"` +} + +// RawStreamMsg is a raw message stored in JetStream. +type RawStreamMsg struct { + Subject string + Sequence uint64 + Header http.Header + Data []byte + Time time.Time +} + +// storedMsg is a raw message stored in JetStream. +type storedMsg struct { + Subject string `json:"subject"` + Sequence uint64 `json:"seq"` + Header []byte `json:"hdrs,omitempty"` + Data []byte `json:"data,omitempty"` + Time time.Time `json:"time"` +} + +// apiMsgGetResponse is the response for a Stream get request. +type apiMsgGetResponse struct { + apiResponse + Message *storedMsg `json:"message,omitempty"` + Success bool `json:"success,omitempty"` +} + +// GetMsg retrieves a raw stream message stored in JetStream by sequence number. +func (js *js) GetMsg(name string, seq uint64) (*RawStreamMsg, error) { + if name == _EMPTY_ { + return nil, ErrStreamNameRequired + } + + req, err := json.Marshal(&apiMsgGetRequest{Seq: seq}) + if err != nil { + return nil, err + } + + dsSubj := js.apiSubj(fmt.Sprintf(apiMsgGetT, name)) + r, err := js.nc.Request(dsSubj, req, js.wait) + if err != nil { + return nil, err + } + + var resp apiMsgGetResponse + if err := json.Unmarshal(r.Data, &resp); err != nil { + return nil, err + } + if resp.Error != nil { + return nil, errors.New(resp.Error.Description) + } + + msg := resp.Message + + var hdr http.Header + if msg.Header != nil { + hdr, err = decodeHeadersMsg(msg.Header) + if err != nil { + return nil, err + } + } + + return &RawStreamMsg{ + Subject: msg.Subject, + Sequence: msg.Sequence, + Header: hdr, + Data: msg.Data, + Time: msg.Time, + }, nil +} + +type msgDeleteRequest struct { + Seq uint64 `json:"seq"` +} + +// msgDeleteResponse is the response for a Stream delete request. +type msgDeleteResponse struct { + apiResponse + Success bool `json:"success,omitempty"` +} + +// DeleteMsg deletes a message from a stream. +func (js *js) DeleteMsg(name string, seq uint64) error { + if name == _EMPTY_ { + return ErrStreamNameRequired + } + + req, err := json.Marshal(&msgDeleteRequest{Seq: seq}) + if err != nil { + return err + } + + dsSubj := js.apiSubj(fmt.Sprintf(apiMsgDeleteT, name)) + r, err := js.nc.Request(dsSubj, req, js.wait) + if err != nil { + return err + } + var resp msgDeleteResponse + if err := json.Unmarshal(r.Data, &resp); err != nil { + return err + } + if resp.Error != nil { + return errors.New(resp.Error.Description) + } + return nil +} + +type streamPurgeResponse struct { + apiResponse + Success bool `json:"success,omitempty"` + Purged uint64 `json:"purged"` +} + +// PurgeStream purges messages on a Stream. +func (js *js) PurgeStream(name string) error { + psSubj := js.apiSubj(fmt.Sprintf(apiStreamPurgeT, name)) + r, err := js.nc.Request(psSubj, nil, js.wait) + if err != nil { + return err + } + var resp streamPurgeResponse + if err := json.Unmarshal(r.Data, &resp); err != nil { + return err + } + if resp.Error != nil { + return errors.New(resp.Error.Description) + } + return nil +} + +// StreamLister fetches pages of StreamInfo objects. This object is not safe +// to use for multiple threads. +type StreamLister struct { + js *js + page []*StreamInfo + err error + + offset int + pageInfo *apiPaged +} + +// streamListResponse list of detailed stream information. +// A nil request is valid and means all streams. +type streamListResponse struct { + apiResponse + apiPaged + Streams []*StreamInfo `json:"streams"` +} + +// streamNamesRequest is used for Stream Name requests. +type streamNamesRequest struct { + apiPagedRequest + // These are filters that can be applied to the list. + Subject string `json:"subject,omitempty"` +} + +// Next fetches the next StreamInfo page. +func (s *StreamLister) Next() bool { + if s.err != nil { + return false + } + if s.pageInfo != nil && s.offset >= s.pageInfo.Total { + return false + } + + req, err := json.Marshal(streamNamesRequest{ + apiPagedRequest: apiPagedRequest{Offset: s.offset}, + }) + if err != nil { + s.err = err + return false + } + + slSubj := s.js.apiSubj(apiStreamList) + r, err := s.js.nc.Request(slSubj, req, s.js.wait) + if err != nil { + s.err = err + return false + } + var resp streamListResponse + if err := json.Unmarshal(r.Data, &resp); err != nil { + s.err = err + return false + } + if resp.Error != nil { + s.err = errors.New(resp.Error.Description) + return false + } + + s.pageInfo = &resp.apiPaged + s.page = resp.Streams + s.offset += len(s.page) + return true +} + +// Page returns the current StreamInfo page. +func (s *StreamLister) Page() []*StreamInfo { + return s.page +} + +// Err returns any errors found while fetching pages. +func (s *StreamLister) Err() error { + return s.err +} + +// NewStreamLister is used to return pages of StreamInfo objects. +func (js *js) NewStreamLister() *StreamLister { + return &StreamLister{js: js} +} diff --git a/vendor/github.com/nats-io/nats.go/nats.go b/vendor/github.com/nats-io/nats.go/nats.go index d0d569cc..b19021a4 100644 --- a/vendor/github.com/nats-io/nats.go/nats.go +++ b/vendor/github.com/nats-io/nats.go/nats.go @@ -1,4 +1,4 @@ -// Copyright 2012-2020 The NATS Authors +// Copyright 2012-2021 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -27,6 +27,8 @@ import ( "io/ioutil" "math/rand" "net" + "net/http" + "net/textproto" "net/url" "os" "path/filepath" @@ -37,7 +39,6 @@ import ( "sync/atomic" "time" - "github.com/nats-io/jwt" "github.com/nats-io/nats.go/util" "github.com/nats-io/nkeys" "github.com/nats-io/nuid" @@ -45,7 +46,7 @@ import ( // Default Constants const ( - Version = "1.10.0" + Version = "1.11.0" DefaultURL = "nats://127.0.0.1:4222" DefaultPort = 4222 DefaultMaxReconnect = 60 @@ -55,7 +56,7 @@ const ( DefaultTimeout = 2 * time.Second DefaultPingInterval = 2 * time.Minute DefaultMaxPingOut = 2 - DefaultMaxChanLen = 8192 // 8k + DefaultMaxChanLen = 64 * 1024 // 64k DefaultReconnectBufSize = 8 * 1024 * 1024 // 8MB RequestChanLen = 8 DefaultDrainTimeout = 30 * time.Second @@ -78,48 +79,69 @@ const ( // Errors var ( - ErrConnectionClosed = errors.New("nats: connection closed") - ErrConnectionDraining = errors.New("nats: connection draining") - ErrDrainTimeout = errors.New("nats: draining connection timed out") - ErrConnectionReconnecting = errors.New("nats: connection reconnecting") - ErrSecureConnRequired = errors.New("nats: secure connection required") - ErrSecureConnWanted = errors.New("nats: secure connection not available") - ErrBadSubscription = errors.New("nats: invalid subscription") - ErrTypeSubscription = errors.New("nats: invalid subscription type") - ErrBadSubject = errors.New("nats: invalid subject") - ErrBadQueueName = errors.New("nats: invalid queue name") - ErrSlowConsumer = errors.New("nats: slow consumer, messages dropped") - ErrTimeout = errors.New("nats: timeout") - ErrBadTimeout = errors.New("nats: timeout invalid") - ErrAuthorization = errors.New("nats: authorization violation") - ErrAuthExpired = errors.New("nats: authentication expired") - ErrNoServers = errors.New("nats: no servers available for connection") - ErrJsonParse = errors.New("nats: connect message, json parse error") - ErrChanArg = errors.New("nats: argument needs to be a channel type") - ErrMaxPayload = errors.New("nats: maximum payload exceeded") - ErrMaxMessages = errors.New("nats: maximum messages delivered") - ErrSyncSubRequired = errors.New("nats: illegal call on an async subscription") - ErrMultipleTLSConfigs = errors.New("nats: multiple tls.Configs not allowed") - ErrNoInfoReceived = errors.New("nats: protocol exception, INFO not received") - ErrReconnectBufExceeded = errors.New("nats: outbound buffer limit exceeded") - ErrInvalidConnection = errors.New("nats: invalid connection") - ErrInvalidMsg = errors.New("nats: invalid message or message nil") - ErrInvalidArg = errors.New("nats: invalid argument") - ErrInvalidContext = errors.New("nats: invalid context") - ErrNoDeadlineContext = errors.New("nats: context requires a deadline") - ErrNoEchoNotSupported = errors.New("nats: no echo option not supported by this server") - ErrClientIDNotSupported = errors.New("nats: client ID not supported by this server") - ErrUserButNoSigCB = errors.New("nats: user callback defined without a signature handler") - ErrNkeyButNoSigCB = errors.New("nats: nkey defined without a signature handler") - ErrNoUserCB = errors.New("nats: user callback not defined") - ErrNkeyAndUser = errors.New("nats: user callback and nkey defined") - ErrNkeysNotSupported = errors.New("nats: nkeys not supported by the server") - ErrStaleConnection = errors.New("nats: " + STALE_CONNECTION) - ErrTokenAlreadySet = errors.New("nats: token and token handler both set") - ErrMsgNotBound = errors.New("nats: message is not bound to subscription/connection") - ErrMsgNoReply = errors.New("nats: message does not have a reply") - ErrClientIPNotSupported = errors.New("nats: client IP not supported by this server") - ErrDisconnected = errors.New("nats: server is disconnected") + ErrConnectionClosed = errors.New("nats: connection closed") + ErrConnectionDraining = errors.New("nats: connection draining") + ErrDrainTimeout = errors.New("nats: draining connection timed out") + ErrConnectionReconnecting = errors.New("nats: connection reconnecting") + ErrSecureConnRequired = errors.New("nats: secure connection required") + ErrSecureConnWanted = errors.New("nats: secure connection not available") + ErrBadSubscription = errors.New("nats: invalid subscription") + ErrTypeSubscription = errors.New("nats: invalid subscription type") + ErrBadSubject = errors.New("nats: invalid subject") + ErrBadQueueName = errors.New("nats: invalid queue name") + ErrSlowConsumer = errors.New("nats: slow consumer, messages dropped") + ErrTimeout = errors.New("nats: timeout") + ErrBadTimeout = errors.New("nats: timeout invalid") + ErrAuthorization = errors.New("nats: authorization violation") + ErrAuthExpired = errors.New("nats: authentication expired") + ErrNoServers = errors.New("nats: no servers available for connection") + ErrJsonParse = errors.New("nats: connect message, json parse error") + ErrChanArg = errors.New("nats: argument needs to be a channel type") + ErrMaxPayload = errors.New("nats: maximum payload exceeded") + ErrMaxMessages = errors.New("nats: maximum messages delivered") + ErrSyncSubRequired = errors.New("nats: illegal call on an async subscription") + ErrMultipleTLSConfigs = errors.New("nats: multiple tls.Configs not allowed") + ErrNoInfoReceived = errors.New("nats: protocol exception, INFO not received") + ErrReconnectBufExceeded = errors.New("nats: outbound buffer limit exceeded") + ErrInvalidConnection = errors.New("nats: invalid connection") + ErrInvalidMsg = errors.New("nats: invalid message or message nil") + ErrInvalidArg = errors.New("nats: invalid argument") + ErrInvalidContext = errors.New("nats: invalid context") + ErrNoDeadlineContext = errors.New("nats: context requires a deadline") + ErrNoEchoNotSupported = errors.New("nats: no echo option not supported by this server") + ErrClientIDNotSupported = errors.New("nats: client ID not supported by this server") + ErrUserButNoSigCB = errors.New("nats: user callback defined without a signature handler") + ErrNkeyButNoSigCB = errors.New("nats: nkey defined without a signature handler") + ErrNoUserCB = errors.New("nats: user callback not defined") + ErrNkeyAndUser = errors.New("nats: user callback and nkey defined") + ErrNkeysNotSupported = errors.New("nats: nkeys not supported by the server") + ErrStaleConnection = errors.New("nats: " + STALE_CONNECTION) + ErrTokenAlreadySet = errors.New("nats: token and token handler both set") + ErrMsgNotBound = errors.New("nats: message is not bound to subscription/connection") + ErrMsgNoReply = errors.New("nats: message does not have a reply") + ErrClientIPNotSupported = errors.New("nats: client IP not supported by this server") + ErrDisconnected = errors.New("nats: server is disconnected") + ErrHeadersNotSupported = errors.New("nats: headers not supported by this server") + ErrBadHeaderMsg = errors.New("nats: message could not decode headers") + ErrNoResponders = errors.New("nats: no responders available for request") + ErrNoContextOrTimeout = errors.New("nats: no context or timeout given") + ErrDirectModeRequired = errors.New("nats: direct access requires direct pull or push") + ErrPullModeNotAllowed = errors.New("nats: pull based not supported") + ErrJetStreamNotEnabled = errors.New("nats: jetstream not enabled") + ErrJetStreamBadPre = errors.New("nats: jetstream api prefix not valid") + ErrNoStreamResponse = errors.New("nats: no response from stream") + ErrNotJSMessage = errors.New("nats: not a jetstream message") + ErrInvalidStreamName = errors.New("nats: invalid stream name") + ErrInvalidDurableName = errors.New("nats: invalid durable name") + ErrNoMatchingStream = errors.New("nats: no stream matches subject") + ErrSubjectMismatch = errors.New("nats: subject does not match consumer") + ErrContextAndTimeout = errors.New("nats: context and timeout can not both be set") + ErrInvalidJSAck = errors.New("nats: invalid jetstream publish response") + ErrMultiStreamUnsupported = errors.New("nats: multiple streams are not supported") + ErrStreamNameRequired = errors.New("nats: stream name is required") + ErrConsumerConfigRequired = errors.New("nats: consumer configuration is required") + ErrStreamSnapshotConfigRequired = errors.New("nats: stream snapshot configuration is required") + ErrDeliverSubjectRequired = errors.New("nats: deliver subject is required") ) func init() { @@ -382,6 +404,21 @@ type Options struct { // callbacks after Close() is called. Client won't receive notifications // when Close is invoked by user code. Default is to invoke the callbacks. NoCallbacksAfterClientClose bool + + // LameDuckModeHandler sets the callback to invoke when the server notifies + // the connection that it entered lame duck mode, that is, going to + // gradually disconnect all its connections before shuting down. This is + // often used in deployments when upgrading NATS Servers. + LameDuckModeHandler ConnHandler + + // RetryOnFailedConnect sets the connection in reconnecting state right + // away if it can't connect to a server in the initial set. The + // MaxReconnect and ReconnectWait options are used for this process, + // similarly to when an established connection is disconnected. + // If a ReconnectHandler is set, it will be invoked when the connection + // is established, and if a ClosedHandler is set, it will be invoked if + // it fails to connect (after exhausting the MaxReconnect attempts). + RetryOnFailedConnect bool } const ( @@ -449,7 +486,7 @@ type Conn struct { respRand *rand.Rand // Used for generating suffix } -// A Subscription represents interest in a given subject. +// Subscription represents interest in a given subject. type Subscription struct { mu sync.Mutex sid int64 @@ -463,6 +500,9 @@ type Subscription struct { // only be processed by one member of the group. Queue string + // For holding information about a JetStream consumer. + jsi *jsSub + delivered uint64 max uint64 conn *Conn @@ -490,14 +530,42 @@ type Subscription struct { dropped int } -// Msg is a structure used by Subscribers and PublishMsg(). +// Msg represents a message delivered by NATS. This structure is used +// by Subscribers and PublishMsg(). type Msg struct { Subject string Reply string + Header http.Header Data []byte Sub *Subscription next *Msg barrier *barrierInfo + ackd uint32 +} + +func (m *Msg) headerBytes() ([]byte, error) { + var hdr []byte + if len(m.Header) == 0 { + return hdr, nil + } + + var b bytes.Buffer + _, err := b.WriteString(hdrLine) + if err != nil { + return nil, ErrBadHeaderMsg + } + + err = m.Header.Write(&b) + if err != nil { + return nil, ErrBadHeaderMsg + } + + _, err = b.WriteString(crlf) + if err != nil { + return nil, ErrBadHeaderMsg + } + + return b.Bytes(), nil } type barrierInfo struct { @@ -525,19 +593,24 @@ type srv struct { tlsName string } +// The INFO block received from the server. type serverInfo struct { ID string `json:"server_id"` + Name string `json:"server_name"` + Proto int `json:"proto"` Host string `json:"host"` - Port uint `json:"port"` - Version string `json:"version"` - AuthRequired bool `json:"auth_required"` - TLSRequired bool `json:"tls_required"` + Port int `json:"port"` + Headers bool `json:"headers"` + AuthRequired bool `json:"auth_required,omitempty"` + TLSRequired bool `json:"tls_required,omitempty"` + TLSAvailable bool `json:"tls_available,omitempty"` MaxPayload int64 `json:"max_payload"` - ConnectURLs []string `json:"connect_urls,omitempty"` - Proto int `json:"proto,omitempty"` CID uint64 `json:"client_id,omitempty"` ClientIP string `json:"client_ip,omitempty"` Nonce string `json:"nonce,omitempty"` + Cluster string `json:"cluster,omitempty"` + ConnectURLs []string `json:"connect_urls,omitempty"` + LameDuckMode bool `json:"ldm,omitempty"` } const ( @@ -550,20 +623,22 @@ const ( ) type connectInfo struct { - Verbose bool `json:"verbose"` - Pedantic bool `json:"pedantic"` - UserJWT string `json:"jwt,omitempty"` - Nkey string `json:"nkey,omitempty"` - Signature string `json:"sig,omitempty"` - User string `json:"user,omitempty"` - Pass string `json:"pass,omitempty"` - Token string `json:"auth_token,omitempty"` - TLS bool `json:"tls_required"` - Name string `json:"name"` - Lang string `json:"lang"` - Version string `json:"version"` - Protocol int `json:"protocol"` - Echo bool `json:"echo"` + Verbose bool `json:"verbose"` + Pedantic bool `json:"pedantic"` + UserJWT string `json:"jwt,omitempty"` + Nkey string `json:"nkey,omitempty"` + Signature string `json:"sig,omitempty"` + User string `json:"user,omitempty"` + Pass string `json:"pass,omitempty"` + Token string `json:"auth_token,omitempty"` + TLS bool `json:"tls_required"` + Name string `json:"name"` + Lang string `json:"lang"` + Version string `json:"version"` + Protocol int `json:"protocol"` + Echo bool `json:"echo"` + Headers bool `json:"headers"` + NoResponders bool `json:"no_responders"` } // MsgHandler is a callback function that processes messages delivered to @@ -808,7 +883,7 @@ func DiscoveredServersHandler(cb ConnHandler) Option { } } -// ErrorHandler is an Option to set the async error handler. +// ErrorHandler is an Option to set the async error handler. func ErrorHandler(cb ErrHandler) Option { return func(o *Options) error { o.AsyncErrorCB = cb @@ -947,6 +1022,27 @@ func NoCallbacksAfterClientClose() Option { } } +// LameDuckModeHandler sets the callback to invoke when the server notifies +// the connection that it entered lame duck mode, that is, going to +// gradually disconnect all its connections before shuting down. This is +// often used in deployments when upgrading NATS Servers. +func LameDuckModeHandler(cb ConnHandler) Option { + return func(o *Options) error { + o.LameDuckModeHandler = cb + return nil + } +} + +// RetryOnFailedConnect sets the connection in reconnecting state right away +// if it can't connect to a server in the initial set. +// See RetryOnFailedConnect option for more details. +func RetryOnFailedConnect(retry bool) Option { + return func(o *Options) error { + o.RetryOnFailedConnect = retry + return nil + } +} + // Handler processing // SetDisconnectHandler will set the disconnect event handler. @@ -1066,6 +1162,11 @@ func (o Options) Connect() (*Conn, error) { nc.ach = &asyncCallbacksHandler{} nc.ach.cond = sync.NewCond(&nc.ach.mu) + // Set a default error handler that will print to stderr. + if nc.Opts.AsyncErrorCB == nil { + nc.Opts.AsyncErrorCB = defaultErrHandler + } + if err := nc.connect(); err != nil { return nil, err } @@ -1076,11 +1177,28 @@ func (o Options) Connect() (*Conn, error) { return nc, nil } +func defaultErrHandler(nc *Conn, sub *Subscription, err error) { + var cid uint64 + if nc != nil { + nc.mu.RLock() + cid = nc.info.CID + nc.mu.RUnlock() + } + var errStr string + if sub != nil { + errStr = fmt.Sprintf("%s on connection [%d] for subscription on %q\n", err.Error(), cid, sub.Subject) + } else { + errStr = fmt.Sprintf("%s on connection [%d]\n", err.Error(), cid) + } + os.Stderr.WriteString(errStr) +} + const ( - _CRLF_ = "\r\n" - _EMPTY_ = "" - _SPC_ = " " - _PUB_P_ = "PUB " + _CRLF_ = "\r\n" + _EMPTY_ = "" + _SPC_ = " " + _PUB_P_ = "PUB " + _HPUB_P_ = "HPUB " ) const ( @@ -1091,12 +1209,12 @@ const ( ) const ( - conProto = "CONNECT %s" + _CRLF_ - pingProto = "PING" + _CRLF_ - pongProto = "PONG" + _CRLF_ - subProto = "SUB %s %s %d" + _CRLF_ - unsubProto = "UNSUB %d %s" + _CRLF_ - okProto = _OK_OP_ + _CRLF_ + connectProto = "CONNECT %s" + _CRLF_ + pingProto = "PING" + _CRLF_ + pongProto = "PONG" + _CRLF_ + subProto = "SUB %s %s %d" + _CRLF_ + unsubProto = "UNSUB %d %s" + _CRLF_ + okProto = _OK_OP_ + _CRLF_ ) // Return the currently selected server @@ -1391,7 +1509,7 @@ func (nc *Conn) waitForExits() { nc.wg.Wait() } -// Report the connected server's Url +// ConnectedUrl reports the connected server's URL func (nc *Conn) ConnectedUrl() string { if nc == nil { return _EMPTY_ @@ -1421,7 +1539,7 @@ func (nc *Conn) ConnectedAddr() string { return nc.conn.RemoteAddr().String() } -// Report the connected server's Id +// ConnectedServerId reports the connected server's Id func (nc *Conn) ConnectedServerId() string { if nc == nil { return _EMPTY_ @@ -1436,6 +1554,36 @@ func (nc *Conn) ConnectedServerId() string { return nc.info.ID } +// ConnectedServerName reports the connected server's name +func (nc *Conn) ConnectedServerName() string { + if nc == nil { + return _EMPTY_ + } + + nc.mu.RLock() + defer nc.mu.RUnlock() + + if nc.status != CONNECTED { + return _EMPTY_ + } + return nc.info.Name +} + +// ConnectedClusterName reports the connected server's cluster name if any +func (nc *Conn) ConnectedClusterName() string { + if nc == nil { + return _EMPTY_ + } + + nc.mu.RLock() + defer nc.mu.RUnlock() + + if nc.status != CONNECTED { + return _EMPTY_ + } + return nc.info.Cluster +} + // Low level setup for structs, etc func (nc *Conn) setup() { nc.subs = make(map[int64]*Subscription) @@ -1444,9 +1592,9 @@ func (nc *Conn) setup() { nc.fch = make(chan struct{}, flushChanSize) nc.rqch = make(chan struct{}) - // Setup scratch outbound buffer for PUB - pub := nc.scratch[:len(_PUB_P_)] - copy(pub, _PUB_P_) + // Setup scratch outbound buffer for PUB/HPUB + pub := nc.scratch[:len(_HPUB_P_)] + copy(pub, _HPUB_P_) } // Process a connected connection and initialize properly. @@ -1524,7 +1672,9 @@ func (nc *Conn) connect() error { nc.mu.Unlock() nc.close(DISCONNECTED, false, err) nc.mu.Lock() - nc.current = nil + // Do not reset nc.current here since it would prevent + // RetryOnFailedConnect to work should this be the last server + // to try before starting doReconnect(). } } else { // Cancel out default connection refused, will trigger the @@ -1534,11 +1684,27 @@ func (nc *Conn) connect() error { } } } - nc.initc = false + if returnedErr == nil && nc.status != CONNECTED { returnedErr = ErrNoServers } + if returnedErr == nil { + nc.initc = false + } else if nc.Opts.RetryOnFailedConnect { + nc.setup() + nc.status = RECONNECTING + nc.pending = new(bytes.Buffer) + if nc.bw == nil { + nc.bw = nc.newBuffer() + } + nc.bw.Reset(nc.pending) + go nc.doReconnect(ErrNoServers) + returnedErr = nil + } else { + nc.current = nil + } + return returnedErr } @@ -1550,7 +1716,7 @@ func (nc *Conn) checkForSecure() error { o := nc.Opts // Check for mismatch in setups - if o.Secure && !nc.info.TLSRequired { + if o.Secure && !nc.info.TLSRequired && !nc.info.TLSAvailable { return ErrSecureConnWanted } else if nc.info.TLSRequired && !o.Secure { // Switch to Secure since server needs TLS. @@ -1659,8 +1825,10 @@ func (nc *Conn) connectProto() (string, error) { token = nc.Opts.TokenHandler() } + // If our server does not support headers then we can't do them or no responders. + hdrs := nc.info.Headers cinfo := connectInfo{o.Verbose, o.Pedantic, ujwt, nkey, sig, user, pass, token, - o.Secure, o.Name, LangString, Version, clientProtoInfo, !o.NoEcho} + o.Secure, o.Name, LangString, Version, clientProtoInfo, !o.NoEcho, hdrs, hdrs} b, err := json.Marshal(cinfo) if err != nil { @@ -1672,7 +1840,7 @@ func (nc *Conn) connectProto() (string, error) { return _EMPTY_, ErrNoEchoNotSupported } - return fmt.Sprintf(conProto, b), nil + return fmt.Sprintf(connectProto, b), nil } // normalizeErr removes the prefix -ERR, trim spaces and remove the quotes. @@ -1856,10 +2024,12 @@ func (nc *Conn) doReconnect(err error) { nc.err = nil // Perform appropriate callback if needed for a disconnect. // DisconnectedErrCB has priority over deprecated DisconnectedCB - if nc.Opts.DisconnectedErrCB != nil { - nc.ach.push(func() { nc.Opts.DisconnectedErrCB(nc, err) }) - } else if nc.Opts.DisconnectedCB != nil { - nc.ach.push(func() { nc.Opts.DisconnectedCB(nc) }) + if !nc.initc { + if nc.Opts.DisconnectedErrCB != nil { + nc.ach.push(func() { nc.Opts.DisconnectedErrCB(nc, err) }) + } else if nc.Opts.DisconnectedCB != nil { + nc.ach.push(func() { nc.Opts.DisconnectedCB(nc) }) + } } // This is used to wait on go routines exit if we start them in the loop @@ -2000,6 +2170,10 @@ func (nc *Conn) doReconnect(err error) { // This is where we are truly connected. nc.status = CONNECTED + // If we are here with a retry on failed connect, indicate that the + // initial connect is now complete. + nc.initc = false + // Queue up the reconnect callback. if nc.Opts.ReconnectedCB != nil { nc.ach.push(func() { nc.Opts.ReconnectedCB(nc) }) @@ -2238,18 +2412,18 @@ func (nc *Conn) waitForMsgs(s *Subscription) { // or the pending queue is over the pending limits, the connection is // considered a slow consumer. func (nc *Conn) processMsg(data []byte) { - // Don't lock the connection to avoid server cutting us off if the - // flusher is holding the connection lock, trying to send to the server - // that is itself trying to send data to us. - nc.subsMu.RLock() - // Stats atomic.AddUint64(&nc.InMsgs, 1) atomic.AddUint64(&nc.InBytes, uint64(len(data))) + // Don't lock the connection to avoid server cutting us off if the + // flusher is holding the connection lock, trying to send to the server + // that is itself trying to send data to us. + nc.subsMu.RLock() sub := nc.subs[nc.ps.ma.sid] + nc.subsMu.RUnlock() + if sub == nil { - nc.subsMu.RUnlock() return } @@ -2264,11 +2438,36 @@ func (nc *Conn) processMsg(data []byte) { msgPayload := make([]byte, len(data)) copy(msgPayload, data) + // Check if we have headers encoded here. + var h http.Header + var err error + + if nc.ps.ma.hdr > 0 { + hbuf := msgPayload[:nc.ps.ma.hdr] + msgPayload = msgPayload[nc.ps.ma.hdr:] + h, err = decodeHeadersMsg(hbuf) + if err != nil { + // We will pass the message through but send async error. + nc.mu.Lock() + nc.err = ErrBadHeaderMsg + if nc.Opts.AsyncErrorCB != nil { + nc.ach.push(func() { nc.Opts.AsyncErrorCB(nc, sub, ErrBadHeaderMsg) }) + } + nc.mu.Unlock() + } + } + // FIXME(dlc): Should we recycle these containers? - m := &Msg{Data: msgPayload, Subject: subj, Reply: reply, Sub: sub} + m := &Msg{Header: h, Data: msgPayload, Subject: subj, Reply: reply, Sub: sub} sub.mu.Lock() + // Check if closed. + if sub.closed { + sub.mu.Unlock() + return + } + // Subscription internal stats (applicable only for non ChanSubscription's) if sub.typ != ChanSubscription { sub.pMsgs++ @@ -2300,7 +2499,9 @@ func (nc *Conn) processMsg(data []byte) { if sub.pHead == nil { sub.pHead = m sub.pTail = m - sub.pCond.Signal() + if sub.pCond != nil { + sub.pCond.Signal() + } } else { sub.pTail.next = m sub.pTail = m @@ -2311,7 +2512,6 @@ func (nc *Conn) processMsg(data []byte) { sub.sc = false sub.mu.Unlock() - nc.subsMu.RUnlock() return slowConsumer: @@ -2324,7 +2524,6 @@ slowConsumer: sub.pBytes -= len(m.Data) } sub.mu.Unlock() - nc.subsMu.RUnlock() if sc { // Now we need connection's lock and we may end-up in the situation // that we were trying to avoid, except that in this case, the client @@ -2456,7 +2655,10 @@ func (nc *Conn) processInfo(info string) error { // if advertise is disabled on that server, or servers that // did not include themselves in the async INFO protocol. // If empty, do not remove the implicit servers from the pool. - if len(ncInfo.ConnectURLs) == 0 { + if len(nc.info.ConnectURLs) == 0 { + if !nc.initc && ncInfo.LameDuckMode && nc.Opts.LameDuckModeHandler != nil { + nc.ach.push(func() { nc.Opts.LameDuckModeHandler(nc) }) + } return nil } // Note about pool randomization: when the pool was first created, @@ -2517,7 +2719,9 @@ func (nc *Conn) processInfo(info string) error { nc.ach.push(func() { nc.Opts.DiscoveredServersCB(nc) }) } } - + if !nc.initc && ncInfo.LameDuckMode && nc.Opts.LameDuckModeHandler != nil { + nc.ach.push(func() { nc.Opts.LameDuckModeHandler(nc) }) + } return nil } @@ -2601,7 +2805,52 @@ func (nc *Conn) kickFlusher() { // argument is left untouched and needs to be correctly interpreted on // the receiver. func (nc *Conn) Publish(subj string, data []byte) error { - return nc.publish(subj, _EMPTY_, data) + return nc.publish(subj, _EMPTY_, nil, data) +} + +// NewMsg creates a message for publishing that will use headers. +func NewMsg(subject string) *Msg { + return &Msg{ + Subject: subject, + Header: make(http.Header), + } +} + +const ( + hdrLine = "NATS/1.0\r\n" + crlf = "\r\n" + hdrPreEnd = len(hdrLine) - len(crlf) + statusHdr = "Status" + descrHdr = "Description" + noResponders = "503" + statusLen = 3 // e.g. 20x, 40x, 50x +) + +// decodeHeadersMsg will decode and headers. +func decodeHeadersMsg(data []byte) (http.Header, error) { + tp := textproto.NewReader(bufio.NewReader(bytes.NewReader(data))) + l, err := tp.ReadLine() + if err != nil || len(l) < hdrPreEnd || l[:hdrPreEnd] != hdrLine[:hdrPreEnd] { + return nil, ErrBadHeaderMsg + } + mh, err := tp.ReadMIMEHeader() + if err != nil { + return nil, ErrBadHeaderMsg + } + // Check if we have an inlined status. + if len(l) > hdrPreEnd { + var description string + status := strings.TrimSpace(l[hdrPreEnd:]) + if len(status) != statusLen { + description = strings.TrimSpace(status[statusLen:]) + status = status[:statusLen] + } + mh.Add(statusHdr, status) + if len(description) > 0 { + mh.Add(descrHdr, description) + } + } + return http.Header(mh), nil } // PublishMsg publishes the Msg structure, which includes the @@ -2610,14 +2859,29 @@ func (nc *Conn) PublishMsg(m *Msg) error { if m == nil { return ErrInvalidMsg } - return nc.publish(m.Subject, m.Reply, m.Data) + + var hdr []byte + var err error + + if len(m.Header) > 0 { + if !nc.info.Headers { + return ErrHeadersNotSupported + } + + hdr, err = m.headerBytes() + if err != nil { + return err + } + } + + return nc.publish(m.Subject, m.Reply, hdr, m.Data) } -// PublishRequest will perform a Publish() excpecting a response on the +// PublishRequest will perform a Publish() expecting a response on the // reply subject. Use Request() for automatically waiting for a response // inline. func (nc *Conn) PublishRequest(subj, reply string, data []byte) error { - return nc.publish(subj, reply, data) + return nc.publish(subj, reply, nil, data) } // Used for handrolled itoa @@ -2626,7 +2890,7 @@ const digits = "0123456789" // publish is the internal function to publish messages to a nats-server. // Sends a protocol data message by queuing into the bufio writer // and kicking the flush go routine. These writes should be protected. -func (nc *Conn) publish(subj, reply string, data []byte) error { +func (nc *Conn) publish(subj, reply string, hdr, data []byte) error { if nc == nil { return ErrInvalidConnection } @@ -2646,8 +2910,9 @@ func (nc *Conn) publish(subj, reply string, data []byte) error { } // Proactively reject payloads over the threshold set by server. - msgSize := int64(len(data)) - if msgSize > nc.info.MaxPayload { + msgSize := int64(len(data) + len(hdr)) + // Skip this check if we are not yet connected (RetryOnFailedConnect) + if !nc.initc && msgSize > nc.info.MaxPayload { nc.mu.Unlock() return ErrMaxPayload } @@ -2664,37 +2929,65 @@ func (nc *Conn) publish(subj, reply string, data []byte) error { } } - msgh := nc.scratch[:len(_PUB_P_)] - msgh = append(msgh, subj...) - msgh = append(msgh, ' ') + var mh []byte + if hdr != nil { + mh = nc.scratch[:len(_HPUB_P_)] + } else { + mh = nc.scratch[1:len(_HPUB_P_)] + } + mh = append(mh, subj...) + mh = append(mh, ' ') if reply != "" { - msgh = append(msgh, reply...) - msgh = append(msgh, ' ') + mh = append(mh, reply...) + mh = append(mh, ' ') } // We could be smarter here, but simple loop is ok, - // just avoid strconv in fast path + // just avoid strconv in fast path. // FIXME(dlc) - Find a better way here. // msgh = strconv.AppendInt(msgh, int64(len(data)), 10) + // go 1.14 some values strconv faster, may be able to switch over. var b [12]byte var i = len(b) - if len(data) > 0 { - for l := len(data); l > 0; l /= 10 { - i -= 1 + + if hdr != nil { + if len(hdr) > 0 { + for l := len(hdr); l > 0; l /= 10 { + i-- + b[i] = digits[l%10] + } + } else { + i-- + b[i] = digits[0] + } + mh = append(mh, b[i:]...) + mh = append(mh, ' ') + // reset for below. + i = len(b) + } + + if msgSize > 0 { + for l := msgSize; l > 0; l /= 10 { + i-- b[i] = digits[l%10] } } else { - i -= 1 + i-- b[i] = digits[0] } - msgh = append(msgh, b[i:]...) - msgh = append(msgh, _CRLF_...) + mh = append(mh, b[i:]...) + mh = append(mh, _CRLF_...) - _, err := nc.bw.Write(msgh) + _, err := nc.bw.Write(mh) if err == nil { - _, err = nc.bw.Write(data) + if hdr != nil { + _, err = nc.bw.Write(hdr) + } + if err == nil { + _, err = nc.bw.Write(data) + } } if err == nil { _, err = nc.bw.WriteString(_CRLF_) @@ -2705,7 +2998,7 @@ func (nc *Conn) publish(subj, reply string, data []byte) error { } nc.OutMsgs++ - nc.OutBytes += uint64(len(data)) + nc.OutBytes += uint64(len(data) + len(hdr)) if len(nc.fch) == 0 { nc.kickFlusher() @@ -2757,7 +3050,8 @@ func (nc *Conn) respHandler(m *Msg) { } // Helper to setup and send new request style requests. Return the chan to receive the response. -func (nc *Conn) createNewRequestAndSend(subj string, data []byte) (chan *Msg, string, error) { +func (nc *Conn) createNewRequestAndSend(subj string, hdr, data []byte) (chan *Msg, string, error) { + nc.mu.Lock() // Do setup for the new style if needed. if nc.respMap == nil { nc.initNewResp() @@ -2771,7 +3065,7 @@ func (nc *Conn) createNewRequestAndSend(subj string, data []byte) (chan *Msg, st // Create the response subscription we will use for all new style responses. // This will be on an _INBOX with an additional terminal token. The subscription // will be on a wildcard. - s, err := nc.subscribeLocked(nc.respSub, _EMPTY_, nc.respHandler, nil, false) + s, err := nc.subscribeLocked(nc.respSub, _EMPTY_, nc.respHandler, nil, false, nil) if err != nil { nc.mu.Unlock() return nil, token, err @@ -2781,28 +3075,68 @@ func (nc *Conn) createNewRequestAndSend(subj string, data []byte) (chan *Msg, st } nc.mu.Unlock() - if err := nc.PublishRequest(subj, respInbox, data); err != nil { + if err := nc.publish(subj, respInbox, hdr, data); err != nil { return nil, token, err } return mch, token, nil } +// RequestMsg will send a request payload including optional headers and deliver +// the response message, or an error, including a timeout if no message was received properly. +func (nc *Conn) RequestMsg(msg *Msg, timeout time.Duration) (*Msg, error) { + var hdr []byte + var err error + + if len(msg.Header) > 0 { + if !nc.info.Headers { + return nil, ErrHeadersNotSupported + } + hdr, err = msg.headerBytes() + if err != nil { + return nil, err + } + } + + return nc.request(msg.Subject, hdr, msg.Data, timeout) +} + // Request will send a request payload and deliver the response message, // or an error, including a timeout if no message was received properly. func (nc *Conn) Request(subj string, data []byte, timeout time.Duration) (*Msg, error) { + return nc.request(subj, nil, data, timeout) +} + +func (nc *Conn) useOldRequestStyle() bool { + nc.mu.RLock() + r := nc.Opts.UseOldRequestStyle + nc.mu.RUnlock() + return r +} + +func (nc *Conn) request(subj string, hdr, data []byte, timeout time.Duration) (*Msg, error) { if nc == nil { return nil, ErrInvalidConnection } - nc.mu.Lock() - // If user wants the old style. - if nc.Opts.UseOldRequestStyle { - nc.mu.Unlock() - return nc.oldRequest(subj, data, timeout) + var m *Msg + var err error + + if nc.useOldRequestStyle() { + m, err = nc.oldRequest(subj, hdr, data, timeout) + } else { + m, err = nc.newRequest(subj, hdr, data, timeout) } - mch, token, err := nc.createNewRequestAndSend(subj, data) + // Check for no responder status. + if err == nil && len(m.Data) == 0 && m.Header.Get(statusHdr) == noResponders { + m, err = nil, ErrNoResponders + } + return m, err +} + +func (nc *Conn) newRequest(subj string, hdr, data []byte, timeout time.Duration) (*Msg, error) { + mch, token, err := nc.createNewRequestAndSend(subj, hdr, data) if err != nil { return nil, err } @@ -2831,21 +3165,22 @@ func (nc *Conn) Request(subj string, data []byte, timeout time.Duration) (*Msg, // oldRequest will create an Inbox and perform a Request() call // with the Inbox reply and return the first reply received. // This is optimized for the case of multiple responses. -func (nc *Conn) oldRequest(subj string, data []byte, timeout time.Duration) (*Msg, error) { +func (nc *Conn) oldRequest(subj string, hdr, data []byte, timeout time.Duration) (*Msg, error) { inbox := NewInbox() ch := make(chan *Msg, RequestChanLen) - s, err := nc.subscribe(inbox, _EMPTY_, nil, ch, true) + s, err := nc.subscribe(inbox, _EMPTY_, nil, ch, true, nil) if err != nil { return nil, err } s.AutoUnsubscribe(1) defer s.Unsubscribe() - err = nc.PublishRequest(subj, inbox, data) + err = nc.publish(subj, inbox, hdr, data) if err != nil { return nil, err } + return s.NextMsg(timeout) } @@ -2922,14 +3257,14 @@ func (nc *Conn) respToken(respInbox string) string { // can have wildcards (partial:*, full:>). Messages will be delivered // to the associated MsgHandler. func (nc *Conn) Subscribe(subj string, cb MsgHandler) (*Subscription, error) { - return nc.subscribe(subj, _EMPTY_, cb, nil, false) + return nc.subscribe(subj, _EMPTY_, cb, nil, false, nil) } // ChanSubscribe will express interest in the given subject and place // all messages received on the channel. // You should not close the channel until sub.Unsubscribe() has been called. func (nc *Conn) ChanSubscribe(subj string, ch chan *Msg) (*Subscription, error) { - return nc.subscribe(subj, _EMPTY_, nil, ch, false) + return nc.subscribe(subj, _EMPTY_, nil, ch, false, nil) } // ChanQueueSubscribe will express interest in the given subject. @@ -2939,7 +3274,7 @@ func (nc *Conn) ChanSubscribe(subj string, ch chan *Msg) (*Subscription, error) // You should not close the channel until sub.Unsubscribe() has been called. // Note: This is the same than QueueSubscribeSyncWithChan. func (nc *Conn) ChanQueueSubscribe(subj, group string, ch chan *Msg) (*Subscription, error) { - return nc.subscribe(subj, group, nil, ch, false) + return nc.subscribe(subj, group, nil, ch, false, nil) } // SubscribeSync will express interest on the given subject. Messages will @@ -2949,7 +3284,7 @@ func (nc *Conn) SubscribeSync(subj string) (*Subscription, error) { return nil, ErrInvalidConnection } mch := make(chan *Msg, nc.Opts.SubChanLen) - s, e := nc.subscribe(subj, _EMPTY_, nil, mch, true) + s, e := nc.subscribe(subj, _EMPTY_, nil, mch, true, nil) return s, e } @@ -2958,7 +3293,7 @@ func (nc *Conn) SubscribeSync(subj string) (*Subscription, error) { // only one member of the group will be selected to receive any given // message asynchronously. func (nc *Conn) QueueSubscribe(subj, queue string, cb MsgHandler) (*Subscription, error) { - return nc.subscribe(subj, queue, cb, nil, false) + return nc.subscribe(subj, queue, cb, nil, false, nil) } // QueueSubscribeSync creates a synchronous queue subscriber on the given @@ -2967,7 +3302,7 @@ func (nc *Conn) QueueSubscribe(subj, queue string, cb MsgHandler) (*Subscription // given message synchronously using Subscription.NextMsg(). func (nc *Conn) QueueSubscribeSync(subj, queue string) (*Subscription, error) { mch := make(chan *Msg, nc.Opts.SubChanLen) - s, e := nc.subscribe(subj, queue, nil, mch, true) + s, e := nc.subscribe(subj, queue, nil, mch, true, nil) return s, e } @@ -2978,7 +3313,7 @@ func (nc *Conn) QueueSubscribeSync(subj, queue string) (*Subscription, error) { // You should not close the channel until sub.Unsubscribe() has been called. // Note: This is the same than ChanQueueSubscribe. func (nc *Conn) QueueSubscribeSyncWithChan(subj, queue string, ch chan *Msg) (*Subscription, error) { - return nc.subscribe(subj, queue, nil, ch, false) + return nc.subscribe(subj, queue, nil, ch, false, nil) } // badSubject will do quick test on whether a subject is acceptable. @@ -3002,17 +3337,16 @@ func badQueue(qname string) bool { } // subscribe is the internal subscribe function that indicates interest in a subject. -func (nc *Conn) subscribe(subj, queue string, cb MsgHandler, ch chan *Msg, isSync bool) (*Subscription, error) { +func (nc *Conn) subscribe(subj, queue string, cb MsgHandler, ch chan *Msg, isSync bool, js *jsSub) (*Subscription, error) { if nc == nil { return nil, ErrInvalidConnection } nc.mu.Lock() - s, err := nc.subscribeLocked(subj, queue, cb, ch, isSync) - nc.mu.Unlock() - return s, err + defer nc.mu.Unlock() + return nc.subscribeLocked(subj, queue, cb, ch, isSync, js) } -func (nc *Conn) subscribeLocked(subj, queue string, cb MsgHandler, ch chan *Msg, isSync bool) (*Subscription, error) { +func (nc *Conn) subscribeLocked(subj, queue string, cb MsgHandler, ch chan *Msg, isSync bool, js *jsSub) (*Subscription, error) { if nc == nil { return nil, ErrInvalidConnection } @@ -3035,9 +3369,13 @@ func (nc *Conn) subscribeLocked(subj, queue string, cb MsgHandler, ch chan *Msg, return nil, ErrBadSubscription } - sub := &Subscription{Subject: subj, Queue: queue, mcb: cb, conn: nc} + sub := &Subscription{Subject: subj, Queue: queue, mcb: cb, conn: nc, jsi: js} // Set pending limits. - sub.pMsgsLimit = DefaultSubPendingMsgsLimit + if ch != nil { + sub.pMsgsLimit = cap(ch) + } else { + sub.pMsgsLimit = DefaultSubPendingMsgsLimit + } sub.pBytesLimit = DefaultSubPendingBytesLimit // If we have an async callback, start up a sub specific @@ -3227,6 +3565,17 @@ func (s *Subscription) AutoUnsubscribe(max int) error { // unsubscribe performs the low level unsubscribe to the server. // Use Subscription.Unsubscribe() func (nc *Conn) unsubscribe(sub *Subscription, max int, drainMode bool) error { + // Check whether it is a JetStream sub and should clean up consumers. + sub.mu.Lock() + jsi := sub.jsi + sub.mu.Unlock() + if jsi != nil { + err := jsi.unsubscribe(drainMode) + if err != nil { + return err + } + } + nc.mu.Lock() // ok here, but defer is expensive defer nc.mu.Unlock() @@ -3366,6 +3715,7 @@ func (s *Subscription) processNextMsgDelivered(msg *Msg) error { s.mu.Lock() nc := s.conn max := s.max + jsi := s.jsi // Update some stats. s.delivered++ @@ -3388,6 +3738,12 @@ func (s *Subscription) processNextMsgDelivered(msg *Msg) error { } } + // In case this is a JetStream message and in pull mode + // then check whether it is an JS API error. + if jsi != nil && jsi.pull > 0 && len(msg.Data) == 0 && msg.Header.Get(statusHdr) == noResponders { + return ErrNoResponders + } + return nil } @@ -3449,8 +3805,10 @@ func (s *Subscription) ClearMaxPending() error { // Pending Limits const ( - DefaultSubPendingMsgsLimit = 65536 - DefaultSubPendingBytesLimit = 65536 * 1024 + // DefaultSubPendingMsgsLimit will be 512k msgs. + DefaultSubPendingMsgsLimit = 512 * 1024 + // DefaultSubPendingBytesLimit is 64MB + DefaultSubPendingBytesLimit = 64 * 1024 * 1024 ) // PendingLimits returns the current limits for this subscription. @@ -3536,6 +3894,22 @@ func (m *Msg) Respond(data []byte) error { return nc.Publish(m.Reply, data) } +// RespondMsg allows a convenient way to respond to requests in service based subscriptions that might include headers +func (m *Msg) RespondMsg(msg *Msg) error { + if m == nil || m.Sub == nil { + return ErrMsgNotBound + } + if m.Reply == "" { + return ErrMsgNoReply + } + msg.Subject = m.Reply + m.Sub.mu.Lock() + nc := m.Sub.conn + m.Sub.mu.Unlock() + // No need to check the connection here since the call to publish will do all the checking. + return nc.PublishMsg(msg) +} + // FIXME: This is a hack // removeFlushEntry is needed when we need to discard queued up responses // for our pings as part of a flush call. This happens when we have a flush @@ -3865,10 +4239,16 @@ func (nc *Conn) drainConnection() { subs := make([]*Subscription, 0, len(nc.subs)) for _, s := range nc.subs { + if s == nc.respMux { + // Skip since might be in use while messages + // are being processed (can miss responses). + continue + } subs = append(subs, s) } errCB := nc.Opts.AsyncErrorCB drainWait := nc.Opts.DrainTimeout + respMux := nc.respMux nc.mu.Unlock() // for pushing errors with context. @@ -3881,7 +4261,7 @@ func (nc *Conn) drainConnection() { nc.mu.Unlock() } - // Do subs first + // Do subs first, skip request handler if present. for _, s := range subs { if err := s.Drain(); err != nil { // We will notify about these but continue. @@ -3891,13 +4271,34 @@ func (nc *Conn) drainConnection() { // Wait for the subscriptions to drop to zero. timeout := time.Now().Add(drainWait) + var min int + if respMux != nil { + min = 1 + } else { + min = 0 + } for time.Now().Before(timeout) { - if nc.NumSubscriptions() == 0 { + if nc.NumSubscriptions() == min { break } time.Sleep(10 * time.Millisecond) } + // In case there was a request/response handler + // then need to call drain at the end. + if respMux != nil { + if err := respMux.Drain(); err != nil { + // We will notify about these but continue. + pushErr(err) + } + for time.Now().Before(timeout) { + if nc.NumSubscriptions() == 0 { + break + } + time.Sleep(10 * time.Millisecond) + } + } + // Check if we timed out. if nc.NumSubscriptions() != 0 { pushErr(ErrDrainTimeout) @@ -4049,6 +4450,13 @@ func (nc *Conn) MaxPayload() int64 { return nc.info.MaxPayload } +// HeadersSupported will return if the server supports headers +func (nc *Conn) HeadersSupported() bool { + nc.mu.RLock() + defer nc.mu.RUnlock() + return nc.info.Headers +} + // AuthRequired will return if the connected server requires authorization. func (nc *Conn) AuthRequired() bool { nc.mu.RLock() @@ -4186,7 +4594,7 @@ func userFromFile(userFile string) (string, error) { return _EMPTY_, fmt.Errorf("nats: %v", err) } defer wipeSlice(contents) - return jwt.ParseDecoratedJWT(contents) + return nkeys.ParseDecoratedJWT(contents) } func homeDir() (string, error) { @@ -4235,7 +4643,7 @@ func nkeyPairFromSeedFile(seedFile string) (nkeys.KeyPair, error) { return nil, fmt.Errorf("nats: %v", err) } defer wipeSlice(contents) - return jwt.ParseDecoratedNKey(contents) + return nkeys.ParseDecoratedNKey(contents) } // Sign authentication challenges from the server. diff --git a/vendor/github.com/nats-io/nats.go/netchan.go b/vendor/github.com/nats-io/nats.go/netchan.go index fd86d065..3f2a33e6 100644 --- a/vendor/github.com/nats-io/nats.go/netchan.go +++ b/vendor/github.com/nats-io/nats.go/netchan.go @@ -107,5 +107,5 @@ func (c *EncodedConn) bindRecvChan(subject, queue string, channel interface{}) ( chVal.Send(oPtr) } - return c.Conn.subscribe(subject, queue, cb, nil, false) + return c.Conn.subscribe(subject, queue, cb, nil, false, nil) } diff --git a/vendor/github.com/nats-io/nats.go/parser.go b/vendor/github.com/nats-io/nats.go/parser.go index 630142a3..c9cbfeb6 100644 --- a/vendor/github.com/nats-io/nats.go/parser.go +++ b/vendor/github.com/nats-io/nats.go/parser.go @@ -1,4 +1,4 @@ -// Copyright 2012-2018 The NATS Authors +// Copyright 2012-2020 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -21,6 +21,7 @@ type msgArg struct { subject []byte reply []byte sid int64 + hdr int size int } @@ -30,6 +31,7 @@ type parseState struct { state int as int drop int + hdr int ma msgArg argBuf []byte msgBuf []byte @@ -54,6 +56,7 @@ const ( MSG_ARG MSG_PAYLOAD MSG_END + OP_H OP_P OP_PI OP_PIN @@ -83,6 +86,12 @@ func (nc *Conn) parse(buf []byte) error { switch b { case 'M', 'm': nc.ps.state = OP_M + nc.ps.hdr = -1 + nc.ps.ma.hdr = -1 + case 'H', 'h': + nc.ps.state = OP_H + nc.ps.hdr = 0 + nc.ps.ma.hdr = 0 case 'P', 'p': nc.ps.state = OP_P case '+': @@ -94,6 +103,13 @@ func (nc *Conn) parse(buf []byte) error { default: goto parseErr } + case OP_H: + switch b { + case 'M', 'm': + nc.ps.state = OP_M + default: + goto parseErr + } case OP_M: switch b { case 'S', 's': @@ -140,8 +156,7 @@ func (nc *Conn) parse(buf []byte) error { nc.ps.drop, nc.ps.as, nc.ps.state = 0, i+1, MSG_PAYLOAD // jump ahead with the index. If this overruns - // what is left we fall out and process split - // buffer. + // what is left we fall out and process a split buffer. i = nc.ps.as + nc.ps.ma.size - 1 default: if nc.ps.argBuf != nil { @@ -415,6 +430,11 @@ func (nc *Conn) cloneMsgArg() { const argsLenMax = 4 func (nc *Conn) processMsgArgs(arg []byte) error { + // Use separate function for header based messages. + if nc.ps.hdr >= 0 { + return nc.processHeaderMsgArgs(arg) + } + // Unroll splitArgs to avoid runtime/heap issues a := [argsLenMax][]byte{} args := a[:0] @@ -459,6 +479,57 @@ func (nc *Conn) processMsgArgs(arg []byte) error { return nil } +// processHeaderMsgArgs is for a header based message. +func (nc *Conn) processHeaderMsgArgs(arg []byte) error { + // Unroll splitArgs to avoid runtime/heap issues + a := [argsLenMax][]byte{} + args := a[:0] + start := -1 + for i, b := range arg { + switch b { + case ' ', '\t', '\r', '\n': + if start >= 0 { + args = append(args, arg[start:i]) + start = -1 + } + default: + if start < 0 { + start = i + } + } + } + if start >= 0 { + args = append(args, arg[start:]) + } + + switch len(args) { + case 4: + nc.ps.ma.subject = args[0] + nc.ps.ma.sid = parseInt64(args[1]) + nc.ps.ma.reply = nil + nc.ps.ma.hdr = int(parseInt64(args[2])) + nc.ps.ma.size = int(parseInt64(args[3])) + case 5: + nc.ps.ma.subject = args[0] + nc.ps.ma.sid = parseInt64(args[1]) + nc.ps.ma.reply = args[2] + nc.ps.ma.hdr = int(parseInt64(args[3])) + nc.ps.ma.size = int(parseInt64(args[4])) + default: + return fmt.Errorf("nats: processHeaderMsgArgs Parse Error: '%s'", arg) + } + if nc.ps.ma.sid < 0 { + return fmt.Errorf("nats: processHeaderMsgArgs Bad or Missing Sid: '%s'", arg) + } + if nc.ps.ma.hdr < 0 || nc.ps.ma.hdr > nc.ps.ma.size { + return fmt.Errorf("nats: processHeaderMsgArgs Bad or Missing Header Size: '%s'", arg) + } + if nc.ps.ma.size < 0 { + return fmt.Errorf("nats: processHeaderMsgArgs Bad or Missing Size: '%s'", arg) + } + return nil +} + // Ascii numbers 0-9 const ( ascii_0 = 48 diff --git a/vendor/github.com/nats-io/nkeys/.travis.yml b/vendor/github.com/nats-io/nkeys/.travis.yml index 7df27cbe..c13448e2 100644 --- a/vendor/github.com/nats-io/nkeys/.travis.yml +++ b/vendor/github.com/nats-io/nkeys/.travis.yml @@ -1,8 +1,12 @@ language: go sudo: false + +arch: +- amd64 +- ppc64le go: -- 1.12.x -- 1.11.x +- 1.16.x +- 1.15.x install: - go get -t ./... @@ -28,4 +32,4 @@ script: # script: curl -sL http://git.io/goreleaser | bash # on: # tags: true -# condition: $TRAVIS_OS_NAME = linux \ No newline at end of file +# condition: $TRAVIS_OS_NAME = linux diff --git a/vendor/github.com/nats-io/nkeys/MAINTAINERS.md b/vendor/github.com/nats-io/nkeys/MAINTAINERS.md index 6d0ed3e3..23214655 100644 --- a/vendor/github.com/nats-io/nkeys/MAINTAINERS.md +++ b/vendor/github.com/nats-io/nkeys/MAINTAINERS.md @@ -2,5 +2,7 @@ Maintainership is on a per project basis. -### Core-maintainers - - Derek Collison [@derekcollison](https://github.com/derekcollison) \ No newline at end of file +### Maintainers + - Derek Collison [@derekcollison](https://github.com/derekcollison) + - Ivan Kozlovic [@kozlovic](https://github.com/kozlovic) + - Waldemar Quevedo [@wallyqs](https://github.com/wallyqs) diff --git a/vendor/github.com/nats-io/nkeys/README.md b/vendor/github.com/nats-io/nkeys/README.md index 8b787cc3..13032c56 100644 --- a/vendor/github.com/nats-io/nkeys/README.md +++ b/vendor/github.com/nats-io/nkeys/README.md @@ -2,10 +2,9 @@ [![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) [![ReportCard](http://goreportcard.com/badge/nats-io/nkeys)](http://goreportcard.com/report/nats-io/nkeys) -[![Build Status](https://travis-ci.org/nats-io/nkeys.svg?branch=master)](http://travis-ci.org/nats-io/nkeys) +[![Build Status](https://travis-ci.com/nats-io/nkeys.svg?branch=master)](http://travis-ci.com/nats-io/nkeys) [![GoDoc](http://godoc.org/github.com/nats-io/nkeys?status.svg)](http://godoc.org/github.com/nats-io/nkeys) [![Coverage Status](https://coveralls.io/repos/github/nats-io/nkeys/badge.svg?branch=master&service=github)](https://coveralls.io/github/nats-io/nkeys?branch=master) -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fnats-io%2Fnkeys.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fnats-io%2Fnkeys?ref=badge_shield) A public-key signature system based on [Ed25519](https://ed25519.cr.yp.to/) for the NATS ecosystem. @@ -68,5 +67,3 @@ user2, _ := nkeys.FromRawSeed(PrefixByteUser, rawSeed) Unless otherwise noted, the NATS source files are distributed under the Apache Version 2.0 license found in the LICENSE file. - -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fnats-io%2Fnkeys.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fnats-io%2Fnkeys?ref=badge_large) diff --git a/vendor/github.com/nats-io/nkeys/creds_utils.go b/vendor/github.com/nats-io/nkeys/creds_utils.go new file mode 100644 index 00000000..e1c3d941 --- /dev/null +++ b/vendor/github.com/nats-io/nkeys/creds_utils.go @@ -0,0 +1,78 @@ +package nkeys + +import ( + "bytes" + "errors" + "regexp" +) + +var userConfigRE = regexp.MustCompile(`\s*(?:(?:[-]{3,}.*[-]{3,}\r?\n)([\w\-.=]+)(?:\r?\n[-]{3,}.*[-]{3,}\r?\n))`) + +// ParseDecoratedJWT takes a creds file and returns the JWT portion. +func ParseDecoratedJWT(contents []byte) (string, error) { + items := userConfigRE.FindAllSubmatch(contents, -1) + if len(items) == 0 { + return string(contents), nil + } + // First result should be the user JWT. + // We copy here so that if the file contained a seed file too we wipe appropriately. + raw := items[0][1] + tmp := make([]byte, len(raw)) + copy(tmp, raw) + return string(tmp), nil +} + +// ParseDecoratedNKey takes a creds file, finds the NKey portion and creates a +// key pair from it. +func ParseDecoratedNKey(contents []byte) (KeyPair, error) { + var seed []byte + + items := userConfigRE.FindAllSubmatch(contents, -1) + if len(items) > 1 { + seed = items[1][1] + } else { + lines := bytes.Split(contents, []byte("\n")) + for _, line := range lines { + if bytes.HasPrefix(bytes.TrimSpace(line), []byte("SO")) || + bytes.HasPrefix(bytes.TrimSpace(line), []byte("SA")) || + bytes.HasPrefix(bytes.TrimSpace(line), []byte("SU")) { + seed = line + break + } + } + } + if seed == nil { + return nil, errors.New("no nkey seed found") + } + if !bytes.HasPrefix(seed, []byte("SO")) && + !bytes.HasPrefix(seed, []byte("SA")) && + !bytes.HasPrefix(seed, []byte("SU")) { + return nil, errors.New("doesn't contain a seed nkey") + } + kp, err := FromSeed(seed) + if err != nil { + return nil, err + } + return kp, nil +} + +// ParseDecoratedUserNKey takes a creds file, finds the NKey portion and creates a +// key pair from it. Similar to ParseDecoratedNKey but fails for non-user keys. +func ParseDecoratedUserNKey(contents []byte) (KeyPair, error) { + nk, err := ParseDecoratedNKey(contents) + if err != nil { + return nil, err + } + seed, err := nk.Seed() + if err != nil { + return nil, err + } + if !bytes.HasPrefix(seed, []byte("SU")) { + return nil, errors.New("doesn't contain an user seed nkey") + } + kp, err := FromSeed(seed) + if err != nil { + return nil, err + } + return kp, nil +} diff --git a/vendor/github.com/nats-io/nkeys/go.mod b/vendor/github.com/nats-io/nkeys/go.mod index 6cdaeb3c..2384db3f 100644 --- a/vendor/github.com/nats-io/nkeys/go.mod +++ b/vendor/github.com/nats-io/nkeys/go.mod @@ -1,3 +1,5 @@ module github.com/nats-io/nkeys -require golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 +go 1.16 + +require golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b diff --git a/vendor/github.com/nats-io/nkeys/go.sum b/vendor/github.com/nats-io/nkeys/go.sum index 8fdcca76..42fec3ae 100644 --- a/vendor/github.com/nats-io/nkeys/go.sum +++ b/vendor/github.com/nats-io/nkeys/go.sum @@ -1,7 +1,7 @@ -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/vendor/github.com/nats-io/nkeys/main.go b/vendor/github.com/nats-io/nkeys/main.go index 5c7152dc..47d01c56 100644 --- a/vendor/github.com/nats-io/nkeys/main.go +++ b/vendor/github.com/nats-io/nkeys/main.go @@ -20,7 +20,7 @@ import ( ) // Version is our current version -const Version = "0.1.4" +const Version = "0.3.0" // Errors var ( diff --git a/vendor/golang.org/x/crypto/ed25519/ed25519.go b/vendor/golang.org/x/crypto/ed25519/ed25519.go index c7f8c7e6..71ad917d 100644 --- a/vendor/golang.org/x/crypto/ed25519/ed25519.go +++ b/vendor/golang.org/x/crypto/ed25519/ed25519.go @@ -5,6 +5,7 @@ // In Go 1.13, the ed25519 package was promoted to the standard library as // crypto/ed25519, and this package became a wrapper for the standard library one. // +//go:build !go1.13 // +build !go1.13 // Package ed25519 implements the Ed25519 signature algorithm. See diff --git a/vendor/golang.org/x/crypto/ed25519/ed25519_go113.go b/vendor/golang.org/x/crypto/ed25519/ed25519_go113.go index d1448d8d..b5974dc8 100644 --- a/vendor/golang.org/x/crypto/ed25519/ed25519_go113.go +++ b/vendor/golang.org/x/crypto/ed25519/ed25519_go113.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.13 // +build go1.13 // Package ed25519 implements the Ed25519 signature algorithm. See diff --git a/vendor/golang.org/x/net/context/go17.go b/vendor/golang.org/x/net/context/go17.go index d20f52b7..344bd143 100644 --- a/vendor/golang.org/x/net/context/go17.go +++ b/vendor/golang.org/x/net/context/go17.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.7 // +build go1.7 package context diff --git a/vendor/golang.org/x/net/context/go19.go b/vendor/golang.org/x/net/context/go19.go index d88bd1db..64d31ecc 100644 --- a/vendor/golang.org/x/net/context/go19.go +++ b/vendor/golang.org/x/net/context/go19.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.9 // +build go1.9 package context diff --git a/vendor/golang.org/x/net/context/pre_go17.go b/vendor/golang.org/x/net/context/pre_go17.go index 0f35592d..5270db5d 100644 --- a/vendor/golang.org/x/net/context/pre_go17.go +++ b/vendor/golang.org/x/net/context/pre_go17.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !go1.7 // +build !go1.7 package context diff --git a/vendor/golang.org/x/net/context/pre_go19.go b/vendor/golang.org/x/net/context/pre_go19.go index b105f80b..1f971534 100644 --- a/vendor/golang.org/x/net/context/pre_go19.go +++ b/vendor/golang.org/x/net/context/pre_go19.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !go1.9 // +build !go1.9 package context diff --git a/vendor/golang.org/x/net/http2/go111.go b/vendor/golang.org/x/net/http2/go111.go index 3a131016..5bf62b03 100644 --- a/vendor/golang.org/x/net/http2/go111.go +++ b/vendor/golang.org/x/net/http2/go111.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.11 // +build go1.11 package http2 diff --git a/vendor/golang.org/x/net/http2/not_go111.go b/vendor/golang.org/x/net/http2/not_go111.go index 161bca7c..cc0baa81 100644 --- a/vendor/golang.org/x/net/http2/not_go111.go +++ b/vendor/golang.org/x/net/http2/not_go111.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !go1.11 // +build !go1.11 package http2 diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go index 2aa859f7..e125bbd2 100644 --- a/vendor/golang.org/x/net/http2/server.go +++ b/vendor/golang.org/x/net/http2/server.go @@ -1293,7 +1293,9 @@ func (sc *serverConn) startGracefulShutdown() { sc.shutdownOnce.Do(func() { sc.sendServeMsg(gracefulShutdownMsg) }) } -// After sending GOAWAY, the connection will close after goAwayTimeout. +// After sending GOAWAY with an error code (non-graceful shutdown), the +// connection will close after goAwayTimeout. +// // If we close the connection immediately after sending GOAWAY, there may // be unsent data in our kernel receive buffer, which will cause the kernel // to send a TCP RST on close() instead of a FIN. This RST will abort the @@ -1629,23 +1631,37 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error { func (sc *serverConn) processData(f *DataFrame) error { sc.serveG.check() - if sc.inGoAway && sc.goAwayCode != ErrCodeNo { + id := f.Header().StreamID + if sc.inGoAway && (sc.goAwayCode != ErrCodeNo || id > sc.maxClientStreamID) { + // Discard all DATA frames if the GOAWAY is due to an + // error, or: + // + // Section 6.8: After sending a GOAWAY frame, the sender + // can discard frames for streams initiated by the + // receiver with identifiers higher than the identified + // last stream. return nil } - data := f.Data() - // "If a DATA frame is received whose stream is not in "open" - // or "half closed (local)" state, the recipient MUST respond - // with a stream error (Section 5.4.2) of type STREAM_CLOSED." - id := f.Header().StreamID + data := f.Data() state, st := sc.state(id) if id == 0 || state == stateIdle { + // Section 6.1: "DATA frames MUST be associated with a + // stream. If a DATA frame is received whose stream + // identifier field is 0x0, the recipient MUST respond + // with a connection error (Section 5.4.1) of type + // PROTOCOL_ERROR." + // // Section 5.1: "Receiving any frame other than HEADERS // or PRIORITY on a stream in this state MUST be // treated as a connection error (Section 5.4.1) of // type PROTOCOL_ERROR." return ConnectionError(ErrCodeProtocol) } + + // "If a DATA frame is received whose stream is not in "open" + // or "half closed (local)" state, the recipient MUST respond + // with a stream error (Section 5.4.2) of type STREAM_CLOSED." if st == nil || state != stateOpen || st.gotTrailerHeader || st.resetQueued { // This includes sending a RST_STREAM if the stream is // in stateHalfClosedLocal (which currently means that diff --git a/vendor/golang.org/x/net/idna/idna10.0.0.go b/vendor/golang.org/x/net/idna/idna10.0.0.go index a98a31f4..7e69ee1b 100644 --- a/vendor/golang.org/x/net/idna/idna10.0.0.go +++ b/vendor/golang.org/x/net/idna/idna10.0.0.go @@ -4,6 +4,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.10 // +build go1.10 // Package idna implements IDNA2008 using the compatibility processing diff --git a/vendor/golang.org/x/net/idna/idna9.0.0.go b/vendor/golang.org/x/net/idna/idna9.0.0.go index 8842146b..7c745637 100644 --- a/vendor/golang.org/x/net/idna/idna9.0.0.go +++ b/vendor/golang.org/x/net/idna/idna9.0.0.go @@ -4,6 +4,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !go1.10 // +build !go1.10 // Package idna implements IDNA2008 using the compatibility processing diff --git a/vendor/golang.org/x/net/idna/tables10.0.0.go b/vendor/golang.org/x/net/idna/tables10.0.0.go index 54fddb4b..d1d62ef4 100644 --- a/vendor/golang.org/x/net/idna/tables10.0.0.go +++ b/vendor/golang.org/x/net/idna/tables10.0.0.go @@ -1,5 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. +//go:build go1.10 && !go1.13 // +build go1.10,!go1.13 package idna diff --git a/vendor/golang.org/x/net/idna/tables11.0.0.go b/vendor/golang.org/x/net/idna/tables11.0.0.go index 8ce0811f..167efba7 100644 --- a/vendor/golang.org/x/net/idna/tables11.0.0.go +++ b/vendor/golang.org/x/net/idna/tables11.0.0.go @@ -1,5 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. +//go:build go1.13 && !go1.14 // +build go1.13,!go1.14 package idna diff --git a/vendor/golang.org/x/net/idna/tables12.0.0.go b/vendor/golang.org/x/net/idna/tables12.0.0.go index f39f0cb4..ab40f7bc 100644 --- a/vendor/golang.org/x/net/idna/tables12.0.0.go +++ b/vendor/golang.org/x/net/idna/tables12.0.0.go @@ -1,5 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. +//go:build go1.14 && !go1.16 // +build go1.14,!go1.16 package idna diff --git a/vendor/golang.org/x/net/idna/tables13.0.0.go b/vendor/golang.org/x/net/idna/tables13.0.0.go index e8c7a36d..390c5e56 100644 --- a/vendor/golang.org/x/net/idna/tables13.0.0.go +++ b/vendor/golang.org/x/net/idna/tables13.0.0.go @@ -1,5 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. +//go:build go1.16 // +build go1.16 package idna diff --git a/vendor/golang.org/x/net/idna/tables9.0.0.go b/vendor/golang.org/x/net/idna/tables9.0.0.go index 8b65fa16..4074b533 100644 --- a/vendor/golang.org/x/net/idna/tables9.0.0.go +++ b/vendor/golang.org/x/net/idna/tables9.0.0.go @@ -1,5 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. +//go:build !go1.10 // +build !go1.10 package idna diff --git a/vendor/modules.txt b/vendor/modules.txt index bbbad1ca..a39c7f68 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -112,7 +112,7 @@ github.com/jmespath/go-jmespath github.com/jstemmer/go-junit-report github.com/jstemmer/go-junit-report/formatter github.com/jstemmer/go-junit-report/parser -# github.com/klauspost/compress v1.11.0 +# github.com/klauspost/compress v1.11.12 github.com/klauspost/compress/fse github.com/klauspost/compress/huff0 github.com/klauspost/compress/snappy @@ -125,16 +125,14 @@ github.com/matttproud/golang_protobuf_extensions/pbutil # github.com/mmcloughlin/geohash v0.10.0 ## explicit github.com/mmcloughlin/geohash -# github.com/nats-io/jwt v1.1.0 -github.com/nats-io/jwt -# github.com/nats-io/nats-server/v2 v2.1.9 +# github.com/nats-io/nats-server/v2 v2.2.0 ## explicit -# github.com/nats-io/nats.go v1.10.0 +# github.com/nats-io/nats.go v1.10.1-0.20210228004050-ed743748acac ## explicit github.com/nats-io/nats.go github.com/nats-io/nats.go/encoders/builtin github.com/nats-io/nats.go/util -# github.com/nats-io/nkeys v0.1.4 +# github.com/nats-io/nkeys v0.3.0 github.com/nats-io/nkeys # github.com/nats-io/nuid v1.0.1 github.com/nats-io/nuid @@ -242,7 +240,7 @@ go.opencensus.io/trace go.opencensus.io/trace/internal go.opencensus.io/trace/propagation go.opencensus.io/trace/tracestate -# golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a +# golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b golang.org/x/crypto/ed25519 golang.org/x/crypto/ed25519/internal/edwards25519 golang.org/x/crypto/md4 @@ -253,7 +251,7 @@ golang.org/x/lint/golint # golang.org/x/mod v0.2.0 golang.org/x/mod/module golang.org/x/mod/semver -# golang.org/x/net v0.0.0-20210119194325-5f4716e94777 +# golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 ## explicit golang.org/x/net/context golang.org/x/net/context/ctxhttp