diff --git a/go.mod b/go.mod
index 438fd46067..7278839026 100644
--- a/go.mod
+++ b/go.mod
@@ -55,7 +55,7 @@ require (
github.com/mitchellh/mapstructure v1.5.0
github.com/mna/pigeon v1.3.0
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
- github.com/nats-io/nats-server/v2 v2.14.0
+ github.com/nats-io/nats-server/v2 v2.14.1
github.com/nats-io/nats.go v1.51.0
github.com/olekukonko/tablewriter v1.1.4
github.com/onsi/ginkgo v1.16.5
@@ -257,7 +257,7 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/juliangruber/go-intersect v1.1.0 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
- github.com/klauspost/compress v1.18.5 // indirect
+ github.com/klauspost/compress v1.18.6 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/klauspost/crc32 v1.3.0 // indirect
github.com/kovidgoyal/go-parallel v1.1.1 // indirect
diff --git a/go.sum b/go.sum
index 7a96ad429d..e5fe18dfbb 100644
--- a/go.sum
+++ b/go.sum
@@ -719,8 +719,8 @@ github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
-github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
+github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
+github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
@@ -900,8 +900,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
github.com/nats-io/jwt/v2 v2.8.1 h1:V0xpGuD/N8Mi+fQNDynXohVvp7ZztevW5io8CUWlPmU=
github.com/nats-io/jwt/v2 v2.8.1/go.mod h1:nWnOEEiVMiKHQpnAy4eXlizVEtSfzacZ1Q43LIRavZg=
-github.com/nats-io/nats-server/v2 v2.14.0 h1:+8q0HrDFotwLLcGH/legOEOnowunhK+aZ4GYBIWpQlM=
-github.com/nats-io/nats-server/v2 v2.14.0/go.mod h1:ImVUUDvfClJbb6cuJQRc1VmgDCXKM5ds0OoiG9MVOKo=
+github.com/nats-io/nats-server/v2 v2.14.1 h1:wXs/a5fw9Hzm3CvuzLxGeIwpjPulSa7gMT3eSuhGkcg=
+github.com/nats-io/nats-server/v2 v2.14.1/go.mod h1:4N17zLpuS7WMbG8T9gsE2B7z9hC9PraPyulVBfpK6nU=
github.com/nats-io/nats.go v1.51.0 h1:ByW84XTz6W03GSSsygsZcA+xgKK8vPGaa/FCAAEHnAI=
github.com/nats-io/nats.go v1.51.0/go.mod h1:26HypzazeOkyO3/mqd1zZd53STJN0EjCYF9Uy2ZOBno=
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
diff --git a/vendor/github.com/klauspost/compress/.gitattributes b/vendor/github.com/klauspost/compress/.gitattributes
index 402433593c..57aa6487c5 100644
--- a/vendor/github.com/klauspost/compress/.gitattributes
+++ b/vendor/github.com/klauspost/compress/.gitattributes
@@ -1,2 +1,3 @@
* -text
*.bin -text -diff
+*.md text eol=lf
diff --git a/vendor/github.com/klauspost/compress/README.md b/vendor/github.com/klauspost/compress/README.md
index e839fe9c60..fb023f2cf2 100644
--- a/vendor/github.com/klauspost/compress/README.md
+++ b/vendor/github.com/klauspost/compress/README.md
@@ -1,700 +1,700 @@
-# compress
-
-This package provides various compression algorithms.
-
-* [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression in pure Go.
-* [S2](https://github.com/klauspost/compress/tree/master/s2#s2-compression) is a high performance replacement for Snappy.
-* Optimized [deflate](https://godoc.org/github.com/klauspost/compress/flate) packages which can be used as a dropin replacement for [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip) and [zlib](https://godoc.org/github.com/klauspost/compress/zlib).
-* [snappy](https://github.com/klauspost/compress/tree/master/snappy) is a drop-in replacement for `github.com/golang/snappy` offering better compression and concurrent streams.
-* [huff0](https://github.com/klauspost/compress/tree/master/huff0) and [FSE](https://github.com/klauspost/compress/tree/master/fse) implementations for raw entropy encoding.
-* [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp) Provides client and server wrappers for handling gzipped/zstd HTTP requests efficiently.
-* [pgzip](https://github.com/klauspost/pgzip) is a separate package that provides a very fast parallel gzip implementation.
-
-[](https://pkg.go.dev/github.com/klauspost/compress?tab=subdirectories)
-[](https://github.com/klauspost/compress/actions/workflows/go.yml)
-[](https://sourcegraph.com/github.com/klauspost/compress?badge)
-
-# package usage
-
-Use `go get github.com/klauspost/compress@latest` to add it to your project.
-
-This package will support the current Go version and 2 versions back.
-
-* Use the `nounsafe` tag to disable all use of the "unsafe" package.
-* Use the `noasm` tag to disable all assembly across packages.
-
-Use the links above for more information on each.
-
-# changelog
-
-* Feb 9th, 2026 [1.18.4](https://github.com/klauspost/compress/releases/tag/v1.18.4)
- * gzhttp: Add zstandard to server handler wrapper https://github.com/klauspost/compress/pull/1121
- * zstd: Add ResetWithOptions to encoder/decoder https://github.com/klauspost/compress/pull/1122
- * gzhttp: preserve qvalue when extra parameters follow in Accept-Encoding by @analytically in https://github.com/klauspost/compress/pull/1116
-
-* Jan 16th, 2026 [1.18.3](https://github.com/klauspost/compress/releases/tag/v1.18.3)
- * Downstream CVE-2025-61728. See [golang/go#77102](https://github.com/golang/go/issues/77102).
-
-* Dec 1st, 2025 - [1.18.2](https://github.com/klauspost/compress/releases/tag/v1.18.2)
- * flate: Fix invalid encoding on level 9 with single value input in https://github.com/klauspost/compress/pull/1115
- * flate: reduce stateless allocations by @RXamzin in https://github.com/klauspost/compress/pull/1106
-
-* Oct 20, 2025 - [1.18.1](https://github.com/klauspost/compress/releases/tag/v1.18.1) - RETRACTED
- * zstd: Add simple zstd EncodeTo/DecodeTo functions https://github.com/klauspost/compress/pull/1079
- * zstd: Fix incorrect buffer size in dictionary encodes https://github.com/klauspost/compress/pull/1059
- * s2: check for cap, not len of buffer in EncodeBetter/Best by @vdarulis in https://github.com/klauspost/compress/pull/1080
- * zlib: Avoiding extra allocation in zlib.reader.Reset by @travelpolicy in https://github.com/klauspost/compress/pull/1086
- * gzhttp: remove redundant err check in zstdReader by @ryanfowler in https://github.com/klauspost/compress/pull/1090
- * flate: Faster load+store https://github.com/klauspost/compress/pull/1104
- * flate: Simplify matchlen https://github.com/klauspost/compress/pull/1101
- * flate: Use exact sizes for huffman tables https://github.com/klauspost/compress/pull/1103
-
-* Feb 19th, 2025 - [1.18.0](https://github.com/klauspost/compress/releases/tag/v1.18.0)
- * Add unsafe little endian loaders https://github.com/klauspost/compress/pull/1036
- * fix: check `r.err != nil` but return a nil value error `err` by @alingse in https://github.com/klauspost/compress/pull/1028
- * flate: Simplify L4-6 loading https://github.com/klauspost/compress/pull/1043
- * flate: Simplify matchlen (remove asm) https://github.com/klauspost/compress/pull/1045
- * s2: Improve small block compression speed w/o asm https://github.com/klauspost/compress/pull/1048
- * flate: Fix matchlen L5+L6 https://github.com/klauspost/compress/pull/1049
- * flate: Cleanup & reduce casts https://github.com/klauspost/compress/pull/1050
-
-
- See changes to v1.17.x
-
-* Oct 11th, 2024 - [1.17.11](https://github.com/klauspost/compress/releases/tag/v1.17.11)
- * zstd: Fix extra CRC written with multiple Close calls https://github.com/klauspost/compress/pull/1017
- * s2: Don't use stack for index tables https://github.com/klauspost/compress/pull/1014
- * gzhttp: No content-type on no body response code by @juliens in https://github.com/klauspost/compress/pull/1011
- * gzhttp: Do not set the content-type when response has no body by @kevinpollet in https://github.com/klauspost/compress/pull/1013
-
-* Sep 23rd, 2024 - [1.17.10](https://github.com/klauspost/compress/releases/tag/v1.17.10)
- * gzhttp: Add TransportAlwaysDecompress option. https://github.com/klauspost/compress/pull/978
- * gzhttp: Add supported decompress request body by @mirecl in https://github.com/klauspost/compress/pull/1002
- * s2: Add EncodeBuffer buffer recycling callback https://github.com/klauspost/compress/pull/982
- * zstd: Improve memory usage on small streaming encodes https://github.com/klauspost/compress/pull/1007
- * flate: read data written with partial flush by @vajexal in https://github.com/klauspost/compress/pull/996
-
-* Jun 12th, 2024 - [1.17.9](https://github.com/klauspost/compress/releases/tag/v1.17.9)
- * s2: Reduce ReadFrom temporary allocations https://github.com/klauspost/compress/pull/949
- * flate, zstd: Shave some bytes off amd64 matchLen by @greatroar in https://github.com/klauspost/compress/pull/963
- * Upgrade zip/zlib to 1.22.4 upstream https://github.com/klauspost/compress/pull/970 https://github.com/klauspost/compress/pull/971
- * zstd: BuildDict fails with RLE table https://github.com/klauspost/compress/pull/951
-
-* Apr 9th, 2024 - [1.17.8](https://github.com/klauspost/compress/releases/tag/v1.17.8)
- * zstd: Reject blocks where reserved values are not 0 https://github.com/klauspost/compress/pull/885
- * zstd: Add RLE detection+encoding https://github.com/klauspost/compress/pull/938
-
-* Feb 21st, 2024 - [1.17.7](https://github.com/klauspost/compress/releases/tag/v1.17.7)
- * s2: Add AsyncFlush method: Complete the block without flushing by @Jille in https://github.com/klauspost/compress/pull/927
- * s2: Fix literal+repeat exceeds dst crash https://github.com/klauspost/compress/pull/930
-
-* Feb 5th, 2024 - [1.17.6](https://github.com/klauspost/compress/releases/tag/v1.17.6)
- * zstd: Fix incorrect repeat coding in best mode https://github.com/klauspost/compress/pull/923
- * s2: Fix DecodeConcurrent deadlock on errors https://github.com/klauspost/compress/pull/925
-
-* Jan 26th, 2024 - [v1.17.5](https://github.com/klauspost/compress/releases/tag/v1.17.5)
- * flate: Fix reset with dictionary on custom window encodes https://github.com/klauspost/compress/pull/912
- * zstd: Add Frame header encoding and stripping https://github.com/klauspost/compress/pull/908
- * zstd: Limit better/best default window to 8MB https://github.com/klauspost/compress/pull/913
- * zstd: Speed improvements by @greatroar in https://github.com/klauspost/compress/pull/896 https://github.com/klauspost/compress/pull/910
- * s2: Fix callbacks for skippable blocks and disallow 0xfe (Padding) by @Jille in https://github.com/klauspost/compress/pull/916 https://github.com/klauspost/compress/pull/917
-https://github.com/klauspost/compress/pull/919 https://github.com/klauspost/compress/pull/918
-
-* Dec 1st, 2023 - [v1.17.4](https://github.com/klauspost/compress/releases/tag/v1.17.4)
- * huff0: Speed up symbol counting by @greatroar in https://github.com/klauspost/compress/pull/887
- * huff0: Remove byteReader by @greatroar in https://github.com/klauspost/compress/pull/886
- * gzhttp: Allow overriding decompression on transport https://github.com/klauspost/compress/pull/892
- * gzhttp: Clamp compression level https://github.com/klauspost/compress/pull/890
- * gzip: Error out if reserved bits are set https://github.com/klauspost/compress/pull/891
-
-* Nov 15th, 2023 - [v1.17.3](https://github.com/klauspost/compress/releases/tag/v1.17.3)
- * fse: Fix max header size https://github.com/klauspost/compress/pull/881
- * zstd: Improve better/best compression https://github.com/klauspost/compress/pull/877
- * gzhttp: Fix missing content type on Close https://github.com/klauspost/compress/pull/883
-
-* Oct 22nd, 2023 - [v1.17.2](https://github.com/klauspost/compress/releases/tag/v1.17.2)
- * zstd: Fix rare *CORRUPTION* output in "best" mode. See https://github.com/klauspost/compress/pull/876
-
-* Oct 14th, 2023 - [v1.17.1](https://github.com/klauspost/compress/releases/tag/v1.17.1)
- * s2: Fix S2 "best" dictionary wrong encoding https://github.com/klauspost/compress/pull/871
- * flate: Reduce allocations in decompressor and minor code improvements by @fakefloordiv in https://github.com/klauspost/compress/pull/869
- * s2: Fix EstimateBlockSize on 6&7 length input https://github.com/klauspost/compress/pull/867
-
-* Sept 19th, 2023 - [v1.17.0](https://github.com/klauspost/compress/releases/tag/v1.17.0)
- * Add experimental dictionary builder https://github.com/klauspost/compress/pull/853
- * Add xerial snappy read/writer https://github.com/klauspost/compress/pull/838
- * flate: Add limited window compression https://github.com/klauspost/compress/pull/843
- * s2: Do 2 overlapping match checks https://github.com/klauspost/compress/pull/839
- * flate: Add amd64 assembly matchlen https://github.com/klauspost/compress/pull/837
- * gzip: Copy bufio.Reader on Reset by @thatguystone in https://github.com/klauspost/compress/pull/860
-
-
-
- See changes to v1.16.x
-
-
-* July 1st, 2023 - [v1.16.7](https://github.com/klauspost/compress/releases/tag/v1.16.7)
- * zstd: Fix default level first dictionary encode https://github.com/klauspost/compress/pull/829
- * s2: add GetBufferCapacity() method by @GiedriusS in https://github.com/klauspost/compress/pull/832
-
-* June 13, 2023 - [v1.16.6](https://github.com/klauspost/compress/releases/tag/v1.16.6)
- * zstd: correctly ignore WithEncoderPadding(1) by @ianlancetaylor in https://github.com/klauspost/compress/pull/806
- * zstd: Add amd64 match length assembly https://github.com/klauspost/compress/pull/824
- * gzhttp: Handle informational headers by @rtribotte in https://github.com/klauspost/compress/pull/815
- * s2: Improve Better compression slightly https://github.com/klauspost/compress/pull/663
-
-* Apr 16, 2023 - [v1.16.5](https://github.com/klauspost/compress/releases/tag/v1.16.5)
- * zstd: readByte needs to use io.ReadFull by @jnoxon in https://github.com/klauspost/compress/pull/802
- * gzip: Fix WriterTo after initial read https://github.com/klauspost/compress/pull/804
-
-* Apr 5, 2023 - [v1.16.4](https://github.com/klauspost/compress/releases/tag/v1.16.4)
- * zstd: Improve zstd best efficiency by @greatroar and @klauspost in https://github.com/klauspost/compress/pull/784
- * zstd: Respect WithAllLitEntropyCompression https://github.com/klauspost/compress/pull/792
- * zstd: Fix amd64 not always detecting corrupt data https://github.com/klauspost/compress/pull/785
- * zstd: Various minor improvements by @greatroar in https://github.com/klauspost/compress/pull/788 https://github.com/klauspost/compress/pull/794 https://github.com/klauspost/compress/pull/795
- * s2: Fix huge block overflow https://github.com/klauspost/compress/pull/779
- * s2: Allow CustomEncoder fallback https://github.com/klauspost/compress/pull/780
- * gzhttp: Support ResponseWriter Unwrap() in gzhttp handler by @jgimenez in https://github.com/klauspost/compress/pull/799
-
-* Mar 13, 2023 - [v1.16.1](https://github.com/klauspost/compress/releases/tag/v1.16.1)
- * zstd: Speed up + improve best encoder by @greatroar in https://github.com/klauspost/compress/pull/776
- * gzhttp: Add optional [BREACH mitigation](https://github.com/klauspost/compress/tree/master/gzhttp#breach-mitigation). https://github.com/klauspost/compress/pull/762 https://github.com/klauspost/compress/pull/768 https://github.com/klauspost/compress/pull/769 https://github.com/klauspost/compress/pull/770 https://github.com/klauspost/compress/pull/767
- * s2: Add Intel LZ4s converter https://github.com/klauspost/compress/pull/766
- * zstd: Minor bug fixes https://github.com/klauspost/compress/pull/771 https://github.com/klauspost/compress/pull/772 https://github.com/klauspost/compress/pull/773
- * huff0: Speed up compress1xDo by @greatroar in https://github.com/klauspost/compress/pull/774
-
-* Feb 26, 2023 - [v1.16.0](https://github.com/klauspost/compress/releases/tag/v1.16.0)
- * s2: Add [Dictionary](https://github.com/klauspost/compress/tree/master/s2#dictionaries) support. https://github.com/klauspost/compress/pull/685
- * s2: Add Compression Size Estimate. https://github.com/klauspost/compress/pull/752
- * s2: Add support for custom stream encoder. https://github.com/klauspost/compress/pull/755
- * s2: Add LZ4 block converter. https://github.com/klauspost/compress/pull/748
- * s2: Support io.ReaderAt in ReadSeeker. https://github.com/klauspost/compress/pull/747
- * s2c/s2sx: Use concurrent decoding. https://github.com/klauspost/compress/pull/746
-
-
-
- See changes to v1.15.x
-
-* Jan 21st, 2023 (v1.15.15)
- * deflate: Improve level 7-9 https://github.com/klauspost/compress/pull/739
- * zstd: Add delta encoding support by @greatroar in https://github.com/klauspost/compress/pull/728
- * zstd: Various speed improvements by @greatroar https://github.com/klauspost/compress/pull/741 https://github.com/klauspost/compress/pull/734 https://github.com/klauspost/compress/pull/736 https://github.com/klauspost/compress/pull/744 https://github.com/klauspost/compress/pull/743 https://github.com/klauspost/compress/pull/745
- * gzhttp: Add SuffixETag() and DropETag() options to prevent ETag collisions on compressed responses by @willbicks in https://github.com/klauspost/compress/pull/740
-
-* Jan 3rd, 2023 (v1.15.14)
-
- * flate: Improve speed in big stateless blocks https://github.com/klauspost/compress/pull/718
- * zstd: Minor speed tweaks by @greatroar in https://github.com/klauspost/compress/pull/716 https://github.com/klauspost/compress/pull/720
- * export NoGzipResponseWriter for custom ResponseWriter wrappers by @harshavardhana in https://github.com/klauspost/compress/pull/722
- * s2: Add example for indexing and existing stream https://github.com/klauspost/compress/pull/723
-
-* Dec 11, 2022 (v1.15.13)
- * zstd: Add [MaxEncodedSize](https://pkg.go.dev/github.com/klauspost/compress@v1.15.13/zstd#Encoder.MaxEncodedSize) to encoder https://github.com/klauspost/compress/pull/691
- * zstd: Various tweaks and improvements https://github.com/klauspost/compress/pull/693 https://github.com/klauspost/compress/pull/695 https://github.com/klauspost/compress/pull/696 https://github.com/klauspost/compress/pull/701 https://github.com/klauspost/compress/pull/702 https://github.com/klauspost/compress/pull/703 https://github.com/klauspost/compress/pull/704 https://github.com/klauspost/compress/pull/705 https://github.com/klauspost/compress/pull/706 https://github.com/klauspost/compress/pull/707 https://github.com/klauspost/compress/pull/708
-
-* Oct 26, 2022 (v1.15.12)
-
- * zstd: Tweak decoder allocs. https://github.com/klauspost/compress/pull/680
- * gzhttp: Always delete `HeaderNoCompression` https://github.com/klauspost/compress/pull/683
-
-* Sept 26, 2022 (v1.15.11)
-
- * flate: Improve level 1-3 compression https://github.com/klauspost/compress/pull/678
- * zstd: Improve "best" compression by @nightwolfz in https://github.com/klauspost/compress/pull/677
- * zstd: Fix+reduce decompression allocations https://github.com/klauspost/compress/pull/668
- * zstd: Fix non-effective noescape tag https://github.com/klauspost/compress/pull/667
-
-* Sept 16, 2022 (v1.15.10)
-
- * zstd: Add [WithDecodeAllCapLimit](https://pkg.go.dev/github.com/klauspost/compress@v1.15.10/zstd#WithDecodeAllCapLimit) https://github.com/klauspost/compress/pull/649
- * Add Go 1.19 - deprecate Go 1.16 https://github.com/klauspost/compress/pull/651
- * flate: Improve level 5+6 compression https://github.com/klauspost/compress/pull/656
- * zstd: Improve "better" compression https://github.com/klauspost/compress/pull/657
- * s2: Improve "best" compression https://github.com/klauspost/compress/pull/658
- * s2: Improve "better" compression. https://github.com/klauspost/compress/pull/635
- * s2: Slightly faster non-assembly decompression https://github.com/klauspost/compress/pull/646
- * Use arrays for constant size copies https://github.com/klauspost/compress/pull/659
-
-* July 21, 2022 (v1.15.9)
-
- * zstd: Fix decoder crash on amd64 (no BMI) on invalid input https://github.com/klauspost/compress/pull/645
- * zstd: Disable decoder extended memory copies (amd64) due to possible crashes https://github.com/klauspost/compress/pull/644
- * zstd: Allow single segments up to "max decoded size" https://github.com/klauspost/compress/pull/643
-
-* July 13, 2022 (v1.15.8)
-
- * gzip: fix stack exhaustion bug in Reader.Read https://github.com/klauspost/compress/pull/641
- * s2: Add Index header trim/restore https://github.com/klauspost/compress/pull/638
- * zstd: Optimize seqdeq amd64 asm by @greatroar in https://github.com/klauspost/compress/pull/636
- * zstd: Improve decoder memcopy https://github.com/klauspost/compress/pull/637
- * huff0: Pass a single bitReader pointer to asm by @greatroar in https://github.com/klauspost/compress/pull/634
- * zstd: Branchless getBits for amd64 w/o BMI2 by @greatroar in https://github.com/klauspost/compress/pull/640
- * gzhttp: Remove header before writing https://github.com/klauspost/compress/pull/639
-
-* June 29, 2022 (v1.15.7)
-
- * s2: Fix absolute forward seeks https://github.com/klauspost/compress/pull/633
- * zip: Merge upstream https://github.com/klauspost/compress/pull/631
- * zip: Re-add zip64 fix https://github.com/klauspost/compress/pull/624
- * zstd: translate fseDecoder.buildDtable into asm by @WojciechMula in https://github.com/klauspost/compress/pull/598
- * flate: Faster histograms https://github.com/klauspost/compress/pull/620
- * deflate: Use compound hcode https://github.com/klauspost/compress/pull/622
-
-* June 3, 2022 (v1.15.6)
- * s2: Improve coding for long, close matches https://github.com/klauspost/compress/pull/613
- * s2c: Add Snappy/S2 stream recompression https://github.com/klauspost/compress/pull/611
- * zstd: Always use configured block size https://github.com/klauspost/compress/pull/605
- * zstd: Fix incorrect hash table placement for dict encoding in default https://github.com/klauspost/compress/pull/606
- * zstd: Apply default config to ZipDecompressor without options https://github.com/klauspost/compress/pull/608
- * gzhttp: Exclude more common archive formats https://github.com/klauspost/compress/pull/612
- * s2: Add ReaderIgnoreCRC https://github.com/klauspost/compress/pull/609
- * s2: Remove sanity load on index creation https://github.com/klauspost/compress/pull/607
- * snappy: Use dedicated function for scoring https://github.com/klauspost/compress/pull/614
- * s2c+s2d: Use official snappy framed extension https://github.com/klauspost/compress/pull/610
-
-* May 25, 2022 (v1.15.5)
- * s2: Add concurrent stream decompression https://github.com/klauspost/compress/pull/602
- * s2: Fix final emit oob read crash on amd64 https://github.com/klauspost/compress/pull/601
- * huff0: asm implementation of Decompress1X by @WojciechMula https://github.com/klauspost/compress/pull/596
- * zstd: Use 1 less goroutine for stream decoding https://github.com/klauspost/compress/pull/588
- * zstd: Copy literal in 16 byte blocks when possible https://github.com/klauspost/compress/pull/592
- * zstd: Speed up when WithDecoderLowmem(false) https://github.com/klauspost/compress/pull/599
- * zstd: faster next state update in BMI2 version of decode by @WojciechMula in https://github.com/klauspost/compress/pull/593
- * huff0: Do not check max size when reading table. https://github.com/klauspost/compress/pull/586
- * flate: Inplace hashing for level 7-9 https://github.com/klauspost/compress/pull/590
-
-
-* May 11, 2022 (v1.15.4)
- * huff0: decompress directly into output by @WojciechMula in [#577](https://github.com/klauspost/compress/pull/577)
- * inflate: Keep dict on stack [#581](https://github.com/klauspost/compress/pull/581)
- * zstd: Faster decoding memcopy in asm [#583](https://github.com/klauspost/compress/pull/583)
- * zstd: Fix ignored crc [#580](https://github.com/klauspost/compress/pull/580)
-
-* May 5, 2022 (v1.15.3)
- * zstd: Allow to ignore checksum checking by @WojciechMula [#572](https://github.com/klauspost/compress/pull/572)
- * s2: Fix incorrect seek for io.SeekEnd in [#575](https://github.com/klauspost/compress/pull/575)
-
-* Apr 26, 2022 (v1.15.2)
- * zstd: Add x86-64 assembly for decompression on streams and blocks. Contributed by [@WojciechMula](https://github.com/WojciechMula). Typically 2x faster. [#528](https://github.com/klauspost/compress/pull/528) [#531](https://github.com/klauspost/compress/pull/531) [#545](https://github.com/klauspost/compress/pull/545) [#537](https://github.com/klauspost/compress/pull/537)
- * zstd: Add options to ZipDecompressor and fixes [#539](https://github.com/klauspost/compress/pull/539)
- * s2: Use sorted search for index [#555](https://github.com/klauspost/compress/pull/555)
- * Minimum version is Go 1.16, added CI test on 1.18.
-
-* Mar 11, 2022 (v1.15.1)
- * huff0: Add x86 assembly of Decode4X by @WojciechMula in [#512](https://github.com/klauspost/compress/pull/512)
- * zstd: Reuse zip decoders in [#514](https://github.com/klauspost/compress/pull/514)
- * zstd: Detect extra block data and report as corrupted in [#520](https://github.com/klauspost/compress/pull/520)
- * zstd: Handle zero sized frame content size stricter in [#521](https://github.com/klauspost/compress/pull/521)
- * zstd: Add stricter block size checks in [#523](https://github.com/klauspost/compress/pull/523)
-
-* Mar 3, 2022 (v1.15.0)
- * zstd: Refactor decoder [#498](https://github.com/klauspost/compress/pull/498)
- * zstd: Add stream encoding without goroutines [#505](https://github.com/klauspost/compress/pull/505)
- * huff0: Prevent single blocks exceeding 16 bits by @klauspost in[#507](https://github.com/klauspost/compress/pull/507)
- * flate: Inline literal emission [#509](https://github.com/klauspost/compress/pull/509)
- * gzhttp: Add zstd to transport [#400](https://github.com/klauspost/compress/pull/400)
- * gzhttp: Make content-type optional [#510](https://github.com/klauspost/compress/pull/510)
-
-Both compression and decompression now supports "synchronous" stream operations. This means that whenever "concurrency" is set to 1, they will operate without spawning goroutines.
-
-Stream decompression is now faster on asynchronous, since the goroutine allocation much more effectively splits the workload. On typical streams this will typically use 2 cores fully for decompression. When a stream has finished decoding no goroutines will be left over, so decoders can now safely be pooled and still be garbage collected.
-
-While the release has been extensively tested, it is recommended to testing when upgrading.
-
-
-
-
- See changes to v1.14.x
-
-* Feb 22, 2022 (v1.14.4)
- * flate: Fix rare huffman only (-2) corruption. [#503](https://github.com/klauspost/compress/pull/503)
- * zip: Update deprecated CreateHeaderRaw to correctly call CreateRaw by @saracen in [#502](https://github.com/klauspost/compress/pull/502)
- * zip: don't read data descriptor early by @saracen in [#501](https://github.com/klauspost/compress/pull/501) #501
- * huff0: Use static decompression buffer up to 30% faster [#499](https://github.com/klauspost/compress/pull/499) [#500](https://github.com/klauspost/compress/pull/500)
-
-* Feb 17, 2022 (v1.14.3)
- * flate: Improve fastest levels compression speed ~10% more throughput. [#482](https://github.com/klauspost/compress/pull/482) [#489](https://github.com/klauspost/compress/pull/489) [#490](https://github.com/klauspost/compress/pull/490) [#491](https://github.com/klauspost/compress/pull/491) [#494](https://github.com/klauspost/compress/pull/494) [#478](https://github.com/klauspost/compress/pull/478)
- * flate: Faster decompression speed, ~5-10%. [#483](https://github.com/klauspost/compress/pull/483)
- * s2: Faster compression with Go v1.18 and amd64 microarch level 3+. [#484](https://github.com/klauspost/compress/pull/484) [#486](https://github.com/klauspost/compress/pull/486)
-
-* Jan 25, 2022 (v1.14.2)
- * zstd: improve header decoder by @dsnet [#476](https://github.com/klauspost/compress/pull/476)
- * zstd: Add bigger default blocks [#469](https://github.com/klauspost/compress/pull/469)
- * zstd: Remove unused decompression buffer [#470](https://github.com/klauspost/compress/pull/470)
- * zstd: Fix logically dead code by @ningmingxiao [#472](https://github.com/klauspost/compress/pull/472)
- * flate: Improve level 7-9 [#471](https://github.com/klauspost/compress/pull/471) [#473](https://github.com/klauspost/compress/pull/473)
- * zstd: Add noasm tag for xxhash [#475](https://github.com/klauspost/compress/pull/475)
-
-* Jan 11, 2022 (v1.14.1)
- * s2: Add stream index in [#462](https://github.com/klauspost/compress/pull/462)
- * flate: Speed and efficiency improvements in [#439](https://github.com/klauspost/compress/pull/439) [#461](https://github.com/klauspost/compress/pull/461) [#455](https://github.com/klauspost/compress/pull/455) [#452](https://github.com/klauspost/compress/pull/452) [#458](https://github.com/klauspost/compress/pull/458)
- * zstd: Performance improvement in [#420]( https://github.com/klauspost/compress/pull/420) [#456](https://github.com/klauspost/compress/pull/456) [#437](https://github.com/klauspost/compress/pull/437) [#467](https://github.com/klauspost/compress/pull/467) [#468](https://github.com/klauspost/compress/pull/468)
- * zstd: add arm64 xxhash assembly in [#464](https://github.com/klauspost/compress/pull/464)
- * Add garbled for binaries for s2 in [#445](https://github.com/klauspost/compress/pull/445)
-
-
-
- See changes to v1.13.x
-
-* Aug 30, 2021 (v1.13.5)
- * gz/zlib/flate: Alias stdlib errors [#425](https://github.com/klauspost/compress/pull/425)
- * s2: Add block support to commandline tools [#413](https://github.com/klauspost/compress/pull/413)
- * zstd: pooledZipWriter should return Writers to the same pool [#426](https://github.com/klauspost/compress/pull/426)
- * Removed golang/snappy as external dependency for tests [#421](https://github.com/klauspost/compress/pull/421)
-
-* Aug 12, 2021 (v1.13.4)
- * Add [snappy replacement package](https://github.com/klauspost/compress/tree/master/snappy).
- * zstd: Fix incorrect encoding in "best" mode [#415](https://github.com/klauspost/compress/pull/415)
-
-* Aug 3, 2021 (v1.13.3)
- * zstd: Improve Best compression [#404](https://github.com/klauspost/compress/pull/404)
- * zstd: Fix WriteTo error forwarding [#411](https://github.com/klauspost/compress/pull/411)
- * gzhttp: Return http.HandlerFunc instead of http.Handler. Unlikely breaking change. [#406](https://github.com/klauspost/compress/pull/406)
- * s2sx: Fix max size error [#399](https://github.com/klauspost/compress/pull/399)
- * zstd: Add optional stream content size on reset [#401](https://github.com/klauspost/compress/pull/401)
- * zstd: use SpeedBestCompression for level >= 10 [#410](https://github.com/klauspost/compress/pull/410)
-
-* Jun 14, 2021 (v1.13.1)
- * s2: Add full Snappy output support [#396](https://github.com/klauspost/compress/pull/396)
- * zstd: Add configurable [Decoder window](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithDecoderMaxWindow) size [#394](https://github.com/klauspost/compress/pull/394)
- * gzhttp: Add header to skip compression [#389](https://github.com/klauspost/compress/pull/389)
- * s2: Improve speed with bigger output margin [#395](https://github.com/klauspost/compress/pull/395)
-
-* Jun 3, 2021 (v1.13.0)
- * Added [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp#gzip-handler) which allows wrapping HTTP servers and clients with GZIP compressors.
- * zstd: Detect short invalid signatures [#382](https://github.com/klauspost/compress/pull/382)
- * zstd: Spawn decoder goroutine only if needed. [#380](https://github.com/klauspost/compress/pull/380)
-
-
-
-
- See changes to v1.12.x
-
-* May 25, 2021 (v1.12.3)
- * deflate: Better/faster Huffman encoding [#374](https://github.com/klauspost/compress/pull/374)
- * deflate: Allocate less for history. [#375](https://github.com/klauspost/compress/pull/375)
- * zstd: Forward read errors [#373](https://github.com/klauspost/compress/pull/373)
-
-* Apr 27, 2021 (v1.12.2)
- * zstd: Improve better/best compression [#360](https://github.com/klauspost/compress/pull/360) [#364](https://github.com/klauspost/compress/pull/364) [#365](https://github.com/klauspost/compress/pull/365)
- * zstd: Add helpers to compress/decompress zstd inside zip files [#363](https://github.com/klauspost/compress/pull/363)
- * deflate: Improve level 5+6 compression [#367](https://github.com/klauspost/compress/pull/367)
- * s2: Improve better/best compression [#358](https://github.com/klauspost/compress/pull/358) [#359](https://github.com/klauspost/compress/pull/358)
- * s2: Load after checking src limit on amd64. [#362](https://github.com/klauspost/compress/pull/362)
- * s2sx: Limit max executable size [#368](https://github.com/klauspost/compress/pull/368)
-
-* Apr 14, 2021 (v1.12.1)
- * snappy package removed. Upstream added as dependency.
- * s2: Better compression in "best" mode [#353](https://github.com/klauspost/compress/pull/353)
- * s2sx: Add stdin input and detect pre-compressed from signature [#352](https://github.com/klauspost/compress/pull/352)
- * s2c/s2d: Add http as possible input [#348](https://github.com/klauspost/compress/pull/348)
- * s2c/s2d/s2sx: Always truncate when writing files [#352](https://github.com/klauspost/compress/pull/352)
- * zstd: Reduce memory usage further when using [WithLowerEncoderMem](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithLowerEncoderMem) [#346](https://github.com/klauspost/compress/pull/346)
- * s2: Fix potential problem with amd64 assembly and profilers [#349](https://github.com/klauspost/compress/pull/349)
-
-
-
- See changes to v1.11.x
-
-* Mar 26, 2021 (v1.11.13)
- * zstd: Big speedup on small dictionary encodes [#344](https://github.com/klauspost/compress/pull/344) [#345](https://github.com/klauspost/compress/pull/345)
- * zstd: Add [WithLowerEncoderMem](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithLowerEncoderMem) encoder option [#336](https://github.com/klauspost/compress/pull/336)
- * deflate: Improve entropy compression [#338](https://github.com/klauspost/compress/pull/338)
- * s2: Clean up and minor performance improvement in best [#341](https://github.com/klauspost/compress/pull/341)
-
-* Mar 5, 2021 (v1.11.12)
- * s2: Add `s2sx` binary that creates [self extracting archives](https://github.com/klauspost/compress/tree/master/s2#s2sx-self-extracting-archives).
- * s2: Speed up decompression on non-assembly platforms [#328](https://github.com/klauspost/compress/pull/328)
-
-* Mar 1, 2021 (v1.11.9)
- * s2: Add ARM64 decompression assembly. Around 2x output speed. [#324](https://github.com/klauspost/compress/pull/324)
- * s2: Improve "better" speed and efficiency. [#325](https://github.com/klauspost/compress/pull/325)
- * s2: Fix binaries.
-
-* Feb 25, 2021 (v1.11.8)
- * s2: Fixed occasional out-of-bounds write on amd64. Upgrade recommended.
- * s2: Add AMD64 assembly for better mode. 25-50% faster. [#315](https://github.com/klauspost/compress/pull/315)
- * s2: Less upfront decoder allocation. [#322](https://github.com/klauspost/compress/pull/322)
- * zstd: Faster "compression" of incompressible data. [#314](https://github.com/klauspost/compress/pull/314)
- * zip: Fix zip64 headers. [#313](https://github.com/klauspost/compress/pull/313)
-
-* Jan 14, 2021 (v1.11.7)
- * Use Bytes() interface to get bytes across packages. [#309](https://github.com/klauspost/compress/pull/309)
- * s2: Add 'best' compression option. [#310](https://github.com/klauspost/compress/pull/310)
- * s2: Add ReaderMaxBlockSize, changes `s2.NewReader` signature to include varargs. [#311](https://github.com/klauspost/compress/pull/311)
- * s2: Fix crash on small better buffers. [#308](https://github.com/klauspost/compress/pull/308)
- * s2: Clean up decoder. [#312](https://github.com/klauspost/compress/pull/312)
-
-* Jan 7, 2021 (v1.11.6)
- * zstd: Make decoder allocations smaller [#306](https://github.com/klauspost/compress/pull/306)
- * zstd: Free Decoder resources when Reset is called with a nil io.Reader [#305](https://github.com/klauspost/compress/pull/305)
-
-* Dec 20, 2020 (v1.11.4)
- * zstd: Add Best compression mode [#304](https://github.com/klauspost/compress/pull/304)
- * Add header decoder [#299](https://github.com/klauspost/compress/pull/299)
- * s2: Add uncompressed stream option [#297](https://github.com/klauspost/compress/pull/297)
- * Simplify/speed up small blocks with known max size. [#300](https://github.com/klauspost/compress/pull/300)
- * zstd: Always reset literal dict encoder [#303](https://github.com/klauspost/compress/pull/303)
-
-* Nov 15, 2020 (v1.11.3)
- * inflate: 10-15% faster decompression [#293](https://github.com/klauspost/compress/pull/293)
- * zstd: Tweak DecodeAll default allocation [#295](https://github.com/klauspost/compress/pull/295)
-
-* Oct 11, 2020 (v1.11.2)
- * s2: Fix out of bounds read in "better" block compression [#291](https://github.com/klauspost/compress/pull/291)
-
-* Oct 1, 2020 (v1.11.1)
- * zstd: Set allLitEntropy true in default configuration [#286](https://github.com/klauspost/compress/pull/286)
-
-* Sept 8, 2020 (v1.11.0)
- * zstd: Add experimental compression [dictionaries](https://github.com/klauspost/compress/tree/master/zstd#dictionaries) [#281](https://github.com/klauspost/compress/pull/281)
- * zstd: Fix mixed Write and ReadFrom calls [#282](https://github.com/klauspost/compress/pull/282)
- * inflate/gz: Limit variable shifts, ~5% faster decompression [#274](https://github.com/klauspost/compress/pull/274)
-
-
-
- See changes to v1.10.x
-
-* July 8, 2020 (v1.10.11)
- * zstd: Fix extra block when compressing with ReadFrom. [#278](https://github.com/klauspost/compress/pull/278)
- * huff0: Also populate compression table when reading decoding table. [#275](https://github.com/klauspost/compress/pull/275)
-
-* June 23, 2020 (v1.10.10)
- * zstd: Skip entropy compression in fastest mode when no matches. [#270](https://github.com/klauspost/compress/pull/270)
-
-* June 16, 2020 (v1.10.9):
- * zstd: API change for specifying dictionaries. See [#268](https://github.com/klauspost/compress/pull/268)
- * zip: update CreateHeaderRaw to handle zip64 fields. [#266](https://github.com/klauspost/compress/pull/266)
- * Fuzzit tests removed. The service has been purchased and is no longer available.
-
-* June 5, 2020 (v1.10.8):
- * 1.15x faster zstd block decompression. [#265](https://github.com/klauspost/compress/pull/265)
-
-* June 1, 2020 (v1.10.7):
- * Added zstd decompression [dictionary support](https://github.com/klauspost/compress/tree/master/zstd#dictionaries)
- * Increase zstd decompression speed up to 1.19x. [#259](https://github.com/klauspost/compress/pull/259)
- * Remove internal reset call in zstd compression and reduce allocations. [#263](https://github.com/klauspost/compress/pull/263)
-
-* May 21, 2020: (v1.10.6)
- * zstd: Reduce allocations while decoding. [#258](https://github.com/klauspost/compress/pull/258), [#252](https://github.com/klauspost/compress/pull/252)
- * zstd: Stricter decompression checks.
-
-* April 12, 2020: (v1.10.5)
- * s2-commands: Flush output when receiving SIGINT. [#239](https://github.com/klauspost/compress/pull/239)
-
-* Apr 8, 2020: (v1.10.4)
- * zstd: Minor/special case optimizations. [#251](https://github.com/klauspost/compress/pull/251), [#250](https://github.com/klauspost/compress/pull/250), [#249](https://github.com/klauspost/compress/pull/249), [#247](https://github.com/klauspost/compress/pull/247)
-* Mar 11, 2020: (v1.10.3)
- * s2: Use S2 encoder in pure Go mode for Snappy output as well. [#245](https://github.com/klauspost/compress/pull/245)
- * s2: Fix pure Go block encoder. [#244](https://github.com/klauspost/compress/pull/244)
- * zstd: Added "better compression" mode. [#240](https://github.com/klauspost/compress/pull/240)
- * zstd: Improve speed of fastest compression mode by 5-10% [#241](https://github.com/klauspost/compress/pull/241)
- * zstd: Skip creating encoders when not needed. [#238](https://github.com/klauspost/compress/pull/238)
-
-* Feb 27, 2020: (v1.10.2)
- * Close to 50% speedup in inflate (gzip/zip decompression). [#236](https://github.com/klauspost/compress/pull/236) [#234](https://github.com/klauspost/compress/pull/234) [#232](https://github.com/klauspost/compress/pull/232)
- * Reduce deflate level 1-6 memory usage up to 59%. [#227](https://github.com/klauspost/compress/pull/227)
-
-* Feb 18, 2020: (v1.10.1)
- * Fix zstd crash when resetting multiple times without sending data. [#226](https://github.com/klauspost/compress/pull/226)
- * deflate: Fix dictionary use on level 1-6. [#224](https://github.com/klauspost/compress/pull/224)
- * Remove deflate writer reference when closing. [#224](https://github.com/klauspost/compress/pull/224)
-
-* Feb 4, 2020: (v1.10.0)
- * Add optional dictionary to [stateless deflate](https://pkg.go.dev/github.com/klauspost/compress/flate?tab=doc#StatelessDeflate). Breaking change, send `nil` for previous behaviour. [#216](https://github.com/klauspost/compress/pull/216)
- * Fix buffer overflow on repeated small block deflate. [#218](https://github.com/klauspost/compress/pull/218)
- * Allow copying content from an existing ZIP file without decompressing+compressing. [#214](https://github.com/klauspost/compress/pull/214)
- * Added [S2](https://github.com/klauspost/compress/tree/master/s2#s2-compression) AMD64 assembler and various optimizations. Stream speed >10GB/s. [#186](https://github.com/klauspost/compress/pull/186)
-
-
-
-
- See changes prior to v1.10.0
-
-* Jan 20,2020 (v1.9.8) Optimize gzip/deflate with better size estimates and faster table generation. [#207](https://github.com/klauspost/compress/pull/207) by [luyu6056](https://github.com/luyu6056), [#206](https://github.com/klauspost/compress/pull/206).
-* Jan 11, 2020: S2 Encode/Decode will use provided buffer if capacity is big enough. [#204](https://github.com/klauspost/compress/pull/204)
-* Jan 5, 2020: (v1.9.7) Fix another zstd regression in v1.9.5 - v1.9.6 removed.
-* Jan 4, 2020: (v1.9.6) Regression in v1.9.5 fixed causing corrupt zstd encodes in rare cases.
-* Jan 4, 2020: Faster IO in [s2c + s2d commandline tools](https://github.com/klauspost/compress/tree/master/s2#commandline-tools) compression/decompression. [#192](https://github.com/klauspost/compress/pull/192)
-* Dec 29, 2019: Removed v1.9.5 since fuzz tests showed a compatibility problem with the reference zstandard decoder.
-* Dec 29, 2019: (v1.9.5) zstd: 10-20% faster block compression. [#199](https://github.com/klauspost/compress/pull/199)
-* Dec 29, 2019: [zip](https://godoc.org/github.com/klauspost/compress/zip) package updated with latest Go features
-* Dec 29, 2019: zstd: Single segment flag condintions tweaked. [#197](https://github.com/klauspost/compress/pull/197)
-* Dec 18, 2019: s2: Faster compression when ReadFrom is used. [#198](https://github.com/klauspost/compress/pull/198)
-* Dec 10, 2019: s2: Fix repeat length output when just above at 16MB limit.
-* Dec 10, 2019: zstd: Add function to get decoder as io.ReadCloser. [#191](https://github.com/klauspost/compress/pull/191)
-* Dec 3, 2019: (v1.9.4) S2: limit max repeat length. [#188](https://github.com/klauspost/compress/pull/188)
-* Dec 3, 2019: Add [WithNoEntropyCompression](https://godoc.org/github.com/klauspost/compress/zstd#WithNoEntropyCompression) to zstd [#187](https://github.com/klauspost/compress/pull/187)
-* Dec 3, 2019: Reduce memory use for tests. Check for leaked goroutines.
-* Nov 28, 2019 (v1.9.3) Less allocations in stateless deflate.
-* Nov 28, 2019: 5-20% Faster huff0 decode. Impacts zstd as well. [#184](https://github.com/klauspost/compress/pull/184)
-* Nov 12, 2019 (v1.9.2) Added [Stateless Compression](#stateless-compression) for gzip/deflate.
-* Nov 12, 2019: Fixed zstd decompression of large single blocks. [#180](https://github.com/klauspost/compress/pull/180)
-* Nov 11, 2019: Set default [s2c](https://github.com/klauspost/compress/tree/master/s2#commandline-tools) block size to 4MB.
-* Nov 11, 2019: Reduce inflate memory use by 1KB.
-* Nov 10, 2019: Less allocations in deflate bit writer.
-* Nov 10, 2019: Fix inconsistent error returned by zstd decoder.
-* Oct 28, 2019 (v1.9.1) ztsd: Fix crash when compressing blocks. [#174](https://github.com/klauspost/compress/pull/174)
-* Oct 24, 2019 (v1.9.0) zstd: Fix rare data corruption [#173](https://github.com/klauspost/compress/pull/173)
-* Oct 24, 2019 zstd: Fix huff0 out of buffer write [#171](https://github.com/klauspost/compress/pull/171) and always return errors [#172](https://github.com/klauspost/compress/pull/172)
-* Oct 10, 2019: Big deflate rewrite, 30-40% faster with better compression [#105](https://github.com/klauspost/compress/pull/105)
-
-
-
-
- See changes prior to v1.9.0
-
-* Oct 10, 2019: (v1.8.6) zstd: Allow partial reads to get flushed data. [#169](https://github.com/klauspost/compress/pull/169)
-* Oct 3, 2019: Fix inconsistent results on broken zstd streams.
-* Sep 25, 2019: Added `-rm` (remove source files) and `-q` (no output except errors) to `s2c` and `s2d` [commands](https://github.com/klauspost/compress/tree/master/s2#commandline-tools)
-* Sep 16, 2019: (v1.8.4) Add `s2c` and `s2d` [commandline tools](https://github.com/klauspost/compress/tree/master/s2#commandline-tools).
-* Sep 10, 2019: (v1.8.3) Fix s2 decoder [Skip](https://godoc.org/github.com/klauspost/compress/s2#Reader.Skip).
-* Sep 7, 2019: zstd: Added [WithWindowSize](https://godoc.org/github.com/klauspost/compress/zstd#WithWindowSize), contributed by [ianwilkes](https://github.com/ianwilkes).
-* Sep 5, 2019: (v1.8.2) Add [WithZeroFrames](https://godoc.org/github.com/klauspost/compress/zstd#WithZeroFrames) which adds full zero payload block encoding option.
-* Sep 5, 2019: Lazy initialization of zstandard predefined en/decoder tables.
-* Aug 26, 2019: (v1.8.1) S2: 1-2% compression increase in "better" compression mode.
-* Aug 26, 2019: zstd: Check maximum size of Huffman 1X compressed literals while decoding.
-* Aug 24, 2019: (v1.8.0) Added [S2 compression](https://github.com/klauspost/compress/tree/master/s2#s2-compression), a high performance replacement for Snappy.
-* Aug 21, 2019: (v1.7.6) Fixed minor issues found by fuzzer. One could lead to zstd not decompressing.
-* Aug 18, 2019: Add [fuzzit](https://fuzzit.dev/) continuous fuzzing.
-* Aug 14, 2019: zstd: Skip incompressible data 2x faster. [#147](https://github.com/klauspost/compress/pull/147)
-* Aug 4, 2019 (v1.7.5): Better literal compression. [#146](https://github.com/klauspost/compress/pull/146)
-* Aug 4, 2019: Faster zstd compression. [#143](https://github.com/klauspost/compress/pull/143) [#144](https://github.com/klauspost/compress/pull/144)
-* Aug 4, 2019: Faster zstd decompression. [#145](https://github.com/klauspost/compress/pull/145) [#143](https://github.com/klauspost/compress/pull/143) [#142](https://github.com/klauspost/compress/pull/142)
-* July 15, 2019 (v1.7.4): Fix double EOF block in rare cases on zstd encoder.
-* July 15, 2019 (v1.7.3): Minor speedup/compression increase in default zstd encoder.
-* July 14, 2019: zstd decoder: Fix decompression error on multiple uses with mixed content.
-* July 7, 2019 (v1.7.2): Snappy update, zstd decoder potential race fix.
-* June 17, 2019: zstd decompression bugfix.
-* June 17, 2019: fix 32 bit builds.
-* June 17, 2019: Easier use in modules (less dependencies).
-* June 9, 2019: New stronger "default" [zstd](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression mode. Matches zstd default compression ratio.
-* June 5, 2019: 20-40% throughput in [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and better compression.
-* June 5, 2019: deflate/gzip compression: Reduce memory usage of lower compression levels.
-* June 2, 2019: Added [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression!
-* May 25, 2019: deflate/gzip: 10% faster bit writer, mostly visible in lower levels.
-* Apr 22, 2019: [zstd](https://github.com/klauspost/compress/tree/master/zstd#zstd) decompression added.
-* Aug 1, 2018: Added [huff0 README](https://github.com/klauspost/compress/tree/master/huff0#huff0-entropy-compression).
-* Jul 8, 2018: Added [Performance Update 2018](#performance-update-2018) below.
-* Jun 23, 2018: Merged [Go 1.11 inflate optimizations](https://go-review.googlesource.com/c/go/+/102235). Go 1.9 is now required. Backwards compatible version tagged with [v1.3.0](https://github.com/klauspost/compress/releases/tag/v1.3.0).
-* Apr 2, 2018: Added [huff0](https://godoc.org/github.com/klauspost/compress/huff0) en/decoder. Experimental for now, API may change.
-* Mar 4, 2018: Added [FSE Entropy](https://godoc.org/github.com/klauspost/compress/fse) en/decoder. Experimental for now, API may change.
-* Nov 3, 2017: Add compression [Estimate](https://godoc.org/github.com/klauspost/compress#Estimate) function.
-* May 28, 2017: Reduce allocations when resetting decoder.
-* Apr 02, 2017: Change back to official crc32, since changes were merged in Go 1.7.
-* Jan 14, 2017: Reduce stack pressure due to array copies. See [Issue #18625](https://github.com/golang/go/issues/18625).
-* Oct 25, 2016: Level 2-4 have been rewritten and now offers significantly better performance than before.
-* Oct 20, 2016: Port zlib changes from Go 1.7 to fix zlib writer issue. Please update.
-* Oct 16, 2016: Go 1.7 changes merged. Apples to apples this package is a few percent faster, but has a significantly better balance between speed and compression per level.
-* Mar 24, 2016: Always attempt Huffman encoding on level 4-7. This improves base 64 encoded data compression.
-* Mar 24, 2016: Small speedup for level 1-3.
-* Feb 19, 2016: Faster bit writer, level -2 is 15% faster, level 1 is 4% faster.
-* Feb 19, 2016: Handle small payloads faster in level 1-3.
-* Feb 19, 2016: Added faster level 2 + 3 compression modes.
-* Feb 19, 2016: [Rebalanced compression levels](https://blog.klauspost.com/rebalancing-deflate-compression-levels/), so there is a more even progression in terms of compression. New default level is 5.
-* Feb 14, 2016: Snappy: Merge upstream changes.
-* Feb 14, 2016: Snappy: Fix aggressive skipping.
-* Feb 14, 2016: Snappy: Update benchmark.
-* Feb 13, 2016: Deflate: Fixed assembler problem that could lead to sub-optimal compression.
-* Feb 12, 2016: Snappy: Added AMD64 SSE 4.2 optimizations to matching, which makes easy to compress material run faster. Typical speedup is around 25%.
-* Feb 9, 2016: Added Snappy package fork. This version is 5-7% faster, much more on hard to compress content.
-* Jan 30, 2016: Optimize level 1 to 3 by not considering static dictionary or storing uncompressed. ~4-5% speedup.
-* Jan 16, 2016: Optimization on deflate level 1,2,3 compression.
-* Jan 8 2016: Merge [CL 18317](https://go-review.googlesource.com/#/c/18317): fix reading, writing of zip64 archives.
-* Dec 8 2015: Make level 1 and -2 deterministic even if write size differs.
-* Dec 8 2015: Split encoding functions, so hashing and matching can potentially be inlined. 1-3% faster on AMD64. 5% faster on other platforms.
-* Dec 8 2015: Fixed rare [one byte out-of bounds read](https://github.com/klauspost/compress/issues/20). Please update!
-* Nov 23 2015: Optimization on token writer. ~2-4% faster. Contributed by [@dsnet](https://github.com/dsnet).
-* Nov 20 2015: Small optimization to bit writer on 64 bit systems.
-* Nov 17 2015: Fixed out-of-bound errors if the underlying Writer returned an error. See [#15](https://github.com/klauspost/compress/issues/15).
-* Nov 12 2015: Added [io.WriterTo](https://golang.org/pkg/io/#WriterTo) support to gzip/inflate.
-* Nov 11 2015: Merged [CL 16669](https://go-review.googlesource.com/#/c/16669/4): archive/zip: enable overriding (de)compressors per file
-* Oct 15 2015: Added skipping on uncompressible data. Random data speed up >5x.
-
-
-
-# deflate usage
-
-The packages are drop-in replacements for standard library [deflate](https://godoc.org/github.com/klauspost/compress/flate), [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip), and [zlib](https://godoc.org/github.com/klauspost/compress/zlib). Simply replace the import path to use them:
-
-Typical speed is about 2x of the standard library packages.
-
-| old import | new import | Documentation |
-|------------------|---------------------------------------|-------------------------------------------------------------------------|
-| `compress/gzip` | `github.com/klauspost/compress/gzip` | [gzip](https://pkg.go.dev/github.com/klauspost/compress/gzip?tab=doc) |
-| `compress/zlib` | `github.com/klauspost/compress/zlib` | [zlib](https://pkg.go.dev/github.com/klauspost/compress/zlib?tab=doc) |
-| `archive/zip` | `github.com/klauspost/compress/zip` | [zip](https://pkg.go.dev/github.com/klauspost/compress/zip?tab=doc) |
-| `compress/flate` | `github.com/klauspost/compress/flate` | [flate](https://pkg.go.dev/github.com/klauspost/compress/flate?tab=doc) |
-
-You may also be interested in [pgzip](https://github.com/klauspost/pgzip), which is a drop-in replacement for gzip, which support multithreaded compression on big files and the optimized [crc32](https://github.com/klauspost/crc32) package used by these packages.
-
-The packages implement the same API as the standard library, so you can use the original godoc documentation: [gzip](http://golang.org/pkg/compress/gzip/), [zip](http://golang.org/pkg/archive/zip/), [zlib](http://golang.org/pkg/compress/zlib/), [flate](http://golang.org/pkg/compress/flate/).
-
-Currently there is only minor speedup on decompression (mostly CRC32 calculation).
-
-Memory usage is typically 1MB for a Writer. stdlib is in the same range.
-If you expect to have a lot of concurrently allocated Writers consider using
-the stateless compression described below.
-
-For compression performance, see: [this spreadsheet](https://docs.google.com/spreadsheets/d/1nuNE2nPfuINCZJRMt6wFWhKpToF95I47XjSsc-1rbPQ/edit?usp=sharing).
-
-To disable all assembly add `-tags=noasm`. This works across all packages.
-
-# Stateless compression
-
-This package offers stateless compression as a special option for gzip/deflate.
-It will do compression but without maintaining any state between Write calls.
-
-This means there will be no memory kept between Write calls, but compression and speed will be suboptimal.
-
-This is only relevant in cases where you expect to run many thousands of compressors concurrently,
-but with very little activity. This is *not* intended for regular web servers serving individual requests.
-
-Because of this, the size of actual Write calls will affect output size.
-
-In gzip, specify level `-3` / `gzip.StatelessCompression` to enable.
-
-For direct deflate use, NewStatelessWriter and StatelessDeflate are available. See [documentation](https://godoc.org/github.com/klauspost/compress/flate#NewStatelessWriter)
-
-A `bufio.Writer` can of course be used to control write sizes. For example, to use a 4KB buffer:
-
-```go
- // replace 'ioutil.Discard' with your output.
- gzw, err := gzip.NewWriterLevel(ioutil.Discard, gzip.StatelessCompression)
- if err != nil {
- return err
- }
- defer gzw.Close()
-
- w := bufio.NewWriterSize(gzw, 4096)
- defer w.Flush()
-
- // Write to 'w'
-```
-
-This will only use up to 4KB in memory when the writer is idle.
-
-Compression is almost always worse than the fastest compression level
-and each write will allocate (a little) memory.
-
-
-# Other packages
-
-Here are other packages of good quality and pure Go (no cgo wrappers or autoconverted code):
-
-* [github.com/pierrec/lz4](https://github.com/pierrec/lz4) - strong multithreaded LZ4 compression.
-* [github.com/cosnicolaou/pbzip2](https://github.com/cosnicolaou/pbzip2) - multithreaded bzip2 decompression.
-* [github.com/dsnet/compress](https://github.com/dsnet/compress) - brotli decompression, bzip2 writer.
-* [github.com/ronanh/intcomp](https://github.com/ronanh/intcomp) - Integer compression.
-* [github.com/spenczar/fpc](https://github.com/spenczar/fpc) - Float compression.
-* [github.com/minio/zipindex](https://github.com/minio/zipindex) - External ZIP directory index.
-* [github.com/ybirader/pzip](https://github.com/ybirader/pzip) - Fast concurrent zip archiver and extractor.
-
-# license
-
-This code is licensed under the same conditions as the original Go code. See LICENSE file.
-
-
-
-
-
+# compress
+
+This package provides various compression algorithms.
+
+* [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression in pure Go.
+* [S2](https://github.com/klauspost/compress/tree/master/s2#s2-compression) is a high performance replacement for Snappy.
+* Optimized [deflate](https://godoc.org/github.com/klauspost/compress/flate) packages which can be used as a dropin replacement for [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip) and [zlib](https://godoc.org/github.com/klauspost/compress/zlib).
+* [snappy](https://github.com/klauspost/compress/tree/master/snappy) is a drop-in replacement for `github.com/golang/snappy` offering better compression and concurrent streams.
+* [huff0](https://github.com/klauspost/compress/tree/master/huff0) and [FSE](https://github.com/klauspost/compress/tree/master/fse) implementations for raw entropy encoding.
+* [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp) Provides client and server wrappers for handling gzipped/zstd HTTP requests efficiently.
+* [pgzip](https://github.com/klauspost/pgzip) is a separate package that provides a very fast parallel gzip implementation.
+
+[](https://pkg.go.dev/github.com/klauspost/compress?tab=subdirectories)
+[](https://github.com/klauspost/compress/actions/workflows/go.yml)
+[](https://sourcegraph.com/github.com/klauspost/compress?badge)
+
+# package usage
+
+Use `go get github.com/klauspost/compress@latest` to add it to your project.
+
+This package will support the current Go version and 2 versions back.
+
+* Use the `nounsafe` tag to disable all use of the "unsafe" package.
+* Use the `noasm` tag to disable all assembly across packages.
+
+Use the links above for more information on each.
+
+# changelog
+
+* Feb 9th, 2026 [1.18.4](https://github.com/klauspost/compress/releases/tag/v1.18.4)
+ * gzhttp: Add zstandard to server handler wrapper https://github.com/klauspost/compress/pull/1121
+ * zstd: Add ResetWithOptions to encoder/decoder https://github.com/klauspost/compress/pull/1122
+ * gzhttp: preserve qvalue when extra parameters follow in Accept-Encoding by @analytically in https://github.com/klauspost/compress/pull/1116
+
+* Jan 16th, 2026 [1.18.3](https://github.com/klauspost/compress/releases/tag/v1.18.3)
+ * Downstream CVE-2025-61728. See [golang/go#77102](https://github.com/golang/go/issues/77102).
+
+* Dec 1st, 2025 - [1.18.2](https://github.com/klauspost/compress/releases/tag/v1.18.2)
+ * flate: Fix invalid encoding on level 9 with single value input in https://github.com/klauspost/compress/pull/1115
+ * flate: reduce stateless allocations by @RXamzin in https://github.com/klauspost/compress/pull/1106
+
+* Oct 20, 2025 - [1.18.1](https://github.com/klauspost/compress/releases/tag/v1.18.1) - RETRACTED
+ * zstd: Add simple zstd EncodeTo/DecodeTo functions https://github.com/klauspost/compress/pull/1079
+ * zstd: Fix incorrect buffer size in dictionary encodes https://github.com/klauspost/compress/pull/1059
+ * s2: check for cap, not len of buffer in EncodeBetter/Best by @vdarulis in https://github.com/klauspost/compress/pull/1080
+ * zlib: Avoiding extra allocation in zlib.reader.Reset by @travelpolicy in https://github.com/klauspost/compress/pull/1086
+ * gzhttp: remove redundant err check in zstdReader by @ryanfowler in https://github.com/klauspost/compress/pull/1090
+ * flate: Faster load+store https://github.com/klauspost/compress/pull/1104
+ * flate: Simplify matchlen https://github.com/klauspost/compress/pull/1101
+ * flate: Use exact sizes for huffman tables https://github.com/klauspost/compress/pull/1103
+
+* Feb 19th, 2025 - [1.18.0](https://github.com/klauspost/compress/releases/tag/v1.18.0)
+ * Add unsafe little endian loaders https://github.com/klauspost/compress/pull/1036
+ * fix: check `r.err != nil` but return a nil value error `err` by @alingse in https://github.com/klauspost/compress/pull/1028
+ * flate: Simplify L4-6 loading https://github.com/klauspost/compress/pull/1043
+ * flate: Simplify matchlen (remove asm) https://github.com/klauspost/compress/pull/1045
+ * s2: Improve small block compression speed w/o asm https://github.com/klauspost/compress/pull/1048
+ * flate: Fix matchlen L5+L6 https://github.com/klauspost/compress/pull/1049
+ * flate: Cleanup & reduce casts https://github.com/klauspost/compress/pull/1050
+
+
+ See changes to v1.17.x
+
+* Oct 11th, 2024 - [1.17.11](https://github.com/klauspost/compress/releases/tag/v1.17.11)
+ * zstd: Fix extra CRC written with multiple Close calls https://github.com/klauspost/compress/pull/1017
+ * s2: Don't use stack for index tables https://github.com/klauspost/compress/pull/1014
+ * gzhttp: No content-type on no body response code by @juliens in https://github.com/klauspost/compress/pull/1011
+ * gzhttp: Do not set the content-type when response has no body by @kevinpollet in https://github.com/klauspost/compress/pull/1013
+
+* Sep 23rd, 2024 - [1.17.10](https://github.com/klauspost/compress/releases/tag/v1.17.10)
+ * gzhttp: Add TransportAlwaysDecompress option. https://github.com/klauspost/compress/pull/978
+ * gzhttp: Add supported decompress request body by @mirecl in https://github.com/klauspost/compress/pull/1002
+ * s2: Add EncodeBuffer buffer recycling callback https://github.com/klauspost/compress/pull/982
+ * zstd: Improve memory usage on small streaming encodes https://github.com/klauspost/compress/pull/1007
+ * flate: read data written with partial flush by @vajexal in https://github.com/klauspost/compress/pull/996
+
+* Jun 12th, 2024 - [1.17.9](https://github.com/klauspost/compress/releases/tag/v1.17.9)
+ * s2: Reduce ReadFrom temporary allocations https://github.com/klauspost/compress/pull/949
+ * flate, zstd: Shave some bytes off amd64 matchLen by @greatroar in https://github.com/klauspost/compress/pull/963
+ * Upgrade zip/zlib to 1.22.4 upstream https://github.com/klauspost/compress/pull/970 https://github.com/klauspost/compress/pull/971
+ * zstd: BuildDict fails with RLE table https://github.com/klauspost/compress/pull/951
+
+* Apr 9th, 2024 - [1.17.8](https://github.com/klauspost/compress/releases/tag/v1.17.8)
+ * zstd: Reject blocks where reserved values are not 0 https://github.com/klauspost/compress/pull/885
+ * zstd: Add RLE detection+encoding https://github.com/klauspost/compress/pull/938
+
+* Feb 21st, 2024 - [1.17.7](https://github.com/klauspost/compress/releases/tag/v1.17.7)
+ * s2: Add AsyncFlush method: Complete the block without flushing by @Jille in https://github.com/klauspost/compress/pull/927
+ * s2: Fix literal+repeat exceeds dst crash https://github.com/klauspost/compress/pull/930
+
+* Feb 5th, 2024 - [1.17.6](https://github.com/klauspost/compress/releases/tag/v1.17.6)
+ * zstd: Fix incorrect repeat coding in best mode https://github.com/klauspost/compress/pull/923
+ * s2: Fix DecodeConcurrent deadlock on errors https://github.com/klauspost/compress/pull/925
+
+* Jan 26th, 2024 - [v1.17.5](https://github.com/klauspost/compress/releases/tag/v1.17.5)
+ * flate: Fix reset with dictionary on custom window encodes https://github.com/klauspost/compress/pull/912
+ * zstd: Add Frame header encoding and stripping https://github.com/klauspost/compress/pull/908
+ * zstd: Limit better/best default window to 8MB https://github.com/klauspost/compress/pull/913
+ * zstd: Speed improvements by @greatroar in https://github.com/klauspost/compress/pull/896 https://github.com/klauspost/compress/pull/910
+ * s2: Fix callbacks for skippable blocks and disallow 0xfe (Padding) by @Jille in https://github.com/klauspost/compress/pull/916 https://github.com/klauspost/compress/pull/917
+https://github.com/klauspost/compress/pull/919 https://github.com/klauspost/compress/pull/918
+
+* Dec 1st, 2023 - [v1.17.4](https://github.com/klauspost/compress/releases/tag/v1.17.4)
+ * huff0: Speed up symbol counting by @greatroar in https://github.com/klauspost/compress/pull/887
+ * huff0: Remove byteReader by @greatroar in https://github.com/klauspost/compress/pull/886
+ * gzhttp: Allow overriding decompression on transport https://github.com/klauspost/compress/pull/892
+ * gzhttp: Clamp compression level https://github.com/klauspost/compress/pull/890
+ * gzip: Error out if reserved bits are set https://github.com/klauspost/compress/pull/891
+
+* Nov 15th, 2023 - [v1.17.3](https://github.com/klauspost/compress/releases/tag/v1.17.3)
+ * fse: Fix max header size https://github.com/klauspost/compress/pull/881
+ * zstd: Improve better/best compression https://github.com/klauspost/compress/pull/877
+ * gzhttp: Fix missing content type on Close https://github.com/klauspost/compress/pull/883
+
+* Oct 22nd, 2023 - [v1.17.2](https://github.com/klauspost/compress/releases/tag/v1.17.2)
+ * zstd: Fix rare *CORRUPTION* output in "best" mode. See https://github.com/klauspost/compress/pull/876
+
+* Oct 14th, 2023 - [v1.17.1](https://github.com/klauspost/compress/releases/tag/v1.17.1)
+ * s2: Fix S2 "best" dictionary wrong encoding https://github.com/klauspost/compress/pull/871
+ * flate: Reduce allocations in decompressor and minor code improvements by @fakefloordiv in https://github.com/klauspost/compress/pull/869
+ * s2: Fix EstimateBlockSize on 6&7 length input https://github.com/klauspost/compress/pull/867
+
+* Sept 19th, 2023 - [v1.17.0](https://github.com/klauspost/compress/releases/tag/v1.17.0)
+ * Add experimental dictionary builder https://github.com/klauspost/compress/pull/853
+ * Add xerial snappy read/writer https://github.com/klauspost/compress/pull/838
+ * flate: Add limited window compression https://github.com/klauspost/compress/pull/843
+ * s2: Do 2 overlapping match checks https://github.com/klauspost/compress/pull/839
+ * flate: Add amd64 assembly matchlen https://github.com/klauspost/compress/pull/837
+ * gzip: Copy bufio.Reader on Reset by @thatguystone in https://github.com/klauspost/compress/pull/860
+
+
+
+ See changes to v1.16.x
+
+
+* July 1st, 2023 - [v1.16.7](https://github.com/klauspost/compress/releases/tag/v1.16.7)
+ * zstd: Fix default level first dictionary encode https://github.com/klauspost/compress/pull/829
+ * s2: add GetBufferCapacity() method by @GiedriusS in https://github.com/klauspost/compress/pull/832
+
+* June 13, 2023 - [v1.16.6](https://github.com/klauspost/compress/releases/tag/v1.16.6)
+ * zstd: correctly ignore WithEncoderPadding(1) by @ianlancetaylor in https://github.com/klauspost/compress/pull/806
+ * zstd: Add amd64 match length assembly https://github.com/klauspost/compress/pull/824
+ * gzhttp: Handle informational headers by @rtribotte in https://github.com/klauspost/compress/pull/815
+ * s2: Improve Better compression slightly https://github.com/klauspost/compress/pull/663
+
+* Apr 16, 2023 - [v1.16.5](https://github.com/klauspost/compress/releases/tag/v1.16.5)
+ * zstd: readByte needs to use io.ReadFull by @jnoxon in https://github.com/klauspost/compress/pull/802
+ * gzip: Fix WriterTo after initial read https://github.com/klauspost/compress/pull/804
+
+* Apr 5, 2023 - [v1.16.4](https://github.com/klauspost/compress/releases/tag/v1.16.4)
+ * zstd: Improve zstd best efficiency by @greatroar and @klauspost in https://github.com/klauspost/compress/pull/784
+ * zstd: Respect WithAllLitEntropyCompression https://github.com/klauspost/compress/pull/792
+ * zstd: Fix amd64 not always detecting corrupt data https://github.com/klauspost/compress/pull/785
+ * zstd: Various minor improvements by @greatroar in https://github.com/klauspost/compress/pull/788 https://github.com/klauspost/compress/pull/794 https://github.com/klauspost/compress/pull/795
+ * s2: Fix huge block overflow https://github.com/klauspost/compress/pull/779
+ * s2: Allow CustomEncoder fallback https://github.com/klauspost/compress/pull/780
+ * gzhttp: Support ResponseWriter Unwrap() in gzhttp handler by @jgimenez in https://github.com/klauspost/compress/pull/799
+
+* Mar 13, 2023 - [v1.16.1](https://github.com/klauspost/compress/releases/tag/v1.16.1)
+ * zstd: Speed up + improve best encoder by @greatroar in https://github.com/klauspost/compress/pull/776
+ * gzhttp: Add optional [BREACH mitigation](https://github.com/klauspost/compress/tree/master/gzhttp#breach-mitigation). https://github.com/klauspost/compress/pull/762 https://github.com/klauspost/compress/pull/768 https://github.com/klauspost/compress/pull/769 https://github.com/klauspost/compress/pull/770 https://github.com/klauspost/compress/pull/767
+ * s2: Add Intel LZ4s converter https://github.com/klauspost/compress/pull/766
+ * zstd: Minor bug fixes https://github.com/klauspost/compress/pull/771 https://github.com/klauspost/compress/pull/772 https://github.com/klauspost/compress/pull/773
+ * huff0: Speed up compress1xDo by @greatroar in https://github.com/klauspost/compress/pull/774
+
+* Feb 26, 2023 - [v1.16.0](https://github.com/klauspost/compress/releases/tag/v1.16.0)
+ * s2: Add [Dictionary](https://github.com/klauspost/compress/tree/master/s2#dictionaries) support. https://github.com/klauspost/compress/pull/685
+ * s2: Add Compression Size Estimate. https://github.com/klauspost/compress/pull/752
+ * s2: Add support for custom stream encoder. https://github.com/klauspost/compress/pull/755
+ * s2: Add LZ4 block converter. https://github.com/klauspost/compress/pull/748
+ * s2: Support io.ReaderAt in ReadSeeker. https://github.com/klauspost/compress/pull/747
+ * s2c/s2sx: Use concurrent decoding. https://github.com/klauspost/compress/pull/746
+
+
+
+ See changes to v1.15.x
+
+* Jan 21st, 2023 (v1.15.15)
+ * deflate: Improve level 7-9 https://github.com/klauspost/compress/pull/739
+ * zstd: Add delta encoding support by @greatroar in https://github.com/klauspost/compress/pull/728
+ * zstd: Various speed improvements by @greatroar https://github.com/klauspost/compress/pull/741 https://github.com/klauspost/compress/pull/734 https://github.com/klauspost/compress/pull/736 https://github.com/klauspost/compress/pull/744 https://github.com/klauspost/compress/pull/743 https://github.com/klauspost/compress/pull/745
+ * gzhttp: Add SuffixETag() and DropETag() options to prevent ETag collisions on compressed responses by @willbicks in https://github.com/klauspost/compress/pull/740
+
+* Jan 3rd, 2023 (v1.15.14)
+
+ * flate: Improve speed in big stateless blocks https://github.com/klauspost/compress/pull/718
+ * zstd: Minor speed tweaks by @greatroar in https://github.com/klauspost/compress/pull/716 https://github.com/klauspost/compress/pull/720
+ * export NoGzipResponseWriter for custom ResponseWriter wrappers by @harshavardhana in https://github.com/klauspost/compress/pull/722
+ * s2: Add example for indexing and existing stream https://github.com/klauspost/compress/pull/723
+
+* Dec 11, 2022 (v1.15.13)
+ * zstd: Add [MaxEncodedSize](https://pkg.go.dev/github.com/klauspost/compress@v1.15.13/zstd#Encoder.MaxEncodedSize) to encoder https://github.com/klauspost/compress/pull/691
+ * zstd: Various tweaks and improvements https://github.com/klauspost/compress/pull/693 https://github.com/klauspost/compress/pull/695 https://github.com/klauspost/compress/pull/696 https://github.com/klauspost/compress/pull/701 https://github.com/klauspost/compress/pull/702 https://github.com/klauspost/compress/pull/703 https://github.com/klauspost/compress/pull/704 https://github.com/klauspost/compress/pull/705 https://github.com/klauspost/compress/pull/706 https://github.com/klauspost/compress/pull/707 https://github.com/klauspost/compress/pull/708
+
+* Oct 26, 2022 (v1.15.12)
+
+ * zstd: Tweak decoder allocs. https://github.com/klauspost/compress/pull/680
+ * gzhttp: Always delete `HeaderNoCompression` https://github.com/klauspost/compress/pull/683
+
+* Sept 26, 2022 (v1.15.11)
+
+ * flate: Improve level 1-3 compression https://github.com/klauspost/compress/pull/678
+ * zstd: Improve "best" compression by @nightwolfz in https://github.com/klauspost/compress/pull/677
+ * zstd: Fix+reduce decompression allocations https://github.com/klauspost/compress/pull/668
+ * zstd: Fix non-effective noescape tag https://github.com/klauspost/compress/pull/667
+
+* Sept 16, 2022 (v1.15.10)
+
+ * zstd: Add [WithDecodeAllCapLimit](https://pkg.go.dev/github.com/klauspost/compress@v1.15.10/zstd#WithDecodeAllCapLimit) https://github.com/klauspost/compress/pull/649
+ * Add Go 1.19 - deprecate Go 1.16 https://github.com/klauspost/compress/pull/651
+ * flate: Improve level 5+6 compression https://github.com/klauspost/compress/pull/656
+ * zstd: Improve "better" compression https://github.com/klauspost/compress/pull/657
+ * s2: Improve "best" compression https://github.com/klauspost/compress/pull/658
+ * s2: Improve "better" compression. https://github.com/klauspost/compress/pull/635
+ * s2: Slightly faster non-assembly decompression https://github.com/klauspost/compress/pull/646
+ * Use arrays for constant size copies https://github.com/klauspost/compress/pull/659
+
+* July 21, 2022 (v1.15.9)
+
+ * zstd: Fix decoder crash on amd64 (no BMI) on invalid input https://github.com/klauspost/compress/pull/645
+ * zstd: Disable decoder extended memory copies (amd64) due to possible crashes https://github.com/klauspost/compress/pull/644
+ * zstd: Allow single segments up to "max decoded size" https://github.com/klauspost/compress/pull/643
+
+* July 13, 2022 (v1.15.8)
+
+ * gzip: fix stack exhaustion bug in Reader.Read https://github.com/klauspost/compress/pull/641
+ * s2: Add Index header trim/restore https://github.com/klauspost/compress/pull/638
+ * zstd: Optimize seqdeq amd64 asm by @greatroar in https://github.com/klauspost/compress/pull/636
+ * zstd: Improve decoder memcopy https://github.com/klauspost/compress/pull/637
+ * huff0: Pass a single bitReader pointer to asm by @greatroar in https://github.com/klauspost/compress/pull/634
+ * zstd: Branchless getBits for amd64 w/o BMI2 by @greatroar in https://github.com/klauspost/compress/pull/640
+ * gzhttp: Remove header before writing https://github.com/klauspost/compress/pull/639
+
+* June 29, 2022 (v1.15.7)
+
+ * s2: Fix absolute forward seeks https://github.com/klauspost/compress/pull/633
+ * zip: Merge upstream https://github.com/klauspost/compress/pull/631
+ * zip: Re-add zip64 fix https://github.com/klauspost/compress/pull/624
+ * zstd: translate fseDecoder.buildDtable into asm by @WojciechMula in https://github.com/klauspost/compress/pull/598
+ * flate: Faster histograms https://github.com/klauspost/compress/pull/620
+ * deflate: Use compound hcode https://github.com/klauspost/compress/pull/622
+
+* June 3, 2022 (v1.15.6)
+ * s2: Improve coding for long, close matches https://github.com/klauspost/compress/pull/613
+ * s2c: Add Snappy/S2 stream recompression https://github.com/klauspost/compress/pull/611
+ * zstd: Always use configured block size https://github.com/klauspost/compress/pull/605
+ * zstd: Fix incorrect hash table placement for dict encoding in default https://github.com/klauspost/compress/pull/606
+ * zstd: Apply default config to ZipDecompressor without options https://github.com/klauspost/compress/pull/608
+ * gzhttp: Exclude more common archive formats https://github.com/klauspost/compress/pull/612
+ * s2: Add ReaderIgnoreCRC https://github.com/klauspost/compress/pull/609
+ * s2: Remove sanity load on index creation https://github.com/klauspost/compress/pull/607
+ * snappy: Use dedicated function for scoring https://github.com/klauspost/compress/pull/614
+ * s2c+s2d: Use official snappy framed extension https://github.com/klauspost/compress/pull/610
+
+* May 25, 2022 (v1.15.5)
+ * s2: Add concurrent stream decompression https://github.com/klauspost/compress/pull/602
+ * s2: Fix final emit oob read crash on amd64 https://github.com/klauspost/compress/pull/601
+ * huff0: asm implementation of Decompress1X by @WojciechMula https://github.com/klauspost/compress/pull/596
+ * zstd: Use 1 less goroutine for stream decoding https://github.com/klauspost/compress/pull/588
+ * zstd: Copy literal in 16 byte blocks when possible https://github.com/klauspost/compress/pull/592
+ * zstd: Speed up when WithDecoderLowmem(false) https://github.com/klauspost/compress/pull/599
+ * zstd: faster next state update in BMI2 version of decode by @WojciechMula in https://github.com/klauspost/compress/pull/593
+ * huff0: Do not check max size when reading table. https://github.com/klauspost/compress/pull/586
+ * flate: Inplace hashing for level 7-9 https://github.com/klauspost/compress/pull/590
+
+
+* May 11, 2022 (v1.15.4)
+ * huff0: decompress directly into output by @WojciechMula in [#577](https://github.com/klauspost/compress/pull/577)
+ * inflate: Keep dict on stack [#581](https://github.com/klauspost/compress/pull/581)
+ * zstd: Faster decoding memcopy in asm [#583](https://github.com/klauspost/compress/pull/583)
+ * zstd: Fix ignored crc [#580](https://github.com/klauspost/compress/pull/580)
+
+* May 5, 2022 (v1.15.3)
+ * zstd: Allow to ignore checksum checking by @WojciechMula [#572](https://github.com/klauspost/compress/pull/572)
+ * s2: Fix incorrect seek for io.SeekEnd in [#575](https://github.com/klauspost/compress/pull/575)
+
+* Apr 26, 2022 (v1.15.2)
+ * zstd: Add x86-64 assembly for decompression on streams and blocks. Contributed by [@WojciechMula](https://github.com/WojciechMula). Typically 2x faster. [#528](https://github.com/klauspost/compress/pull/528) [#531](https://github.com/klauspost/compress/pull/531) [#545](https://github.com/klauspost/compress/pull/545) [#537](https://github.com/klauspost/compress/pull/537)
+ * zstd: Add options to ZipDecompressor and fixes [#539](https://github.com/klauspost/compress/pull/539)
+ * s2: Use sorted search for index [#555](https://github.com/klauspost/compress/pull/555)
+ * Minimum version is Go 1.16, added CI test on 1.18.
+
+* Mar 11, 2022 (v1.15.1)
+ * huff0: Add x86 assembly of Decode4X by @WojciechMula in [#512](https://github.com/klauspost/compress/pull/512)
+ * zstd: Reuse zip decoders in [#514](https://github.com/klauspost/compress/pull/514)
+ * zstd: Detect extra block data and report as corrupted in [#520](https://github.com/klauspost/compress/pull/520)
+ * zstd: Handle zero sized frame content size stricter in [#521](https://github.com/klauspost/compress/pull/521)
+ * zstd: Add stricter block size checks in [#523](https://github.com/klauspost/compress/pull/523)
+
+* Mar 3, 2022 (v1.15.0)
+ * zstd: Refactor decoder [#498](https://github.com/klauspost/compress/pull/498)
+ * zstd: Add stream encoding without goroutines [#505](https://github.com/klauspost/compress/pull/505)
+ * huff0: Prevent single blocks exceeding 16 bits by @klauspost in[#507](https://github.com/klauspost/compress/pull/507)
+ * flate: Inline literal emission [#509](https://github.com/klauspost/compress/pull/509)
+ * gzhttp: Add zstd to transport [#400](https://github.com/klauspost/compress/pull/400)
+ * gzhttp: Make content-type optional [#510](https://github.com/klauspost/compress/pull/510)
+
+Both compression and decompression now supports "synchronous" stream operations. This means that whenever "concurrency" is set to 1, they will operate without spawning goroutines.
+
+Stream decompression is now faster on asynchronous, since the goroutine allocation much more effectively splits the workload. On typical streams this will typically use 2 cores fully for decompression. When a stream has finished decoding no goroutines will be left over, so decoders can now safely be pooled and still be garbage collected.
+
+While the release has been extensively tested, it is recommended to testing when upgrading.
+
+
+
+
+ See changes to v1.14.x
+
+* Feb 22, 2022 (v1.14.4)
+ * flate: Fix rare huffman only (-2) corruption. [#503](https://github.com/klauspost/compress/pull/503)
+ * zip: Update deprecated CreateHeaderRaw to correctly call CreateRaw by @saracen in [#502](https://github.com/klauspost/compress/pull/502)
+ * zip: don't read data descriptor early by @saracen in [#501](https://github.com/klauspost/compress/pull/501) #501
+ * huff0: Use static decompression buffer up to 30% faster [#499](https://github.com/klauspost/compress/pull/499) [#500](https://github.com/klauspost/compress/pull/500)
+
+* Feb 17, 2022 (v1.14.3)
+ * flate: Improve fastest levels compression speed ~10% more throughput. [#482](https://github.com/klauspost/compress/pull/482) [#489](https://github.com/klauspost/compress/pull/489) [#490](https://github.com/klauspost/compress/pull/490) [#491](https://github.com/klauspost/compress/pull/491) [#494](https://github.com/klauspost/compress/pull/494) [#478](https://github.com/klauspost/compress/pull/478)
+ * flate: Faster decompression speed, ~5-10%. [#483](https://github.com/klauspost/compress/pull/483)
+ * s2: Faster compression with Go v1.18 and amd64 microarch level 3+. [#484](https://github.com/klauspost/compress/pull/484) [#486](https://github.com/klauspost/compress/pull/486)
+
+* Jan 25, 2022 (v1.14.2)
+ * zstd: improve header decoder by @dsnet [#476](https://github.com/klauspost/compress/pull/476)
+ * zstd: Add bigger default blocks [#469](https://github.com/klauspost/compress/pull/469)
+ * zstd: Remove unused decompression buffer [#470](https://github.com/klauspost/compress/pull/470)
+ * zstd: Fix logically dead code by @ningmingxiao [#472](https://github.com/klauspost/compress/pull/472)
+ * flate: Improve level 7-9 [#471](https://github.com/klauspost/compress/pull/471) [#473](https://github.com/klauspost/compress/pull/473)
+ * zstd: Add noasm tag for xxhash [#475](https://github.com/klauspost/compress/pull/475)
+
+* Jan 11, 2022 (v1.14.1)
+ * s2: Add stream index in [#462](https://github.com/klauspost/compress/pull/462)
+ * flate: Speed and efficiency improvements in [#439](https://github.com/klauspost/compress/pull/439) [#461](https://github.com/klauspost/compress/pull/461) [#455](https://github.com/klauspost/compress/pull/455) [#452](https://github.com/klauspost/compress/pull/452) [#458](https://github.com/klauspost/compress/pull/458)
+ * zstd: Performance improvement in [#420]( https://github.com/klauspost/compress/pull/420) [#456](https://github.com/klauspost/compress/pull/456) [#437](https://github.com/klauspost/compress/pull/437) [#467](https://github.com/klauspost/compress/pull/467) [#468](https://github.com/klauspost/compress/pull/468)
+ * zstd: add arm64 xxhash assembly in [#464](https://github.com/klauspost/compress/pull/464)
+ * Add garbled for binaries for s2 in [#445](https://github.com/klauspost/compress/pull/445)
+
+
+
+ See changes to v1.13.x
+
+* Aug 30, 2021 (v1.13.5)
+ * gz/zlib/flate: Alias stdlib errors [#425](https://github.com/klauspost/compress/pull/425)
+ * s2: Add block support to commandline tools [#413](https://github.com/klauspost/compress/pull/413)
+ * zstd: pooledZipWriter should return Writers to the same pool [#426](https://github.com/klauspost/compress/pull/426)
+ * Removed golang/snappy as external dependency for tests [#421](https://github.com/klauspost/compress/pull/421)
+
+* Aug 12, 2021 (v1.13.4)
+ * Add [snappy replacement package](https://github.com/klauspost/compress/tree/master/snappy).
+ * zstd: Fix incorrect encoding in "best" mode [#415](https://github.com/klauspost/compress/pull/415)
+
+* Aug 3, 2021 (v1.13.3)
+ * zstd: Improve Best compression [#404](https://github.com/klauspost/compress/pull/404)
+ * zstd: Fix WriteTo error forwarding [#411](https://github.com/klauspost/compress/pull/411)
+ * gzhttp: Return http.HandlerFunc instead of http.Handler. Unlikely breaking change. [#406](https://github.com/klauspost/compress/pull/406)
+ * s2sx: Fix max size error [#399](https://github.com/klauspost/compress/pull/399)
+ * zstd: Add optional stream content size on reset [#401](https://github.com/klauspost/compress/pull/401)
+ * zstd: use SpeedBestCompression for level >= 10 [#410](https://github.com/klauspost/compress/pull/410)
+
+* Jun 14, 2021 (v1.13.1)
+ * s2: Add full Snappy output support [#396](https://github.com/klauspost/compress/pull/396)
+ * zstd: Add configurable [Decoder window](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithDecoderMaxWindow) size [#394](https://github.com/klauspost/compress/pull/394)
+ * gzhttp: Add header to skip compression [#389](https://github.com/klauspost/compress/pull/389)
+ * s2: Improve speed with bigger output margin [#395](https://github.com/klauspost/compress/pull/395)
+
+* Jun 3, 2021 (v1.13.0)
+ * Added [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp#gzip-handler) which allows wrapping HTTP servers and clients with GZIP compressors.
+ * zstd: Detect short invalid signatures [#382](https://github.com/klauspost/compress/pull/382)
+ * zstd: Spawn decoder goroutine only if needed. [#380](https://github.com/klauspost/compress/pull/380)
+
+
+
+
+ See changes to v1.12.x
+
+* May 25, 2021 (v1.12.3)
+ * deflate: Better/faster Huffman encoding [#374](https://github.com/klauspost/compress/pull/374)
+ * deflate: Allocate less for history. [#375](https://github.com/klauspost/compress/pull/375)
+ * zstd: Forward read errors [#373](https://github.com/klauspost/compress/pull/373)
+
+* Apr 27, 2021 (v1.12.2)
+ * zstd: Improve better/best compression [#360](https://github.com/klauspost/compress/pull/360) [#364](https://github.com/klauspost/compress/pull/364) [#365](https://github.com/klauspost/compress/pull/365)
+ * zstd: Add helpers to compress/decompress zstd inside zip files [#363](https://github.com/klauspost/compress/pull/363)
+ * deflate: Improve level 5+6 compression [#367](https://github.com/klauspost/compress/pull/367)
+ * s2: Improve better/best compression [#358](https://github.com/klauspost/compress/pull/358) [#359](https://github.com/klauspost/compress/pull/358)
+ * s2: Load after checking src limit on amd64. [#362](https://github.com/klauspost/compress/pull/362)
+ * s2sx: Limit max executable size [#368](https://github.com/klauspost/compress/pull/368)
+
+* Apr 14, 2021 (v1.12.1)
+ * snappy package removed. Upstream added as dependency.
+ * s2: Better compression in "best" mode [#353](https://github.com/klauspost/compress/pull/353)
+ * s2sx: Add stdin input and detect pre-compressed from signature [#352](https://github.com/klauspost/compress/pull/352)
+ * s2c/s2d: Add http as possible input [#348](https://github.com/klauspost/compress/pull/348)
+ * s2c/s2d/s2sx: Always truncate when writing files [#352](https://github.com/klauspost/compress/pull/352)
+ * zstd: Reduce memory usage further when using [WithLowerEncoderMem](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithLowerEncoderMem) [#346](https://github.com/klauspost/compress/pull/346)
+ * s2: Fix potential problem with amd64 assembly and profilers [#349](https://github.com/klauspost/compress/pull/349)
+
+
+
+ See changes to v1.11.x
+
+* Mar 26, 2021 (v1.11.13)
+ * zstd: Big speedup on small dictionary encodes [#344](https://github.com/klauspost/compress/pull/344) [#345](https://github.com/klauspost/compress/pull/345)
+ * zstd: Add [WithLowerEncoderMem](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithLowerEncoderMem) encoder option [#336](https://github.com/klauspost/compress/pull/336)
+ * deflate: Improve entropy compression [#338](https://github.com/klauspost/compress/pull/338)
+ * s2: Clean up and minor performance improvement in best [#341](https://github.com/klauspost/compress/pull/341)
+
+* Mar 5, 2021 (v1.11.12)
+ * s2: Add `s2sx` binary that creates [self extracting archives](https://github.com/klauspost/compress/tree/master/s2#s2sx-self-extracting-archives).
+ * s2: Speed up decompression on non-assembly platforms [#328](https://github.com/klauspost/compress/pull/328)
+
+* Mar 1, 2021 (v1.11.9)
+ * s2: Add ARM64 decompression assembly. Around 2x output speed. [#324](https://github.com/klauspost/compress/pull/324)
+ * s2: Improve "better" speed and efficiency. [#325](https://github.com/klauspost/compress/pull/325)
+ * s2: Fix binaries.
+
+* Feb 25, 2021 (v1.11.8)
+ * s2: Fixed occasional out-of-bounds write on amd64. Upgrade recommended.
+ * s2: Add AMD64 assembly for better mode. 25-50% faster. [#315](https://github.com/klauspost/compress/pull/315)
+ * s2: Less upfront decoder allocation. [#322](https://github.com/klauspost/compress/pull/322)
+ * zstd: Faster "compression" of incompressible data. [#314](https://github.com/klauspost/compress/pull/314)
+ * zip: Fix zip64 headers. [#313](https://github.com/klauspost/compress/pull/313)
+
+* Jan 14, 2021 (v1.11.7)
+ * Use Bytes() interface to get bytes across packages. [#309](https://github.com/klauspost/compress/pull/309)
+ * s2: Add 'best' compression option. [#310](https://github.com/klauspost/compress/pull/310)
+ * s2: Add ReaderMaxBlockSize, changes `s2.NewReader` signature to include varargs. [#311](https://github.com/klauspost/compress/pull/311)
+ * s2: Fix crash on small better buffers. [#308](https://github.com/klauspost/compress/pull/308)
+ * s2: Clean up decoder. [#312](https://github.com/klauspost/compress/pull/312)
+
+* Jan 7, 2021 (v1.11.6)
+ * zstd: Make decoder allocations smaller [#306](https://github.com/klauspost/compress/pull/306)
+ * zstd: Free Decoder resources when Reset is called with a nil io.Reader [#305](https://github.com/klauspost/compress/pull/305)
+
+* Dec 20, 2020 (v1.11.4)
+ * zstd: Add Best compression mode [#304](https://github.com/klauspost/compress/pull/304)
+ * Add header decoder [#299](https://github.com/klauspost/compress/pull/299)
+ * s2: Add uncompressed stream option [#297](https://github.com/klauspost/compress/pull/297)
+ * Simplify/speed up small blocks with known max size. [#300](https://github.com/klauspost/compress/pull/300)
+ * zstd: Always reset literal dict encoder [#303](https://github.com/klauspost/compress/pull/303)
+
+* Nov 15, 2020 (v1.11.3)
+ * inflate: 10-15% faster decompression [#293](https://github.com/klauspost/compress/pull/293)
+ * zstd: Tweak DecodeAll default allocation [#295](https://github.com/klauspost/compress/pull/295)
+
+* Oct 11, 2020 (v1.11.2)
+ * s2: Fix out of bounds read in "better" block compression [#291](https://github.com/klauspost/compress/pull/291)
+
+* Oct 1, 2020 (v1.11.1)
+ * zstd: Set allLitEntropy true in default configuration [#286](https://github.com/klauspost/compress/pull/286)
+
+* Sept 8, 2020 (v1.11.0)
+ * zstd: Add experimental compression [dictionaries](https://github.com/klauspost/compress/tree/master/zstd#dictionaries) [#281](https://github.com/klauspost/compress/pull/281)
+ * zstd: Fix mixed Write and ReadFrom calls [#282](https://github.com/klauspost/compress/pull/282)
+ * inflate/gz: Limit variable shifts, ~5% faster decompression [#274](https://github.com/klauspost/compress/pull/274)
+
+
+
+ See changes to v1.10.x
+
+* July 8, 2020 (v1.10.11)
+ * zstd: Fix extra block when compressing with ReadFrom. [#278](https://github.com/klauspost/compress/pull/278)
+ * huff0: Also populate compression table when reading decoding table. [#275](https://github.com/klauspost/compress/pull/275)
+
+* June 23, 2020 (v1.10.10)
+ * zstd: Skip entropy compression in fastest mode when no matches. [#270](https://github.com/klauspost/compress/pull/270)
+
+* June 16, 2020 (v1.10.9):
+ * zstd: API change for specifying dictionaries. See [#268](https://github.com/klauspost/compress/pull/268)
+ * zip: update CreateHeaderRaw to handle zip64 fields. [#266](https://github.com/klauspost/compress/pull/266)
+ * Fuzzit tests removed. The service has been purchased and is no longer available.
+
+* June 5, 2020 (v1.10.8):
+ * 1.15x faster zstd block decompression. [#265](https://github.com/klauspost/compress/pull/265)
+
+* June 1, 2020 (v1.10.7):
+ * Added zstd decompression [dictionary support](https://github.com/klauspost/compress/tree/master/zstd#dictionaries)
+ * Increase zstd decompression speed up to 1.19x. [#259](https://github.com/klauspost/compress/pull/259)
+ * Remove internal reset call in zstd compression and reduce allocations. [#263](https://github.com/klauspost/compress/pull/263)
+
+* May 21, 2020: (v1.10.6)
+ * zstd: Reduce allocations while decoding. [#258](https://github.com/klauspost/compress/pull/258), [#252](https://github.com/klauspost/compress/pull/252)
+ * zstd: Stricter decompression checks.
+
+* April 12, 2020: (v1.10.5)
+ * s2-commands: Flush output when receiving SIGINT. [#239](https://github.com/klauspost/compress/pull/239)
+
+* Apr 8, 2020: (v1.10.4)
+ * zstd: Minor/special case optimizations. [#251](https://github.com/klauspost/compress/pull/251), [#250](https://github.com/klauspost/compress/pull/250), [#249](https://github.com/klauspost/compress/pull/249), [#247](https://github.com/klauspost/compress/pull/247)
+* Mar 11, 2020: (v1.10.3)
+ * s2: Use S2 encoder in pure Go mode for Snappy output as well. [#245](https://github.com/klauspost/compress/pull/245)
+ * s2: Fix pure Go block encoder. [#244](https://github.com/klauspost/compress/pull/244)
+ * zstd: Added "better compression" mode. [#240](https://github.com/klauspost/compress/pull/240)
+ * zstd: Improve speed of fastest compression mode by 5-10% [#241](https://github.com/klauspost/compress/pull/241)
+ * zstd: Skip creating encoders when not needed. [#238](https://github.com/klauspost/compress/pull/238)
+
+* Feb 27, 2020: (v1.10.2)
+ * Close to 50% speedup in inflate (gzip/zip decompression). [#236](https://github.com/klauspost/compress/pull/236) [#234](https://github.com/klauspost/compress/pull/234) [#232](https://github.com/klauspost/compress/pull/232)
+ * Reduce deflate level 1-6 memory usage up to 59%. [#227](https://github.com/klauspost/compress/pull/227)
+
+* Feb 18, 2020: (v1.10.1)
+ * Fix zstd crash when resetting multiple times without sending data. [#226](https://github.com/klauspost/compress/pull/226)
+ * deflate: Fix dictionary use on level 1-6. [#224](https://github.com/klauspost/compress/pull/224)
+ * Remove deflate writer reference when closing. [#224](https://github.com/klauspost/compress/pull/224)
+
+* Feb 4, 2020: (v1.10.0)
+ * Add optional dictionary to [stateless deflate](https://pkg.go.dev/github.com/klauspost/compress/flate?tab=doc#StatelessDeflate). Breaking change, send `nil` for previous behaviour. [#216](https://github.com/klauspost/compress/pull/216)
+ * Fix buffer overflow on repeated small block deflate. [#218](https://github.com/klauspost/compress/pull/218)
+ * Allow copying content from an existing ZIP file without decompressing+compressing. [#214](https://github.com/klauspost/compress/pull/214)
+ * Added [S2](https://github.com/klauspost/compress/tree/master/s2#s2-compression) AMD64 assembler and various optimizations. Stream speed >10GB/s. [#186](https://github.com/klauspost/compress/pull/186)
+
+
+
+
+ See changes prior to v1.10.0
+
+* Jan 20,2020 (v1.9.8) Optimize gzip/deflate with better size estimates and faster table generation. [#207](https://github.com/klauspost/compress/pull/207) by [luyu6056](https://github.com/luyu6056), [#206](https://github.com/klauspost/compress/pull/206).
+* Jan 11, 2020: S2 Encode/Decode will use provided buffer if capacity is big enough. [#204](https://github.com/klauspost/compress/pull/204)
+* Jan 5, 2020: (v1.9.7) Fix another zstd regression in v1.9.5 - v1.9.6 removed.
+* Jan 4, 2020: (v1.9.6) Regression in v1.9.5 fixed causing corrupt zstd encodes in rare cases.
+* Jan 4, 2020: Faster IO in [s2c + s2d commandline tools](https://github.com/klauspost/compress/tree/master/s2#commandline-tools) compression/decompression. [#192](https://github.com/klauspost/compress/pull/192)
+* Dec 29, 2019: Removed v1.9.5 since fuzz tests showed a compatibility problem with the reference zstandard decoder.
+* Dec 29, 2019: (v1.9.5) zstd: 10-20% faster block compression. [#199](https://github.com/klauspost/compress/pull/199)
+* Dec 29, 2019: [zip](https://godoc.org/github.com/klauspost/compress/zip) package updated with latest Go features
+* Dec 29, 2019: zstd: Single segment flag condintions tweaked. [#197](https://github.com/klauspost/compress/pull/197)
+* Dec 18, 2019: s2: Faster compression when ReadFrom is used. [#198](https://github.com/klauspost/compress/pull/198)
+* Dec 10, 2019: s2: Fix repeat length output when just above at 16MB limit.
+* Dec 10, 2019: zstd: Add function to get decoder as io.ReadCloser. [#191](https://github.com/klauspost/compress/pull/191)
+* Dec 3, 2019: (v1.9.4) S2: limit max repeat length. [#188](https://github.com/klauspost/compress/pull/188)
+* Dec 3, 2019: Add [WithNoEntropyCompression](https://godoc.org/github.com/klauspost/compress/zstd#WithNoEntropyCompression) to zstd [#187](https://github.com/klauspost/compress/pull/187)
+* Dec 3, 2019: Reduce memory use for tests. Check for leaked goroutines.
+* Nov 28, 2019 (v1.9.3) Less allocations in stateless deflate.
+* Nov 28, 2019: 5-20% Faster huff0 decode. Impacts zstd as well. [#184](https://github.com/klauspost/compress/pull/184)
+* Nov 12, 2019 (v1.9.2) Added [Stateless Compression](#stateless-compression) for gzip/deflate.
+* Nov 12, 2019: Fixed zstd decompression of large single blocks. [#180](https://github.com/klauspost/compress/pull/180)
+* Nov 11, 2019: Set default [s2c](https://github.com/klauspost/compress/tree/master/s2#commandline-tools) block size to 4MB.
+* Nov 11, 2019: Reduce inflate memory use by 1KB.
+* Nov 10, 2019: Less allocations in deflate bit writer.
+* Nov 10, 2019: Fix inconsistent error returned by zstd decoder.
+* Oct 28, 2019 (v1.9.1) ztsd: Fix crash when compressing blocks. [#174](https://github.com/klauspost/compress/pull/174)
+* Oct 24, 2019 (v1.9.0) zstd: Fix rare data corruption [#173](https://github.com/klauspost/compress/pull/173)
+* Oct 24, 2019 zstd: Fix huff0 out of buffer write [#171](https://github.com/klauspost/compress/pull/171) and always return errors [#172](https://github.com/klauspost/compress/pull/172)
+* Oct 10, 2019: Big deflate rewrite, 30-40% faster with better compression [#105](https://github.com/klauspost/compress/pull/105)
+
+
+
+
+ See changes prior to v1.9.0
+
+* Oct 10, 2019: (v1.8.6) zstd: Allow partial reads to get flushed data. [#169](https://github.com/klauspost/compress/pull/169)
+* Oct 3, 2019: Fix inconsistent results on broken zstd streams.
+* Sep 25, 2019: Added `-rm` (remove source files) and `-q` (no output except errors) to `s2c` and `s2d` [commands](https://github.com/klauspost/compress/tree/master/s2#commandline-tools)
+* Sep 16, 2019: (v1.8.4) Add `s2c` and `s2d` [commandline tools](https://github.com/klauspost/compress/tree/master/s2#commandline-tools).
+* Sep 10, 2019: (v1.8.3) Fix s2 decoder [Skip](https://godoc.org/github.com/klauspost/compress/s2#Reader.Skip).
+* Sep 7, 2019: zstd: Added [WithWindowSize](https://godoc.org/github.com/klauspost/compress/zstd#WithWindowSize), contributed by [ianwilkes](https://github.com/ianwilkes).
+* Sep 5, 2019: (v1.8.2) Add [WithZeroFrames](https://godoc.org/github.com/klauspost/compress/zstd#WithZeroFrames) which adds full zero payload block encoding option.
+* Sep 5, 2019: Lazy initialization of zstandard predefined en/decoder tables.
+* Aug 26, 2019: (v1.8.1) S2: 1-2% compression increase in "better" compression mode.
+* Aug 26, 2019: zstd: Check maximum size of Huffman 1X compressed literals while decoding.
+* Aug 24, 2019: (v1.8.0) Added [S2 compression](https://github.com/klauspost/compress/tree/master/s2#s2-compression), a high performance replacement for Snappy.
+* Aug 21, 2019: (v1.7.6) Fixed minor issues found by fuzzer. One could lead to zstd not decompressing.
+* Aug 18, 2019: Add [fuzzit](https://fuzzit.dev/) continuous fuzzing.
+* Aug 14, 2019: zstd: Skip incompressible data 2x faster. [#147](https://github.com/klauspost/compress/pull/147)
+* Aug 4, 2019 (v1.7.5): Better literal compression. [#146](https://github.com/klauspost/compress/pull/146)
+* Aug 4, 2019: Faster zstd compression. [#143](https://github.com/klauspost/compress/pull/143) [#144](https://github.com/klauspost/compress/pull/144)
+* Aug 4, 2019: Faster zstd decompression. [#145](https://github.com/klauspost/compress/pull/145) [#143](https://github.com/klauspost/compress/pull/143) [#142](https://github.com/klauspost/compress/pull/142)
+* July 15, 2019 (v1.7.4): Fix double EOF block in rare cases on zstd encoder.
+* July 15, 2019 (v1.7.3): Minor speedup/compression increase in default zstd encoder.
+* July 14, 2019: zstd decoder: Fix decompression error on multiple uses with mixed content.
+* July 7, 2019 (v1.7.2): Snappy update, zstd decoder potential race fix.
+* June 17, 2019: zstd decompression bugfix.
+* June 17, 2019: fix 32 bit builds.
+* June 17, 2019: Easier use in modules (less dependencies).
+* June 9, 2019: New stronger "default" [zstd](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression mode. Matches zstd default compression ratio.
+* June 5, 2019: 20-40% throughput in [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and better compression.
+* June 5, 2019: deflate/gzip compression: Reduce memory usage of lower compression levels.
+* June 2, 2019: Added [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression!
+* May 25, 2019: deflate/gzip: 10% faster bit writer, mostly visible in lower levels.
+* Apr 22, 2019: [zstd](https://github.com/klauspost/compress/tree/master/zstd#zstd) decompression added.
+* Aug 1, 2018: Added [huff0 README](https://github.com/klauspost/compress/tree/master/huff0#huff0-entropy-compression).
+* Jul 8, 2018: Added [Performance Update 2018](#performance-update-2018) below.
+* Jun 23, 2018: Merged [Go 1.11 inflate optimizations](https://go-review.googlesource.com/c/go/+/102235). Go 1.9 is now required. Backwards compatible version tagged with [v1.3.0](https://github.com/klauspost/compress/releases/tag/v1.3.0).
+* Apr 2, 2018: Added [huff0](https://godoc.org/github.com/klauspost/compress/huff0) en/decoder. Experimental for now, API may change.
+* Mar 4, 2018: Added [FSE Entropy](https://godoc.org/github.com/klauspost/compress/fse) en/decoder. Experimental for now, API may change.
+* Nov 3, 2017: Add compression [Estimate](https://godoc.org/github.com/klauspost/compress#Estimate) function.
+* May 28, 2017: Reduce allocations when resetting decoder.
+* Apr 02, 2017: Change back to official crc32, since changes were merged in Go 1.7.
+* Jan 14, 2017: Reduce stack pressure due to array copies. See [Issue #18625](https://github.com/golang/go/issues/18625).
+* Oct 25, 2016: Level 2-4 have been rewritten and now offers significantly better performance than before.
+* Oct 20, 2016: Port zlib changes from Go 1.7 to fix zlib writer issue. Please update.
+* Oct 16, 2016: Go 1.7 changes merged. Apples to apples this package is a few percent faster, but has a significantly better balance between speed and compression per level.
+* Mar 24, 2016: Always attempt Huffman encoding on level 4-7. This improves base 64 encoded data compression.
+* Mar 24, 2016: Small speedup for level 1-3.
+* Feb 19, 2016: Faster bit writer, level -2 is 15% faster, level 1 is 4% faster.
+* Feb 19, 2016: Handle small payloads faster in level 1-3.
+* Feb 19, 2016: Added faster level 2 + 3 compression modes.
+* Feb 19, 2016: [Rebalanced compression levels](https://blog.klauspost.com/rebalancing-deflate-compression-levels/), so there is a more even progression in terms of compression. New default level is 5.
+* Feb 14, 2016: Snappy: Merge upstream changes.
+* Feb 14, 2016: Snappy: Fix aggressive skipping.
+* Feb 14, 2016: Snappy: Update benchmark.
+* Feb 13, 2016: Deflate: Fixed assembler problem that could lead to sub-optimal compression.
+* Feb 12, 2016: Snappy: Added AMD64 SSE 4.2 optimizations to matching, which makes easy to compress material run faster. Typical speedup is around 25%.
+* Feb 9, 2016: Added Snappy package fork. This version is 5-7% faster, much more on hard to compress content.
+* Jan 30, 2016: Optimize level 1 to 3 by not considering static dictionary or storing uncompressed. ~4-5% speedup.
+* Jan 16, 2016: Optimization on deflate level 1,2,3 compression.
+* Jan 8 2016: Merge [CL 18317](https://go-review.googlesource.com/#/c/18317): fix reading, writing of zip64 archives.
+* Dec 8 2015: Make level 1 and -2 deterministic even if write size differs.
+* Dec 8 2015: Split encoding functions, so hashing and matching can potentially be inlined. 1-3% faster on AMD64. 5% faster on other platforms.
+* Dec 8 2015: Fixed rare [one byte out-of bounds read](https://github.com/klauspost/compress/issues/20). Please update!
+* Nov 23 2015: Optimization on token writer. ~2-4% faster. Contributed by [@dsnet](https://github.com/dsnet).
+* Nov 20 2015: Small optimization to bit writer on 64 bit systems.
+* Nov 17 2015: Fixed out-of-bound errors if the underlying Writer returned an error. See [#15](https://github.com/klauspost/compress/issues/15).
+* Nov 12 2015: Added [io.WriterTo](https://golang.org/pkg/io/#WriterTo) support to gzip/inflate.
+* Nov 11 2015: Merged [CL 16669](https://go-review.googlesource.com/#/c/16669/4): archive/zip: enable overriding (de)compressors per file
+* Oct 15 2015: Added skipping on uncompressible data. Random data speed up >5x.
+
+
+
+# deflate usage
+
+The packages are drop-in replacements for standard library [deflate](https://godoc.org/github.com/klauspost/compress/flate), [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip), and [zlib](https://godoc.org/github.com/klauspost/compress/zlib). Simply replace the import path to use them:
+
+Typical speed is about 2x of the standard library packages.
+
+| old import | new import | Documentation |
+|------------------|---------------------------------------|-------------------------------------------------------------------------|
+| `compress/gzip` | `github.com/klauspost/compress/gzip` | [gzip](https://pkg.go.dev/github.com/klauspost/compress/gzip?tab=doc) |
+| `compress/zlib` | `github.com/klauspost/compress/zlib` | [zlib](https://pkg.go.dev/github.com/klauspost/compress/zlib?tab=doc) |
+| `archive/zip` | `github.com/klauspost/compress/zip` | [zip](https://pkg.go.dev/github.com/klauspost/compress/zip?tab=doc) |
+| `compress/flate` | `github.com/klauspost/compress/flate` | [flate](https://pkg.go.dev/github.com/klauspost/compress/flate?tab=doc) |
+
+You may also be interested in [pgzip](https://github.com/klauspost/pgzip), which is a drop-in replacement for gzip, which support multithreaded compression on big files and the optimized [crc32](https://github.com/klauspost/crc32) package used by these packages.
+
+The packages implement the same API as the standard library, so you can use the original godoc documentation: [gzip](http://golang.org/pkg/compress/gzip/), [zip](http://golang.org/pkg/archive/zip/), [zlib](http://golang.org/pkg/compress/zlib/), [flate](http://golang.org/pkg/compress/flate/).
+
+Currently there is only minor speedup on decompression (mostly CRC32 calculation).
+
+Memory usage is typically 1MB for a Writer. stdlib is in the same range.
+If you expect to have a lot of concurrently allocated Writers consider using
+the stateless compression described below.
+
+For compression performance, see: [this spreadsheet](https://docs.google.com/spreadsheets/d/1nuNE2nPfuINCZJRMt6wFWhKpToF95I47XjSsc-1rbPQ/edit?usp=sharing).
+
+To disable all assembly add `-tags=noasm`. This works across all packages.
+
+# Stateless compression
+
+This package offers stateless compression as a special option for gzip/deflate.
+It will do compression but without maintaining any state between Write calls.
+
+This means there will be no memory kept between Write calls, but compression and speed will be suboptimal.
+
+This is only relevant in cases where you expect to run many thousands of compressors concurrently,
+but with very little activity. This is *not* intended for regular web servers serving individual requests.
+
+Because of this, the size of actual Write calls will affect output size.
+
+In gzip, specify level `-3` / `gzip.StatelessCompression` to enable.
+
+For direct deflate use, NewStatelessWriter and StatelessDeflate are available. See [documentation](https://godoc.org/github.com/klauspost/compress/flate#NewStatelessWriter)
+
+A `bufio.Writer` can of course be used to control write sizes. For example, to use a 4KB buffer:
+
+```go
+ // replace 'ioutil.Discard' with your output.
+ gzw, err := gzip.NewWriterLevel(ioutil.Discard, gzip.StatelessCompression)
+ if err != nil {
+ return err
+ }
+ defer gzw.Close()
+
+ w := bufio.NewWriterSize(gzw, 4096)
+ defer w.Flush()
+
+ // Write to 'w'
+```
+
+This will only use up to 4KB in memory when the writer is idle.
+
+Compression is almost always worse than the fastest compression level
+and each write will allocate (a little) memory.
+
+
+# Other packages
+
+Here are other packages of good quality and pure Go (no cgo wrappers or autoconverted code):
+
+* [github.com/pierrec/lz4](https://github.com/pierrec/lz4) - strong multithreaded LZ4 compression.
+* [github.com/cosnicolaou/pbzip2](https://github.com/cosnicolaou/pbzip2) - multithreaded bzip2 decompression.
+* [github.com/dsnet/compress](https://github.com/dsnet/compress) - brotli decompression, bzip2 writer.
+* [github.com/ronanh/intcomp](https://github.com/ronanh/intcomp) - Integer compression.
+* [github.com/spenczar/fpc](https://github.com/spenczar/fpc) - Float compression.
+* [github.com/minio/zipindex](https://github.com/minio/zipindex) - External ZIP directory index.
+* [github.com/ybirader/pzip](https://github.com/ybirader/pzip) - Fast concurrent zip archiver and extractor.
+
+# license
+
+This code is licensed under the same conditions as the original Go code. See LICENSE file.
+
+
+
+
+
diff --git a/vendor/github.com/klauspost/compress/fse/README.md b/vendor/github.com/klauspost/compress/fse/README.md
index ea7324da67..27d8ed56fc 100644
--- a/vendor/github.com/klauspost/compress/fse/README.md
+++ b/vendor/github.com/klauspost/compress/fse/README.md
@@ -1,79 +1,79 @@
-# Finite State Entropy
-
-This package provides Finite State Entropy encoding and decoding.
-
-Finite State Entropy (also referenced as [tANS](https://en.wikipedia.org/wiki/Asymmetric_numeral_systems#tANS))
-encoding provides a fast near-optimal symbol encoding/decoding
-for byte blocks as implemented in [zstandard](https://github.com/facebook/zstd).
-
-This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
-This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
-but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
-
-* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/fse)
-
-## News
-
- * Feb 2018: First implementation released. Consider this beta software for now.
-
-# Usage
-
-This package provides a low level interface that allows to compress single independent blocks.
-
-Each block is separate, and there is no built in integrity checks.
-This means that the caller should keep track of block sizes and also do checksums if needed.
-
-Compressing a block is done via the [`Compress`](https://godoc.org/github.com/klauspost/compress/fse#Compress) function.
-You must provide input and will receive the output and maybe an error.
-
-These error values can be returned:
-
-| Error | Description |
-|---------------------|-----------------------------------------------------------------------------|
-| `` | Everything ok, output is returned |
-| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
-| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
-| `(error)` | An internal error occurred. |
-
-As can be seen above there are errors that will be returned even under normal operation so it is important to handle these.
-
-To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/fse#Scratch) object
-that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
-object can be used for both.
-
-Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
-you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
-
-Decompressing is done by calling the [`Decompress`](https://godoc.org/github.com/klauspost/compress/fse#Decompress) function.
-You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
-your input was likely corrupted.
-
-It is important to note that a successful decoding does *not* mean your output matches your original input.
-There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
-
-For more detailed usage, see examples in the [godoc documentation](https://godoc.org/github.com/klauspost/compress/fse#pkg-examples).
-
-# Performance
-
-A lot of factors are affecting speed. Block sizes and compressibility of the material are primary factors.
-All compression functions are currently only running on the calling goroutine so only one core will be used per block.
-
-The compressor is significantly faster if symbols are kept as small as possible. The highest byte value of the input
-is used to reduce some of the processing, so if all your input is above byte value 64 for instance, it may be
-beneficial to transpose all your input values down by 64.
-
-With moderate block sizes around 64k speed are typically 200MB/s per core for compression and
-around 300MB/s decompression speed.
-
-The same hardware typically does Huffman (deflate) encoding at 125MB/s and decompression at 100MB/s.
-
-# Plans
-
-At one point, more internals will be exposed to facilitate more "expert" usage of the components.
-
-A streaming interface is also likely to be implemented. Likely compatible with [FSE stream format](https://github.com/Cyan4973/FiniteStateEntropy/blob/dev/programs/fileio.c#L261).
-
-# Contributing
-
-Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
+# Finite State Entropy
+
+This package provides Finite State Entropy encoding and decoding.
+
+Finite State Entropy (also referenced as [tANS](https://en.wikipedia.org/wiki/Asymmetric_numeral_systems#tANS))
+encoding provides a fast near-optimal symbol encoding/decoding
+for byte blocks as implemented in [zstandard](https://github.com/facebook/zstd).
+
+This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
+This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
+but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
+
+* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/fse)
+
+## News
+
+ * Feb 2018: First implementation released. Consider this beta software for now.
+
+# Usage
+
+This package provides a low level interface that allows to compress single independent blocks.
+
+Each block is separate, and there is no built in integrity checks.
+This means that the caller should keep track of block sizes and also do checksums if needed.
+
+Compressing a block is done via the [`Compress`](https://godoc.org/github.com/klauspost/compress/fse#Compress) function.
+You must provide input and will receive the output and maybe an error.
+
+These error values can be returned:
+
+| Error | Description |
+|---------------------|-----------------------------------------------------------------------------|
+| `` | Everything ok, output is returned |
+| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
+| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
+| `(error)` | An internal error occurred. |
+
+As can be seen above there are errors that will be returned even under normal operation so it is important to handle these.
+
+To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/fse#Scratch) object
+that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
+object can be used for both.
+
+Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
+you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
+
+Decompressing is done by calling the [`Decompress`](https://godoc.org/github.com/klauspost/compress/fse#Decompress) function.
+You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
+your input was likely corrupted.
+
+It is important to note that a successful decoding does *not* mean your output matches your original input.
+There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
+
+For more detailed usage, see examples in the [godoc documentation](https://godoc.org/github.com/klauspost/compress/fse#pkg-examples).
+
+# Performance
+
+A lot of factors are affecting speed. Block sizes and compressibility of the material are primary factors.
+All compression functions are currently only running on the calling goroutine so only one core will be used per block.
+
+The compressor is significantly faster if symbols are kept as small as possible. The highest byte value of the input
+is used to reduce some of the processing, so if all your input is above byte value 64 for instance, it may be
+beneficial to transpose all your input values down by 64.
+
+With moderate block sizes around 64k speed are typically 200MB/s per core for compression and
+around 300MB/s decompression speed.
+
+The same hardware typically does Huffman (deflate) encoding at 125MB/s and decompression at 100MB/s.
+
+# Plans
+
+At one point, more internals will be exposed to facilitate more "expert" usage of the components.
+
+A streaming interface is also likely to be implemented. Likely compatible with [FSE stream format](https://github.com/Cyan4973/FiniteStateEntropy/blob/dev/programs/fileio.c#L261).
+
+# Contributing
+
+Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
changes will likely not be accepted. If in doubt open an issue before writing the PR.
\ No newline at end of file
diff --git a/vendor/github.com/klauspost/compress/huff0/README.md b/vendor/github.com/klauspost/compress/huff0/README.md
index 8b6e5c6638..26d5101b36 100644
--- a/vendor/github.com/klauspost/compress/huff0/README.md
+++ b/vendor/github.com/klauspost/compress/huff0/README.md
@@ -1,89 +1,89 @@
-# Huff0 entropy compression
-
-This package provides Huff0 encoding and decoding as used in zstd.
-
-[Huff0](https://github.com/Cyan4973/FiniteStateEntropy#new-generation-entropy-coders),
-a Huffman codec designed for modern CPU, featuring OoO (Out of Order) operations on multiple ALU
-(Arithmetic Logic Unit), achieving extremely fast compression and decompression speeds.
-
-This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
-This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
-but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
-
-* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/huff0)
-
-## News
-
-This is used as part of the [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression package.
-
-This ensures that most functionality is well tested.
-
-# Usage
-
-This package provides a low level interface that allows to compress single independent blocks.
-
-Each block is separate, and there is no built in integrity checks.
-This means that the caller should keep track of block sizes and also do checksums if needed.
-
-Compressing a block is done via the [`Compress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress1X) and
-[`Compress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress4X) functions.
-You must provide input and will receive the output and maybe an error.
-
-These error values can be returned:
-
-| Error | Description |
-|---------------------|-----------------------------------------------------------------------------|
-| `` | Everything ok, output is returned |
-| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
-| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
-| `ErrTooBig` | Returned if the input block exceeds the maximum allowed size (128 Kib) |
-| `(error)` | An internal error occurred. |
-
-
-As can be seen above some of there are errors that will be returned even under normal operation so it is important to handle these.
-
-To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object
-that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
-object can be used for both.
-
-Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
-you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
-
-The `Scratch` object will retain state that allows to re-use previous tables for encoding and decoding.
-
-## Tables and re-use
-
-Huff0 allows for reusing tables from the previous block to save space if that is expected to give better/faster results.
-
-The Scratch object allows you to set a [`ReusePolicy`](https://godoc.org/github.com/klauspost/compress/huff0#ReusePolicy)
-that controls this behaviour. See the documentation for details. This can be altered between each block.
-
-Do however note that this information is *not* stored in the output block and it is up to the users of the package to
-record whether [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable) should be called,
-based on the boolean reported back from the CompressXX call.
-
-If you want to store the table separate from the data, you can access them as `OutData` and `OutTable` on the
-[`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object.
-
-## Decompressing
-
-The first part of decoding is to initialize the decoding table through [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable).
-This will initialize the decoding tables.
-You can supply the complete block to `ReadTable` and it will return the data part of the block
-which can be given to the decompressor.
-
-Decompressing is done by calling the [`Decompress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress1X)
-or [`Decompress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress4X) function.
-
-For concurrently decompressing content with a fixed table a stateless [`Decoder`](https://godoc.org/github.com/klauspost/compress/huff0#Decoder) can be requested which will remain correct as long as the scratch is unchanged. The capacity of the provided slice indicates the expected output size.
-
-You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
-your input was likely corrupted.
-
-It is important to note that a successful decoding does *not* mean your output matches your original input.
-There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
-
-# Contributing
-
-Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
-changes will likely not be accepted. If in doubt open an issue before writing the PR.
+# Huff0 entropy compression
+
+This package provides Huff0 encoding and decoding as used in zstd.
+
+[Huff0](https://github.com/Cyan4973/FiniteStateEntropy#new-generation-entropy-coders),
+a Huffman codec designed for modern CPU, featuring OoO (Out of Order) operations on multiple ALU
+(Arithmetic Logic Unit), achieving extremely fast compression and decompression speeds.
+
+This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
+This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
+but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
+
+* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/huff0)
+
+## News
+
+This is used as part of the [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression package.
+
+This ensures that most functionality is well tested.
+
+# Usage
+
+This package provides a low level interface that allows to compress single independent blocks.
+
+Each block is separate, and there is no built in integrity checks.
+This means that the caller should keep track of block sizes and also do checksums if needed.
+
+Compressing a block is done via the [`Compress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress1X) and
+[`Compress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress4X) functions.
+You must provide input and will receive the output and maybe an error.
+
+These error values can be returned:
+
+| Error | Description |
+|---------------------|-----------------------------------------------------------------------------|
+| `` | Everything ok, output is returned |
+| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
+| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
+| `ErrTooBig` | Returned if the input block exceeds the maximum allowed size (128 Kib) |
+| `(error)` | An internal error occurred. |
+
+
+As can be seen above some of there are errors that will be returned even under normal operation so it is important to handle these.
+
+To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object
+that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
+object can be used for both.
+
+Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
+you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
+
+The `Scratch` object will retain state that allows to re-use previous tables for encoding and decoding.
+
+## Tables and re-use
+
+Huff0 allows for reusing tables from the previous block to save space if that is expected to give better/faster results.
+
+The Scratch object allows you to set a [`ReusePolicy`](https://godoc.org/github.com/klauspost/compress/huff0#ReusePolicy)
+that controls this behaviour. See the documentation for details. This can be altered between each block.
+
+Do however note that this information is *not* stored in the output block and it is up to the users of the package to
+record whether [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable) should be called,
+based on the boolean reported back from the CompressXX call.
+
+If you want to store the table separate from the data, you can access them as `OutData` and `OutTable` on the
+[`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object.
+
+## Decompressing
+
+The first part of decoding is to initialize the decoding table through [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable).
+This will initialize the decoding tables.
+You can supply the complete block to `ReadTable` and it will return the data part of the block
+which can be given to the decompressor.
+
+Decompressing is done by calling the [`Decompress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress1X)
+or [`Decompress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress4X) function.
+
+For concurrently decompressing content with a fixed table a stateless [`Decoder`](https://godoc.org/github.com/klauspost/compress/huff0#Decoder) can be requested which will remain correct as long as the scratch is unchanged. The capacity of the provided slice indicates the expected output size.
+
+You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
+your input was likely corrupted.
+
+It is important to note that a successful decoding does *not* mean your output matches your original input.
+There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
+
+# Contributing
+
+Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
+changes will likely not be accepted. If in doubt open an issue before writing the PR.
diff --git a/vendor/github.com/klauspost/compress/s2/decode_amd64.s b/vendor/github.com/klauspost/compress/s2/decode_amd64.s
index 9b105e03c5..1216df78f5 100644
--- a/vendor/github.com/klauspost/compress/s2/decode_amd64.s
+++ b/vendor/github.com/klauspost/compress/s2/decode_amd64.s
@@ -51,7 +51,7 @@
//
// The d variable is implicitly R_DST - R_DBASE, and len(dst)-d is R_DEND - R_DST.
// The s variable is implicitly R_SRC - R_SBASE, and len(src)-s is R_SEND - R_SRC.
-TEXT ·s2Decode(SB), NOSPLIT, $48-56
+TEXT ·s2Decode(SB), NOSPLIT, $56-56
// Initialize R_SRC, R_DST and R_DBASE-R_SEND.
MOVQ dst_base+0(FP), R_DBASE
MOVQ dst_len+8(FP), R_DLEN
diff --git a/vendor/github.com/klauspost/compress/s2/encode_best.go b/vendor/github.com/klauspost/compress/s2/encode_best.go
index c857c5c283..49f2166ec1 100644
--- a/vendor/github.com/klauspost/compress/s2/encode_best.go
+++ b/vendor/github.com/klauspost/compress/s2/encode_best.go
@@ -23,12 +23,12 @@ func encodeBlockBest(dst, src []byte, dict *Dict) (d int) {
// Initialize the hash tables.
const (
// Long hash matches.
- lTableBits = 19
- maxLTableSize = 1 << lTableBits
+ lTableBits = bestLongTableBits
+ maxLTableSize = bestLongTableSize
// Short hash matches.
- sTableBits = 16
- maxSTableSize = 1 << sTableBits
+ sTableBits = bestShortTableBits
+ maxSTableSize = bestShortTableSize
inputMargin = 8 + 2
@@ -44,8 +44,10 @@ func encodeBlockBest(dst, src []byte, dict *Dict) (d int) {
}
sLimitDict := min(len(src)-inputMargin, MaxDictSrcOffset-inputMargin)
- var lTable [maxLTableSize]uint64
- var sTable [maxSTableSize]uint64
+ tbl := getBestTables()
+ lTable := &tbl.lTable
+ sTable := &tbl.sTable
+ defer bestTablePool.Put(tbl)
// Bail if we can't compress to at least this.
dstLimit := len(src) - 5
@@ -456,12 +458,12 @@ func encodeBlockBestSnappy(dst, src []byte) (d int) {
// Initialize the hash tables.
const (
// Long hash matches.
- lTableBits = 19
- maxLTableSize = 1 << lTableBits
+ lTableBits = bestLongTableBits
+ maxLTableSize = bestLongTableSize
// Short hash matches.
- sTableBits = 16
- maxSTableSize = 1 << sTableBits
+ sTableBits = bestShortTableBits
+ maxSTableSize = bestShortTableSize
inputMargin = 8 + 2
)
@@ -474,8 +476,10 @@ func encodeBlockBestSnappy(dst, src []byte) (d int) {
return 0
}
- var lTable [maxLTableSize]uint64
- var sTable [maxSTableSize]uint64
+ tbl := getBestTables()
+ lTable := &tbl.lTable
+ sTable := &tbl.sTable
+ defer bestTablePool.Put(tbl)
// Bail if we can't compress to at least this.
dstLimit := len(src) - 5
diff --git a/vendor/github.com/klauspost/compress/s2/encode_better.go b/vendor/github.com/klauspost/compress/s2/encode_better.go
index 1e30fb7317..adbc57c4d2 100644
--- a/vendor/github.com/klauspost/compress/s2/encode_better.go
+++ b/vendor/github.com/klauspost/compress/s2/encode_better.go
@@ -59,16 +59,18 @@ func encodeBlockBetterGo(dst, src []byte) (d int) {
// Initialize the hash tables.
const (
// Long hash matches.
- lTableBits = 17
- maxLTableSize = 1 << lTableBits
+ lTableBits = betterLongTableBits
+ maxLTableSize = betterLongTableSize
// Short hash matches.
- sTableBits = 14
- maxSTableSize = 1 << sTableBits
+ sTableBits = betterShortTableBits
+ maxSTableSize = betterShortTableSize
)
- var lTable [maxLTableSize]uint32
- var sTable [maxSTableSize]uint32
+ tbl := getBetterTables()
+ lTable := &tbl.lTable
+ sTable := &tbl.sTable
+ defer betterTablePool.Put(tbl)
// Bail if we can't compress to at least this.
dstLimit := len(src) - len(src)>>5 - 6
@@ -317,16 +319,18 @@ func encodeBlockBetterSnappyGo(dst, src []byte) (d int) {
// Initialize the hash tables.
const (
// Long hash matches.
- lTableBits = 16
- maxLTableSize = 1 << lTableBits
+ lTableBits = betterSnappyLongTableBits
+ maxLTableSize = betterSnappyLongTableSize
// Short hash matches.
- sTableBits = 14
- maxSTableSize = 1 << sTableBits
+ sTableBits = betterShortTableBits
+ maxSTableSize = betterShortTableSize
)
- var lTable [maxLTableSize]uint32
- var sTable [maxSTableSize]uint32
+ tbl := getBetterSnappyTables()
+ lTable := &tbl.lTable
+ sTable := &tbl.sTable
+ defer betterSnappyTablePool.Put(tbl)
// Bail if we can't compress to at least this.
dstLimit := len(src) - len(src)>>5 - 6
@@ -902,12 +906,12 @@ func encodeBlockBetterDict(dst, src []byte, dict *Dict) (d int) {
// Initialize the hash tables.
const (
// Long hash matches.
- lTableBits = 17
- maxLTableSize = 1 << lTableBits
+ lTableBits = betterLongTableBits
+ maxLTableSize = betterLongTableSize
// Short hash matches.
- sTableBits = 14
- maxSTableSize = 1 << sTableBits
+ sTableBits = betterShortTableBits
+ maxSTableSize = betterShortTableSize
maxAhead = 8 // maximum bytes ahead without checking sLimit
@@ -921,8 +925,10 @@ func encodeBlockBetterDict(dst, src []byte, dict *Dict) (d int) {
dict.initBetter()
- var lTable [maxLTableSize]uint32
- var sTable [maxSTableSize]uint32
+ tbl := getBetterTables()
+ lTable := &tbl.lTable
+ sTable := &tbl.sTable
+ defer betterTablePool.Put(tbl)
// Bail if we can't compress to at least this.
dstLimit := len(src) - len(src)>>5 - 6
diff --git a/vendor/github.com/klauspost/compress/s2/hashtable_pool.go b/vendor/github.com/klauspost/compress/s2/hashtable_pool.go
new file mode 100644
index 0000000000..bc7cabd5c5
--- /dev/null
+++ b/vendor/github.com/klauspost/compress/s2/hashtable_pool.go
@@ -0,0 +1,65 @@
+package s2
+
+import "sync"
+
+// Table size constants
+const (
+ betterLongTableBits = 17
+ betterLongTableSize = 1 << betterLongTableBits // 131072
+
+ betterShortTableBits = 14
+ betterShortTableSize = 1 << betterShortTableBits // 16384
+
+ betterSnappyLongTableBits = 16
+ betterSnappyLongTableSize = 1 << betterSnappyLongTableBits // 65536
+
+ bestLongTableBits = 19
+ bestLongTableSize = 1 << bestLongTableBits // 524288
+
+ bestShortTableBits = 16
+ bestShortTableSize = 1 << bestShortTableBits // 65536
+)
+
+type betterTables struct {
+ lTable [betterLongTableSize]uint32
+ sTable [betterShortTableSize]uint32
+}
+
+var betterTablePool = sync.Pool{New: func() interface{} { return &betterTables{} }}
+
+// betterSnappyTables holds better-snappy compression hash tables.
+type betterSnappyTables struct {
+ lTable [betterSnappyLongTableSize]uint32
+ sTable [betterShortTableSize]uint32
+}
+
+var betterSnappyTablePool = sync.Pool{New: func() interface{} { return &betterSnappyTables{} }}
+
+// bestTables holds best compression hash tables.
+type bestTables struct {
+ lTable [bestLongTableSize]uint64
+ sTable [bestShortTableSize]uint64
+}
+
+var bestTablePool = sync.Pool{New: func() interface{} { return &bestTables{} }}
+
+// getBetterTables gets a zeroed betterTables from the pool.
+func getBetterTables() *betterTables {
+ t := betterTablePool.Get().(*betterTables)
+ *t = betterTables{}
+ return t
+}
+
+// getBetterSnappyTables gets a zeroed betterSnappyTables from the pool.
+func getBetterSnappyTables() *betterSnappyTables {
+ t := betterSnappyTablePool.Get().(*betterSnappyTables)
+ *t = betterSnappyTables{}
+ return t
+}
+
+// getBestTables gets a zeroed bestTables from the pool.
+func getBestTables() *bestTables {
+ t := bestTablePool.Get().(*bestTables)
+ *t = bestTables{}
+ return t
+}
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/auth.go b/vendor/github.com/nats-io/nats-server/v2/server/auth.go
index 09fad33460..96c17945d6 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/auth.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/auth.go
@@ -1283,7 +1283,7 @@ func checkClientTLSCertSubject(c *client, fn tlsMapAuthFn) bool {
hasEmailAddresses := len(cert.EmailAddresses) > 0
hasSubject := len(cert.Subject.String()) > 0
hasURIs := len(cert.URIs) > 0
- if !hasEmailAddresses && !hasSubject && !hasURIs {
+ if !hasSANs && !hasEmailAddresses && !hasSubject && !hasURIs {
c.Debugf("User required in cert, none found")
return false
}
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/client.go b/vendor/github.com/nats-io/nats-server/v2/server/client.go
index 162c235007..416eab6957 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/client.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/client.go
@@ -270,7 +270,7 @@ type client struct {
mpay int32
msubs int32
mcl int32
- mu sync.Mutex
+ mu sync.RWMutex
cid uint64
start time.Time
nonce []byte
@@ -998,6 +998,7 @@ func (c *client) RegisterUser(user *User) {
// Reset perms to nil in case client previously had them.
c.perms = nil
c.mperms = nil
+ c.darray = nil
} else {
c.setPermissions(user.Permissions)
}
@@ -1035,6 +1036,7 @@ func (c *client) RegisterNkeyUser(user *NkeyUser) error {
// Reset perms to nil in case client previously had them.
c.perms = nil
c.mperms = nil
+ c.darray = nil
} else {
c.setPermissions(user.Permissions)
}
@@ -1061,6 +1063,8 @@ func (c *client) setPermissions(perms *Permissions) {
return
}
c.perms = &permissions{}
+ c.mperms = nil
+ c.darray = nil
slcache := c.srv != nil && !c.srv.getOpts().NoSublistCache
// Loop over publish permissions
@@ -1092,7 +1096,7 @@ func (c *client) setPermissions(perms *Permissions) {
if perms.Subscribe != nil {
var err error
if len(perms.Subscribe.Allow) > 0 {
- c.perms.sub.allow = NewSublist(slcache)
+ c.perms.sub.allow = NewSublistNoCache()
}
for _, subSubject := range perms.Subscribe.Allow {
sub := &subscription{}
@@ -1104,7 +1108,7 @@ func (c *client) setPermissions(perms *Permissions) {
c.perms.sub.allow.Insert(sub)
}
if len(perms.Subscribe.Deny) > 0 {
- c.perms.sub.deny = NewSublist(slcache)
+ c.perms.sub.deny = NewSublistNoCache()
// Also hold onto this array for later.
c.darray = perms.Subscribe.Deny
}
@@ -1201,38 +1205,40 @@ func (c *client) mergeDenyPermissions(what denyType, denyPubs []string) {
if c.perms == nil {
c.perms = &permissions{}
}
- slcache := c.srv != nil && !c.srv.getOpts().NoSublistCache
- var perms []*perm
- switch what {
- case pub:
- perms = []*perm{&c.perms.pub}
- case sub:
- perms = []*perm{&c.perms.sub}
- case both:
- perms = []*perm{&c.perms.pub, &c.perms.sub}
- }
- for _, p := range perms {
- if p.deny == nil {
- p.deny = NewSublist(slcache)
- }
- FOR_DENY:
- for _, subj := range denyPubs {
- r := p.deny.Match(subj)
- for _, v := range r.qsubs {
- for _, s := range v {
- if string(s.subject) == subj {
- continue FOR_DENY
- }
- }
- }
- for _, s := range r.psubs {
+ if what == pub || what == both {
+ if c.perms.pub.deny == nil {
+ c.perms.pub.deny = NewSublistForServer(c.srv)
+ }
+ mergeDenyPerm(&c.perms.pub, denyPubs)
+ }
+ if what == sub || what == both {
+ if c.perms.sub.deny == nil {
+ // Avoid sublist cache contention in canSubscribe.
+ c.perms.sub.deny = NewSublistNoCache()
+ }
+ mergeDenyPerm(&c.perms.sub, denyPubs)
+ }
+}
+
+// mergeDenyPerm inserts new deny permissions, skipping subjects that already exist.
+func mergeDenyPerm(p *perm, denyPubs []string) {
+FOR_DENY:
+ for _, subj := range denyPubs {
+ r := p.deny.Match(subj)
+ for _, v := range r.qsubs {
+ for _, s := range v {
if string(s.subject) == subj {
continue FOR_DENY
}
}
- sub := &subscription{subject: []byte(subj)}
- p.deny.Insert(sub)
}
+ for _, s := range r.psubs {
+ if string(s.subject) == subj {
+ continue FOR_DENY
+ }
+ }
+ sub := &subscription{subject: []byte(subj)}
+ p.deny.Insert(sub)
}
}
@@ -1535,6 +1541,11 @@ func (c *client) readLoop(pre []byte) {
acc.stats.Unlock()
}
+ if c.kind == CLIENT {
+ atomic.AddInt64(&s.inClientMsgs, inMsgs)
+ atomic.AddInt64(&s.inClientBytes, inBytes)
+ }
+
atomic.AddInt64(&s.inMsgs, inMsgs)
atomic.AddInt64(&s.inBytes, inBytes)
}
@@ -2684,6 +2695,12 @@ func (c *client) processPing() {
srv.mu.Lock()
info := srv.copyInfo()
c.mu.Lock()
+ // Keep the in-process tls_required override from the initial INFO,
+ // otherwise this async INFO would flip it back to true.
+ if c.iproc && info.TLSRequired && !c.flags.isSet(didTLSFirst) {
+ info.TLSRequired = false
+ info.TLSAvailable = true
+ }
info.RemoteAccount = c.acc.Name
info.IsSystemAccount = c.acc == srv.SystemAccount()
info.ConnectInfo = true
@@ -3240,9 +3257,9 @@ func (c *client) addShadowSub(sub *subscription, ime *ime) (*subscription, error
return &nsub, nil
}
-// canSubscribe determines if the client is authorized to subscribe to the
-// given subject. Assumes caller is holding lock.
-func (c *client) canSubscribe(subject string, optQueue ...string) bool {
+// canSubscribeInternal determines if the client is authorized to subscribe to
+// the given subject. Assumes caller is holding at least a read lock.
+func (c *client) canSubscribeInternal(subject string, optQueue ...string) bool {
if c.perms == nil {
return true
}
@@ -3287,23 +3304,32 @@ func (c *client) canSubscribe(subject string, optQueue ...string) bool {
// If the queue appears in the deny list, then DO NOT allow.
allowed = !queueMatches(queue, r.qsubs)
}
+ }
+ return allowed
+}
- // We use the actual subscription to signal us to spin up the deny mperms
- // and cache. We check if the subject is a wildcard that intersects any of
- // the deny clauses.
- // FIXME(dlc) - We could be smarter and track when these go away and remove.
- if allowed && c.mperms == nil && subjectHasWildcard(subject) {
- // Whip through the deny array and check if this wildcard subject can
- // overlap with any denied deliveries.
- for _, sub := range c.darray {
- if SubjectsCollide(sub, subject) {
- c.loadMsgDenyFilter()
- break
- }
+// canSubscribe determines if the client is authorized to subscribe to the
+// given subject and initializes the delivery-time deny filter when needed.
+// Assumes caller is holding the write lock.
+func (c *client) canSubscribe(subject string, optQueue ...string) bool {
+ if !c.canSubscribeInternal(subject, optQueue...) {
+ return false
+ }
+ // We use the actual subscription to signal us to spin up the deny mperms
+ // and cache. We check if the subject is a wildcard that intersects any of
+ // the deny clauses.
+ // FIXME(dlc) - We could be smarter and track when these go away and remove.
+ if c.mperms == nil && subjectHasWildcard(subject) {
+ // Whip through the deny array and check if this wildcard subject can
+ // overlap with any denied deliveries.
+ for _, sub := range c.darray {
+ if SubjectsCollide(sub, subject) {
+ c.loadMsgDenyFilter()
+ break
}
}
}
- return allowed
+ return true
}
func queueMatches(queue string, qsubs [][]*subscription) bool {
@@ -4540,7 +4566,8 @@ func removeHeaderIfPrefixPresent(hdr []byte, prefix string) []byte {
}
index += start
if index < 1 || hdr[index-1] != '\n' {
- return hdr
+ index += len(prefix)
+ continue
}
end := bytes.Index(hdr[index+len(prefix):], []byte(_CRLF_))
@@ -5119,6 +5146,7 @@ func (c *client) processMsgResults(acc *Account, r *SublistResult, msg, deliver,
var dlvExtraSize int64
var dlvRouteMsgs int64
var dlvLeafMsgs int64
+ var dlvClientMsgs int64
// We need to know if this is a MQTT producer because they send messages
// without CR_LF (we otherwise remove the size of CR_LF from message size).
@@ -5132,12 +5160,15 @@ func (c *client) processMsgResults(acc *Account, r *SublistResult, msg, deliver,
totalBytes := dlvMsgs*int64(len(msg)) + dlvExtraSize
routeBytes := dlvRouteMsgs*int64(len(msg)) + dlvExtraSize
leafBytes := dlvLeafMsgs*int64(len(msg)) + dlvExtraSize
+ // dlvExtraSize applies to route/leaf header overhead, not client deliveries
+ clientBytes := dlvClientMsgs * int64(len(msg))
// For non MQTT producers, remove the CR_LF * number of messages
if !prodIsMQTT {
totalBytes -= dlvMsgs * int64(LEN_CR_LF)
routeBytes -= dlvRouteMsgs * int64(LEN_CR_LF)
leafBytes -= dlvLeafMsgs * int64(LEN_CR_LF)
+ clientBytes -= dlvClientMsgs * int64(LEN_CR_LF)
}
if acc != nil {
@@ -5158,6 +5189,9 @@ func (c *client) processMsgResults(acc *Account, r *SublistResult, msg, deliver,
if srv := c.srv; srv != nil {
atomic.AddInt64(&srv.outMsgs, dlvMsgs)
atomic.AddInt64(&srv.outBytes, totalBytes)
+
+ atomic.AddInt64(&srv.outClientMsgs, dlvClientMsgs)
+ atomic.AddInt64(&srv.outClientBytes, clientBytes)
}
}
@@ -5253,6 +5287,9 @@ func (c *client) processMsgResults(acc *Account, r *SublistResult, msg, deliver,
// We don't count internal deliveries, so do only when sub.icb is nil.
if sub.icb == nil {
dlvMsgs++
+ if sub.client.kind == CLIENT {
+ dlvClientMsgs++
+ }
}
didDeliver = true
}
@@ -5480,6 +5517,8 @@ func (c *client) processMsgResults(acc *Account, r *SublistResult, msg, deliver,
dlvRouteMsgs++
case LEAF:
dlvLeafMsgs++
+ case CLIENT:
+ dlvClientMsgs++
}
}
// Do the rest even when message delivery was skipped.
@@ -6538,10 +6577,20 @@ func (c *client) doTLSHandshake(typ string, solicit bool, url *url.URL, tlsConfi
if len(subjs) > 0 {
detail = fmt.Sprintf(" (%s)", strings.Join(subjs, "; "))
}
- if kind == CLIENT {
- c.Errorf("TLS handshake error: %v%s", err, detail)
- } else {
+ if kind == ROUTER || kind == GATEWAY {
+ // Always surface these as errors, as these ports shouldn't be behind a load
+ // balancer or regularly probed.
c.Errorf("TLS %s handshake error: %v%s", typ, err, detail)
+ } else {
+ logf := c.Errorf
+ if isClientProbeTLSHandshakeError(err) {
+ logf = c.Debugf
+ }
+ if kind == CLIENT {
+ logf("TLS handshake error: %v%s", err, detail)
+ } else {
+ logf("TLS %s handshake error: %v%s", typ, err, detail)
+ }
}
c.closeConnection(TLSHandshakeError)
@@ -6571,6 +6620,17 @@ func (c *client) doTLSHandshake(typ string, solicit bool, url *url.URL, tlsConfi
return false, err
}
+func isClientProbeTLSHandshakeError(err error) bool {
+ var netErr net.Error
+ if errors.As(err, &netErr) && netErr.Timeout() {
+ return true
+ }
+ var recordHeaderErr tls.RecordHeaderError
+ // Conn is only set by crypto/tls when the invalid record was the peer's
+ // initial handshake bytes, which is the non-TLS probe/load-balancer case.
+ return errors.As(err, &recordHeaderErr) && recordHeaderErr.Conn != nil
+}
+
// getRawAuthUserLock returns the raw auth user for the client.
// Will acquire the client lock.
func (c *client) getRawAuthUserLock() string {
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/const.go b/vendor/github.com/nats-io/nats-server/v2/server/const.go
index b173aa2a78..2bf65247a3 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/const.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/const.go
@@ -66,7 +66,7 @@ func init() {
const (
// VERSION is the current version for the server.
- VERSION = "2.14.0"
+ VERSION = "2.14.1"
// PROTO is the currently supported protocol.
// 0 was the original
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/consumer.go b/vendor/github.com/nats-io/nats-server/v2/server/consumer.go
index dba9517413..43263a45cc 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/consumer.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/consumer.go
@@ -511,6 +511,7 @@ type consumer struct {
retention RetentionPolicy
monitorWg sync.WaitGroup
+ monitorMu sync.Mutex // Serializes monitorWg's Add against Wait to prevent a WaitGroup reuse panic.
inMonitor bool
// R>1 proposals
@@ -1140,7 +1141,10 @@ func (mset *stream) addConsumerWithAssignment(config *ConsumerConfig, oname stri
mset.mu.Unlock()
return nil, NewJSConsumerWQRequiresExplicitAckError()
}
-
+ if config.DeliverPolicy != DeliverAll {
+ mset.mu.Unlock()
+ return nil, NewJSConsumerWQConsumerNotDeliverAllError()
+ }
if mset.numLimitableConsumers() > 0 {
subjects := gatherSubjectFilters(config.FilterSubject, config.FilterSubjects)
if len(subjects) == 0 {
@@ -1168,10 +1172,6 @@ func (mset *stream) addConsumerWithAssignment(config *ConsumerConfig, oname stri
}
}
}
- if config.DeliverPolicy != DeliverAll {
- mset.mu.Unlock()
- return nil, NewJSConsumerWQConsumerNotDeliverAllError()
- }
}
// Set name, which will be durable name if set, otherwise we create one at random.
@@ -1832,11 +1832,14 @@ func (o *consumer) setLeader(isLeader bool) error {
stopAndClearTimer(&o.uptmr)
// Make sure to clear out any re-deliver queues
o.stopAndClearPtmr()
+ o.rdc = nil
o.rdq = nil
o.rdqi.Empty()
o.pending = nil
o.rsm = nil
o.resetPendingDeliveries()
+ // Reset num pending, these are only authoritative on the leader.
+ o.npc, o.npf = 0, 0
// ok if they are nil, we protect inside unsubscribe()
o.unsubscribe(o.ackSubOld)
o.unsubscribe(o.ackSub)
@@ -2138,7 +2141,7 @@ func (o *consumer) deleteNotActive() {
cnaStart := consumerNotActiveStartInterval
o.mu.Lock()
- if o.mset == nil {
+ if o.mset == nil || !o.isLeader() {
o.mu.Unlock()
return
}
@@ -2213,6 +2216,8 @@ func (o *consumer) deleteNotActive() {
s, js := o.mset.srv, o.srv.js.Load()
acc, stream, name, isDirect := o.acc.Name, o.stream, o.name, o.cfg.Direct
+ // Capture our own view of the assignment while we still hold the lock.
+ ca := o.ca
var qch, cqch chan struct{}
if o.srv != nil {
qch = o.srv.quitCh
@@ -2230,9 +2235,6 @@ func (o *consumer) deleteNotActive() {
"consumer": name,
})
- // We will delete locally regardless.
- defer o.delete()
-
// If we are clustered, check if we still have this consumer assigned.
// If we do forward a proposal to delete ourselves to the metacontroller leader.
if !isDirect && s.JetStreamIsClustered() {
@@ -2241,8 +2243,11 @@ func (o *consumer) deleteNotActive() {
meta RaftNode
removeEntry []byte
)
- ca, cc := js.consumerAssignment(acc, stream, name), js.cluster
- if ca != nil && cc != nil {
+ nca := js.consumerAssignment(acc, stream, name)
+ // Only propose the delete if the meta-layer assignment still refers to
+ // the consumer we captured, otherwise we'd be racing a recreated
+ // consumer with the same name.
+ if cc := js.cluster; cc != nil && ca != nil && ca.sameIdentity(nca) {
meta = cc.meta
cca := ca.clone()
cca.Reply = _EMPTY_
@@ -2251,7 +2256,7 @@ func (o *consumer) deleteNotActive() {
}
js.mu.RUnlock()
- if ca != nil && cc != nil {
+ if ca != nil && meta != nil {
// Check to make sure we went away.
// Don't think this needs to be a monitored go routine.
jitter := time.Duration(rand.Int63n(int64(cnaStart)))
@@ -2274,10 +2279,11 @@ func (o *consumer) deleteNotActive() {
js.mu.RUnlock()
return
}
- nca := js.consumerAssignment(acc, stream, name)
- js.mu.RUnlock()
+ nca = js.consumerAssignment(acc, stream, name)
// Make sure this is the same consumer assignment, and not a new consumer with the same name.
- if nca != nil && reflect.DeepEqual(nca, ca) {
+ match := ca.sameIdentity(nca)
+ js.mu.RUnlock()
+ if match {
s.Warnf("Consumer assignment for '%s > %s > %s' not cleaned up, retrying", acc, stream, name)
meta.ForwardProposal(removeEntry)
if interval < cnaMax {
@@ -2290,6 +2296,10 @@ func (o *consumer) deleteNotActive() {
return
}
}
+ } else {
+ // Otherwise, we can delete locally. Either a consumer that's not tracked
+ // by the meta layer (direct), or a standalone non-clustered server.
+ o.delete()
}
}
@@ -2341,7 +2351,9 @@ func (o *consumer) hasMaxDeliveries(seq uint64) bool {
// Make sure to remove from pending.
if p, ok := o.pending[seq]; ok && p != nil {
delete(o.pending, seq)
- o.updateDelivered(p.Sequence, seq, dc, p.Timestamp)
+ // Increment by one, since the delivery count hasn't been increased above.
+ o.updateDelivered(p.Sequence, seq, dc+1, p.Timestamp)
+ o.moveAckFloor(p.Sequence, seq)
}
// Ensure redelivered state is set, if not already.
if o.rdc == nil {
@@ -3245,14 +3257,42 @@ func (o *consumer) ackWait(next time.Duration) time.Duration {
return o.cfg.AckWait + ackWaitDelay
}
-// Due to bug in calculation of sequences on restoring redelivered let's do quick sanity check.
+func (o *consumer) removeRedeliveredBelow(seq uint64) {
+ if seq == 0 {
+ return
+ }
+ o.mu.Lock()
+ for sseq := range o.rdc {
+ if sseq < seq {
+ delete(o.rdc, sseq)
+ o.removeFromRedeliverQueue(sseq)
+ }
+ }
+ o.mu.Unlock()
+
+ if o.store != nil {
+ o.store.RemoveRedeliveredBelow(seq)
+ }
+}
+
+// checkRedelivered drops rdq entries at/below asflr or below stream's first sequence.
+// But rdc is kept until the message leaves the stream: needAck relies on rdc to mark
+// messages past MaxDeliver.
// Lock should be held.
func (o *consumer) checkRedelivered() {
+ if o.mset == nil {
+ return
+ }
+ var ss StreamState
+ o.mset.store.FastState(&ss)
+
var shouldUpdateState bool
for sseq := range o.rdc {
- if sseq <= o.asflr {
- delete(o.rdc, sseq)
+ if sseq <= o.asflr || sseq < ss.FirstSeq {
o.removeFromRedeliverQueue(sseq)
+ }
+ if sseq < ss.FirstSeq {
+ delete(o.rdc, sseq)
shouldUpdateState = true
}
}
@@ -3641,22 +3681,7 @@ func (o *consumer) processAckMsgLocked(sseq, dseq, dc uint64, reply string, doSa
delete(o.pending, sseq)
// Use the original deliver sequence from our pending record.
dseq = p.Sequence
-
- // Only move floors if we matched an existing pending.
- if len(o.pending) == 0 {
- o.adflr = o.dseq - 1
- o.asflr = o.sseq - 1
- } else if dseq == o.adflr+1 {
- o.adflr, o.asflr = dseq, sseq
- for ss := sseq + 1; ss < o.sseq; ss++ {
- if p, ok := o.pending[ss]; ok {
- if p.Sequence > 0 {
- o.adflr, o.asflr = p.Sequence-1, ss-1
- }
- break
- }
- }
- }
+ o.moveAckFloor(dseq, sseq)
}
delete(o.rdc, sseq)
o.removeFromRedeliverQueue(sseq)
@@ -3727,6 +3752,25 @@ func (o *consumer) processAckMsgLocked(sseq, dseq, dc uint64, reply string, doSa
return ackInPlace
}
+// Lock should be held.
+func (o *consumer) moveAckFloor(dseq, sseq uint64) {
+ // Only move floors if we matched an existing pending.
+ if len(o.pending) == 0 {
+ o.adflr = o.dseq - 1
+ o.asflr = o.sseq - 1
+ } else if dseq == o.adflr+1 {
+ o.adflr, o.asflr = dseq, sseq
+ for ss := sseq + 1; ss < o.sseq; ss++ {
+ if p, ok := o.pending[ss]; ok {
+ if p.Sequence > 0 {
+ o.adflr, o.asflr = p.Sequence-1, ss-1
+ }
+ break
+ }
+ }
+ }
+}
+
// Determine if this is a truly filtered consumer. Modern clients will place filtered subjects
// even if the stream only has a single non-wildcard subject designation.
// Read lock should be held.
@@ -4763,7 +4807,9 @@ func (o *consumer) getNextMsg() (*jsPubMsg, uint64, error) {
// Make sure to remove from pending.
if p, ok := o.pending[seq]; ok && p != nil {
delete(o.pending, seq)
+ // The delivery count has already been incremented once.
o.updateDelivered(p.Sequence, seq, dc, p.Timestamp)
+ o.moveAckFloor(p.Sequence, seq)
}
continue
}
@@ -5507,11 +5553,11 @@ func (o *consumer) streamNumPendingLocked() (uint64, error) {
return o.streamNumPending()
}
-// Will force a set from the stream store of num pending.
+// Will force a set from the stream store of num pending on the consumer leader.
// Depends on delivery policy, for last per subject we calculate differently.
// Lock should be held.
func (o *consumer) streamNumPending() (uint64, error) {
- if o.mset == nil || o.mset.store == nil {
+ if o.mset == nil || o.mset.store == nil || !o.isLeader() {
o.npc, o.npf = 0, 0
return 0, nil
}
@@ -6252,7 +6298,10 @@ func (o *consumer) selectStartingSeqNo() error {
o.asflr = o.sseq - 1
// Set our starting sequence state.
// But only if we're not clustered, if clustered we propose upon becoming leader.
- if o.store != nil && o.sseq > 0 && o.cfg.replicas(&o.mset.cfg) == 1 {
+ o.mset.cfgMu.RLock()
+ isR1 := o.cfg.replicas(&o.mset.cfg) == 1
+ o.mset.cfgMu.RUnlock()
+ if o.store != nil && o.sseq > 0 && isR1 {
if err := o.store.SetStarting(o.sseq - 1); err != nil {
return err
}
@@ -6405,9 +6454,9 @@ func (o *consumer) purge(sseq uint64, slseq uint64, isWider bool) {
}
delete(o.pending, seq)
delete(o.rdc, seq)
+ o.updateAcks(p.Sequence, seq, _EMPTY_)
// rdq handled below.
- }
- if isWider && store != nil {
+ } else if isWider && store != nil {
// Our filtered subject, which could be all, is wider than the underlying purge.
// We need to check if the pending items left are still valid.
var smv StoreMsg
@@ -6420,6 +6469,7 @@ func (o *consumer) purge(sseq uint64, slseq uint64, isWider bool) {
}
delete(o.pending, seq)
delete(o.rdc, seq)
+ o.updateAcks(p.Sequence, seq, _EMPTY_)
}
}
}
@@ -6778,6 +6828,10 @@ func (o *consumer) decStreamPending(sseq uint64, subj string) {
var rdc uint64
if wasPending {
rdc = o.deliveryCount(sseq)
+ } else if _, ok := o.rdc[sseq]; ok && o.isLeader() {
+ delete(o.rdc, sseq)
+ // Pass 0 as the delivered sequence to only remove the redelivered state.
+ o.updateAcks(0, sseq, _EMPTY_)
}
o.mu.Unlock()
@@ -6878,14 +6932,21 @@ func gatherSubjectFilters(filter string, filters []string) []string {
// shouldStartMonitor will return true if we should start a monitor
// goroutine or will return false if one is already running.
func (o *consumer) shouldStartMonitor() bool {
- o.mu.Lock()
- defer o.mu.Unlock()
+ // monitorMu is held across the monitorWg.Add below so that it cannot race
+ // a concurrent monitorWg.Wait in stopMonitoring. It is taken before o.mu to
+ // keep a consistent lock ordering.
+ o.monitorMu.Lock()
+ defer o.monitorMu.Unlock()
+ o.mu.Lock()
if o.inMonitor {
+ o.mu.Unlock()
return false
}
- o.monitorWg.Add(1)
o.inMonitor = true
+ o.mu.Unlock()
+
+ o.monitorWg.Add(1)
return true
}
@@ -6901,6 +6962,18 @@ func (o *consumer) clearMonitorRunning() {
}
}
+// stopMonitoring signals any running monitor goroutine to quit and waits for
+// it to fully exit.
+func (o *consumer) stopMonitoring() {
+ // monitorMu is held across both the quit signal and the wait so that a
+ // concurrent shouldStartMonitor cannot slip a new monitor generation in
+ // between.
+ o.monitorMu.Lock()
+ defer o.monitorMu.Unlock()
+ o.signalMonitorQuit()
+ o.monitorWg.Wait()
+}
+
// Test whether we are in the monitor routine.
func (o *consumer) isMonitorRunning() bool {
o.mu.RLock()
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/dirstore.go b/vendor/github.com/nats-io/nats-server/v2/server/dirstore.go
index 6ef11bec7d..0fae234217 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/dirstore.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/dirstore.go
@@ -230,7 +230,7 @@ func (store *DirJWTStore) Pack(maxJWTs int) (string, error) {
}
store.Lock()
err := filepath.Walk(store.directory, func(path string, info os.FileInfo, err error) error {
- if !info.IsDir() && strings.HasSuffix(path, fileExtension) { // this is a JWT
+ if info != nil && !info.IsDir() && strings.HasSuffix(path, fileExtension) { // this is a JWT
if count == maxJWTs { // won't match negative
return nil
}
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/events.go b/vendor/github.com/nats-io/nats-server/v2/server/events.go
index 8bc9bcd511..1c69d0de8e 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/events.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/events.go
@@ -374,7 +374,9 @@ type ServerStats struct {
ActiveAccounts int `json:"active_accounts"`
NumSubs uint32 `json:"subscriptions"`
Sent DataStats `json:"sent"`
+ SentToClients DataStats `json:"sent_to_clients"`
Received DataStats `json:"received"`
+ ReceivedFromClients DataStats `json:"received_from_clients"`
SlowConsumers int64 `json:"slow_consumers"`
SlowConsumersStats *SlowConsumersStats `json:"slow_consumer_stats,omitempty"`
StaleConnections int64 `json:"stale_connections,omitempty"`
@@ -612,7 +614,7 @@ RESET:
// Optional raw header addition.
if pm.hdr != nil {
- b = append(pm.hdr, b...)
+ b = append(pm.hdr[:len(pm.hdr):len(pm.hdr)], b...)
nhdr := len(pm.hdr)
nsize := len(b) - LEN_CR_LF
// MQTT producers don't have CRLF, so add it back.
@@ -948,8 +950,12 @@ func (s *Server) sendStatsz(subj string) {
m.Stats.ActiveAccounts = int(atomic.LoadInt32(&s.activeAccounts))
m.Stats.Received.Msgs = atomic.LoadInt64(&s.inMsgs)
m.Stats.Received.Bytes = atomic.LoadInt64(&s.inBytes)
+ m.Stats.ReceivedFromClients.Msgs = atomic.LoadInt64(&s.inClientMsgs)
+ m.Stats.ReceivedFromClients.Bytes = atomic.LoadInt64(&s.inClientBytes)
m.Stats.Sent.Msgs = atomic.LoadInt64(&s.outMsgs)
m.Stats.Sent.Bytes = atomic.LoadInt64(&s.outBytes)
+ m.Stats.SentToClients.Msgs = atomic.LoadInt64(&s.outClientMsgs)
+ m.Stats.SentToClients.Bytes = atomic.LoadInt64(&s.outClientBytes)
m.Stats.SlowConsumers = atomic.LoadInt64(&s.slowConsumers)
// Evaluate the slow consumer stats, but set it only if one of the value is not 0.
scs := &SlowConsumersStats{
@@ -1880,6 +1886,10 @@ func (s *Server) shutdownEventing() {
}
s.mu.Lock()
+ if s.sys == nil || s.sys.resetCh == nil {
+ s.mu.Unlock()
+ return
+ }
clearTimer(&s.sys.sweeper)
clearTimer(&s.sys.stmr)
rc := s.sys.resetCh
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/filestore.go b/vendor/github.com/nats-io/nats-server/v2/server/filestore.go
index c6108ad68b..626cbe6e4d 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/filestore.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/filestore.go
@@ -1425,6 +1425,7 @@ func (mb *msgBlock) convertCipher() error {
// Reset the cache since we just read everything in.
mb.cache = nil
+ mb.ecache.Set(nil)
// Generate new keys. If we error for some reason then we will put
// the old keyfile back.
@@ -1462,10 +1463,19 @@ func (mb *msgBlock) convertToEncrypted() error {
} else if err = mb.indexCacheBuf(buf); err != nil {
// This likely indicates this was already encrypted or corrupt.
mb.cache = nil
+ mb.ecache.Set(nil)
return err
}
// Undo cache from above for later.
mb.cache = nil
+ mb.ecache.Set(nil)
+ // Regenerate mb.bek so that the keystream offset is at zero. This matches
+ // what encryptOrDecryptIfNeeded does on read-back, otherwise re-entering
+ // convertToEncrypted with a previously-used mb.bek would write ciphertext at
+ // the wrong stream offset and silently corrupt the block.
+ if mb.bek, err = genBlockEncryptionKey(mb.fs.fcfg.Cipher, mb.seed, mb.nonce); err != nil {
+ return err
+ }
mb.bek.XORKeyStream(buf, buf)
<-dios
err = os.WriteFile(mb.mfn, buf, defaultFilePerms)
@@ -2208,18 +2218,22 @@ func (fs *fileStore) recoverTTLState() error {
// Done.
break
}
- msg, _, err := mb.fetchMsgNoCopy(seq, &sm)
+ mb.mu.Lock()
+ msg, _, err := mb.fetchMsgNoCopyLocked(seq, &sm)
if err != nil {
+ mb.finishedWithCache()
+ mb.mu.Unlock()
fs.warn("Error loading msg seq %d for recovering TTL: %s", seq, err)
continue
}
- if len(msg.hdr) == 0 {
- continue
- }
- if ttl, _ := getMessageTTL(msg.hdr); ttl > 0 {
- expires := time.Duration(msg.ts) + (time.Second * time.Duration(ttl))
- fs.ttls.Add(seq, int64(expires))
+ if len(msg.hdr) > 0 {
+ if ttl, _ := getMessageTTL(msg.hdr); ttl > 0 {
+ expires := time.Duration(msg.ts) + (time.Second * time.Duration(ttl))
+ fs.ttls.Add(seq, int64(expires))
+ }
}
+ mb.finishedWithCache()
+ mb.mu.Unlock()
}
}
return nil
@@ -2289,18 +2303,22 @@ func (fs *fileStore) recoverMsgSchedulingState() error {
// Done.
break
}
- msg, _, err := mb.fetchMsgNoCopy(seq, &sm)
+ mb.mu.Lock()
+ msg, _, err := mb.fetchMsgNoCopyLocked(seq, &sm)
if err != nil {
+ mb.finishedWithCache()
+ mb.mu.Unlock()
fs.warn("Error loading msg seq %d for recovering message schedules: %s", seq, err)
continue
}
- if len(msg.hdr) == 0 {
- continue
- }
- if schedule, apiErr := nextMessageSchedule(sm.hdr, sm.ts); apiErr == nil && !schedule.IsZero() {
- // Copy the subject, as it's stored in the scheduling maps and the backing cache could be reused in the meantime.
- fs.scheduling.init(seq, copyString(sm.subj), schedule.UnixNano())
+ if len(msg.hdr) > 0 {
+ if schedule, apiErr := nextMessageSchedule(msg.hdr, msg.ts); apiErr == nil && !schedule.IsZero() {
+ // Copy the subject, as it's stored in the scheduling maps and the backing cache could be reused in the meantime.
+ fs.scheduling.init(seq, copyString(msg.subj), schedule.UnixNano())
+ }
}
+ mb.finishedWithCache()
+ mb.mu.Unlock()
}
}
return nil
@@ -2755,6 +2773,7 @@ func (fs *fileStore) GetSeqFromTime(t time.Time) uint64 {
// Using a binary search, but need to be aware of interior deletes in the block.
seq := lseq + 1
+ mb.mu.Lock()
loop:
for fseq <= lseq {
mid := fseq + (lseq-fseq)/2
@@ -2762,7 +2781,7 @@ loop:
// Potentially skip over gaps. We keep the original middle but keep track of a
// potential delete range with an offset.
for {
- sm, _, err := mb.fetchMsgNoCopy(mid+off, &smv)
+ sm, _, err := mb.fetchMsgNoCopyLocked(mid+off, &smv)
if err != nil || sm == nil {
off++
if mid+off <= lseq {
@@ -2789,6 +2808,8 @@ loop:
fseq = mid + off + 1
}
}
+ mb.finishedWithCache()
+ mb.mu.Unlock()
return seq
}
@@ -2829,14 +2850,11 @@ func (mb *msgBlock) firstMatchingMulti(sl *gsl.SimpleSublist, start uint64, sm *
// If there are no subject matches then this is effectively no-op.
hseq := uint64(math.MaxUint64)
var ierr error
- stree.IntersectGSL(mb.fss, sl, func(subj []byte, ss *SimpleState) {
- if ierr != nil {
- return
- }
+ stree.IntersectGSL(mb.fss, sl, func(subj []byte, ss *SimpleState) bool {
if ss.firstNeedsUpdate || ss.lastNeedsUpdate {
// mb is already loaded into the cache so should be fast-ish.
if ierr = mb.recalculateForSubj(bytesToString(subj), ss); ierr != nil {
- return
+ return false
}
}
first := max(start, ss.First)
@@ -2844,12 +2862,12 @@ func (mb *msgBlock) firstMatchingMulti(sl *gsl.SimpleSublist, start uint64, sm *
// The start cutoff is after the last sequence for this subject,
// or we think we already know of a subject with an earlier msg
// than our first seq for this subject.
- return
+ return true
}
// Need messages loaded from here on out.
if mb.cacheNotLoaded() {
if ierr = mb.loadMsgsWithLock(); ierr != nil {
- return
+ return false
}
didLoad = true
}
@@ -2863,7 +2881,7 @@ func (mb *msgBlock) firstMatchingMulti(sl *gsl.SimpleSublist, start uint64, sm *
sm = fsm
hseq = ss.First
}
- return
+ return true
}
for seq := first; seq <= ss.Last; seq++ {
// Otherwise we have a start floor that intersects where this subject
@@ -2889,6 +2907,7 @@ func (mb *msgBlock) firstMatchingMulti(sl *gsl.SimpleSublist, start uint64, sm *
// If we are here we did not match, so put the llseq back.
mb.llseq = llseq
}
+ return true
})
if ierr != nil {
return nil, false, ierr
@@ -3126,14 +3145,11 @@ func (mb *msgBlock) prevMatchingMulti(sl *gsl.SimpleSublist, start uint64, sm *S
// If there are no subject matches then this is effectively no-op.
hseq := uint64(0)
var ierr error
- stree.IntersectGSL(mb.fss, sl, func(subj []byte, ss *SimpleState) {
- if ierr != nil {
- return
- }
+ stree.IntersectGSL(mb.fss, sl, func(subj []byte, ss *SimpleState) bool {
if ss.firstNeedsUpdate || ss.lastNeedsUpdate {
// mb is already loaded into the cache so should be fast-ish.
if ierr = mb.recalculateForSubj(bytesToString(subj), ss); ierr != nil {
- return
+ return false
}
}
first := min(start, ss.Last)
@@ -3142,7 +3158,7 @@ func (mb *msgBlock) prevMatchingMulti(sl *gsl.SimpleSublist, start uint64, sm *S
if first < ss.First || first <= hseq {
// The start cutoff is before the first sequence for this subject,
// or we already know of a subject with a later-or-equal msg.
- return
+ return true
}
if first == ss.Last {
// If the start floor is above where this subject starts then we can
@@ -3151,7 +3167,7 @@ func (mb *msgBlock) prevMatchingMulti(sl *gsl.SimpleSublist, start uint64, sm *S
sm = fsm
hseq = ss.Last
}
- return
+ return true
}
for seq := first; seq >= ss.First; seq-- {
// Otherwise we have a start floor that intersects where this subject
@@ -3177,6 +3193,7 @@ func (mb *msgBlock) prevMatchingMulti(sl *gsl.SimpleSublist, start uint64, sm *S
// If we are here we did not match, so put the llseq back.
mb.llseq = llseq
}
+ return true
})
if ierr != nil {
return nil, false, ierr
@@ -3438,16 +3455,29 @@ func (fs *fileStore) checkSkipFirstBlock(filter string, wc bool, bi int) (int, e
// This is used to see if we can selectively jump start blocks based on filter subjects and a starting block index.
// Will return -1 and ErrStoreEOF if no matches at all or no more from where we are.
func (fs *fileStore) checkSkipFirstBlockMulti(sl *gsl.SimpleSublist, bi int) (int, error) {
+ // Don't bother if full wildcard.
+ if sl.MatchesFullWildcard() {
+ return bi + 1, nil
+ }
// Move through psim to gather start and stop bounds.
start, stop := uint32(math.MaxUint32), uint32(0)
- stree.IntersectGSL(fs.psim, sl, func(subj []byte, psi *psi) {
+ guard := fs.blks[bi].getIndex() + 1
+ stree.IntersectGSL(fs.psim, sl, func(subj []byte, psi *psi) bool {
if psi.fblk < start {
start = psi.fblk
}
+ if start == guard {
+ // One of the subjects matches the next block, so there's no point in carrying on trying to skip.
+ return false
+ }
if psi.lblk > stop {
stop = psi.lblk
}
+ return true
})
+ if start == guard {
+ return bi + 1, nil
+ }
// Nothing was found.
if start == uint32(math.MaxUint32) {
return -1, ErrStoreEOF
@@ -4322,10 +4352,10 @@ func (fs *fileStore) NumPendingMulti(sseq uint64, sl *gsl.SimpleSublist, lastPer
mb := fs.blks[seqStart]
bi := mb.index
- stree.IntersectGSL(fs.psim, sl, func(subj []byte, psi *psi) {
+ stree.IntersectGSL(fs.psim, sl, func(subj []byte, psi *psi) bool {
// If the select blk start is greater than entry's last blk skip.
if bi > psi.lblk {
- return
+ return true
}
total++
// We will track the subjects that are an exact match to the last block.
@@ -4333,6 +4363,7 @@ func (fs *fileStore) NumPendingMulti(sseq uint64, sl *gsl.SimpleSublist, lastPer
if psi.lblk == bi {
lbm[string(subj)] = true
}
+ return true
})
// Now check if we need to inspect the seqStart block.
@@ -4422,18 +4453,11 @@ func (fs *fileStore) NumPendingMulti(sseq uint64, sl *gsl.SimpleSublist, lastPer
var ierr error
var havePartial bool
var updateLLTS bool
- stree.IntersectGSL[SimpleState](mb.fss, sl, func(bsubj []byte, ss *SimpleState) {
- if ierr != nil {
- return
- }
+ stree.IntersectGSL[SimpleState](mb.fss, sl, func(bsubj []byte, ss *SimpleState) bool {
subj := bytesToString(bsubj)
- if havePartial {
- // If we already found a partial then don't do anything else.
- return
- }
if ss.firstNeedsUpdate || ss.lastNeedsUpdate {
if ierr = mb.recalculateForSubj(subj, ss); ierr != nil {
- return
+ return false
}
}
if sseq <= ss.First {
@@ -4441,7 +4465,9 @@ func (fs *fileStore) NumPendingMulti(sseq uint64, sl *gsl.SimpleSublist, lastPer
} else if sseq <= ss.Last {
// We matched but its a partial.
havePartial = true
+ return false
}
+ return true
})
if ierr != nil {
mb.mu.Unlock()
@@ -4494,12 +4520,13 @@ func (fs *fileStore) NumPendingMulti(sseq uint64, sl *gsl.SimpleSublist, lastPer
// If we are here it's better to calculate totals from psim and adjust downward by scanning less blocks.
start := uint32(math.MaxUint32)
- stree.IntersectGSL(fs.psim, sl, func(subj []byte, psi *psi) {
+ stree.IntersectGSL(fs.psim, sl, func(subj []byte, psi *psi) bool {
total += psi.total
// Keep track of start index for this subject.
if psi.fblk < start {
start = psi.fblk
}
+ return true
})
// See if we were asked for all, if so we are done.
@@ -4545,8 +4572,9 @@ func (fs *fileStore) NumPendingMulti(sseq uint64, sl *gsl.SimpleSublist, lastPer
}
// Mark fss activity.
mb.lsts = ats.AccessTime()
- stree.IntersectGSL(mb.fss, sl, func(bsubj []byte, ss *SimpleState) {
+ stree.IntersectGSL(mb.fss, sl, func(bsubj []byte, ss *SimpleState) bool {
adjust += ss.Msgs
+ return true
})
}
} else {
@@ -5728,7 +5756,6 @@ func (fs *fileStore) removeMsgFromBlock(mb *msgBlock, seq uint64, secure, viaLim
lhdr, lmsg int
ttl int64
)
- // We don't use a copy as long as that's possible. When unlocking mb or erasing, we'll copy the subject.
sm, err := mb.cacheLookupNoCopy(seq, &smv)
if err != nil {
finishedWithCache()
@@ -5739,7 +5766,9 @@ func (fs *fileStore) removeMsgFromBlock(mb *msgBlock, seq uint64, secure, viaLim
}
return false, err
} else if sm != nil {
- subj = sm.subj
+ // subj aliases mb.cache.buf; copy now because the cache may be erased or
+ // recycled after we drop mb.mu. The rest are scalars stashed for later use.
+ subj = copyString(sm.subj)
ts = sm.ts
lhdr = len(sm.hdr)
lmsg = len(sm.msg)
@@ -5751,8 +5780,6 @@ func (fs *fileStore) removeMsgFromBlock(mb *msgBlock, seq uint64, secure, viaLim
// when the last block is empty.
// If not via limits and not empty (empty writes tombstone below if last) write tombstone.
if !viaLimits && !isEmpty && sm != nil {
- // Need to copy the subject since we unlock and re-acquire, and the cache could change.
- subj = copyString(subj)
mb.mu.Unlock() // Only safe way to checkLastBlock is to unlock here...
lmb, err := fs.checkLastBlock(emptyRecordLen)
if err != nil {
@@ -5785,9 +5812,6 @@ func (fs *fileStore) removeMsgFromBlock(mb *msgBlock, seq uint64, secure, viaLim
mb.mu.Unlock()
return false, err
}
- // Need to copy the subject, as eraseMsg will overwrite the cache and we won't
- // be able to access sm.subj anymore later on.
- subj = copyString(subj)
if err := mb.eraseMsg(seq, int(ri), int(msz), isLastBlock); err != nil {
finishedWithCache()
mb.mu.Unlock()
@@ -6495,10 +6519,9 @@ func (mb *msgBlock) selectNextFirst() {
var smv StoreMsg
sm, _ := mb.cacheLookupNoCopy(seq, &smv)
if sm == nil {
- // Slow path, need to unlock.
- mb.mu.Unlock()
- sm, _, _ = mb.fetchMsgNoCopy(seq, &smv)
- mb.mu.Lock()
+ // Slow path, cache not loaded.
+ sm, _, _ = mb.fetchMsgNoCopyLocked(seq, &smv)
+ mb.finishedWithCache()
}
if sm != nil {
mb.first.ts = sm.ts
@@ -6676,8 +6699,14 @@ func (mb *msgBlock) tryExpireCacheLocked() {
}
// Check for activity on the cache that would prevent us from expiring.
- if tns-bufts <= int64(mb.cexp) {
- mb.resetCacheExpireTimer(mb.cexp - time.Duration(tns-bufts))
+ // Both tns and bufts come from ats.AccessTime(), which means bufts can understate
+ // how recent the last activity actually was by up to one tick.
+ if delta := tns - bufts; delta <= int64(mb.cexp)+int64(ats.TickInterval) {
+ td := mb.cexp - time.Duration(delta)
+ if td <= 0 {
+ td = ats.TickInterval
+ }
+ mb.resetCacheExpireTimer(td)
if strengthened {
mb.finishedWithCache()
}
@@ -8459,25 +8488,25 @@ checkCache:
// We assume the block was selected and is correct, so we do not do range checks.
// Lock should not be held.
func (mb *msgBlock) fetchMsg(seq uint64, sm *StoreMsg) (*StoreMsg, bool, error) {
+ mb.mu.Lock()
+ defer mb.mu.Unlock()
+ defer mb.finishedWithCache()
return mb.fetchMsgEx(seq, sm, true)
}
// Fetch a message from this block, possibly reading in and caching the messages.
// We assume the block was selected and is correct, so we do not do range checks.
-// We will not copy the msg data.
-// Lock should not be held.
-func (mb *msgBlock) fetchMsgNoCopy(seq uint64, sm *StoreMsg) (*StoreMsg, bool, error) {
+// We will not copy the msg data, the returned StoreMsg's subj/hdr/msg/buf are aliased
+// into mb.cache.buf and are only safe to read while mb.mu is held.
+func (mb *msgBlock) fetchMsgNoCopyLocked(seq uint64, sm *StoreMsg) (*StoreMsg, bool, error) {
return mb.fetchMsgEx(seq, sm, false)
}
// Fetch a message from this block, possibly reading in and caching the messages.
// We assume the block was selected and is correct, so we do not do range checks.
// We will copy the msg data based on doCopy boolean.
-// Lock should not be held.
+// Lock should be held.
func (mb *msgBlock) fetchMsgEx(seq uint64, sm *StoreMsg, doCopy bool) (*StoreMsg, bool, error) {
- mb.mu.Lock()
- defer mb.mu.Unlock()
-
fseq, lseq := atomic.LoadUint64(&mb.first.seq), atomic.LoadUint64(&mb.last.seq)
if seq < fseq || seq > lseq {
return nil, false, ErrStoreMsgNotFound
@@ -8499,7 +8528,6 @@ func (mb *msgBlock) fetchMsgEx(seq uint64, sm *StoreMsg, doCopy bool) (*StoreMsg
return nil, false, err
}
}
- defer mb.finishedWithCache()
llseq := mb.llseq
fsm, err := mb.cacheLookupEx(seq, sm, doCopy)
@@ -8507,7 +8535,7 @@ func (mb *msgBlock) fetchMsgEx(seq uint64, sm *StoreMsg, doCopy bool) (*StoreMsg
return nil, false, err
}
expireOk := (seq == lseq && llseq == seq-1) || (seq == fseq && llseq == seq+1)
- return fsm, expireOk, err
+ return fsm, expireOk, nil
}
var (
@@ -8675,9 +8703,15 @@ func (fs *fileStore) sizeForSeq(seq uint64) int {
}
var smv StoreMsg
if mb := fs.selectMsgBlock(seq); mb != nil {
- if sm, _, _ := mb.fetchMsgNoCopy(seq, &smv); sm != nil {
- return int(fileStoreMsgSize(sm.subj, sm.hdr, sm.msg))
+ mb.mu.Lock()
+ sm, _, _ := mb.fetchMsgNoCopyLocked(seq, &smv)
+ var sz int
+ if sm != nil {
+ sz = int(fileStoreMsgSize(sm.subj, sm.hdr, sm.msg))
}
+ mb.finishedWithCache()
+ mb.mu.Unlock()
+ return sz
}
return 0
}
@@ -8867,9 +8901,17 @@ func (fs *fileStore) SubjectForSeq(seq uint64) (string, error) {
mb := fs.selectMsgBlock(seq)
fs.mu.RUnlock()
if mb != nil {
- if sm, _, _ := mb.fetchMsgNoCopy(seq, &smv); sm != nil {
+ mb.mu.Lock()
+ sm, _, _ := mb.fetchMsgNoCopyLocked(seq, &smv)
+ var subj string
+ if sm != nil {
// Copy the subject, as it's used elsewhere, and the backing cache could be reused in the meantime.
- return copyString(sm.subj), nil
+ subj = copyString(sm.subj)
+ }
+ mb.finishedWithCache()
+ mb.mu.Unlock()
+ if sm != nil {
+ return subj, nil
}
}
return _EMPTY_, ErrStoreMsgNotFound
@@ -9028,12 +9070,13 @@ func (fs *fileStore) LoadNextMsgMulti(sl *gsl.SimpleSublist, start uint64, smp *
if start <= fs.state.FirstSeq {
var total uint64
blkStart := uint32(math.MaxUint32)
- stree.IntersectGSL(fs.psim, sl, func(subj []byte, psi *psi) {
+ stree.IntersectGSL(fs.psim, sl, func(subj []byte, psi *psi) bool {
total += psi.total
// Keep track of start index for this subject.
if psi.fblk < blkStart {
blkStart = psi.fblk
}
+ return true
})
// Nothing available.
if total == 0 {
@@ -10690,11 +10733,18 @@ func (fs *fileStore) Truncate(seq uint64) (rerr error) {
// at the end, after we release the lock.
os.Remove(filepath.Join(fs.fcfg.StoreDir, msgDir, streamStreamStateFile))
- var err error
- var lsm *StoreMsg
+ var hasLsm bool
+ var lastTime int64
smb := fs.selectMsgBlock(seq)
if smb != nil {
- lsm, _, err = smb.fetchMsgNoCopy(seq, nil)
+ smb.mu.Lock()
+ lsm, _, err := smb.fetchMsgNoCopyLocked(seq, nil)
+ if lsm != nil {
+ hasLsm = true
+ lastTime = lsm.ts
+ }
+ smb.finishedWithCache()
+ smb.mu.Unlock()
if err != nil && err != ErrStoreMsgNotFound && err != errDeletedMsg {
fs.mu.Unlock()
return err
@@ -10702,13 +10752,12 @@ func (fs *fileStore) Truncate(seq uint64) (rerr error) {
}
// Reset last so new block doesn't contain truncated sequences/timestamps.
- var lastTime int64
- if lsm != nil {
- lastTime = lsm.ts
- } else if smb != nil {
- lastTime = smb.last.ts
- } else {
- lastTime = fs.state.LastTime.UnixNano()
+ if !hasLsm {
+ if smb != nil {
+ lastTime = smb.last.ts
+ } else {
+ lastTime = fs.state.LastTime.UnixNano()
+ }
}
fs.state.LastSeq = seq
fs.state.LastTime = time.Unix(0, lastTime).UTC()
@@ -10730,7 +10779,7 @@ func (fs *fileStore) Truncate(seq uint64) (rerr error) {
// If the selected block is not found or the message was deleted, we'll need to write a tombstone
// at the truncated sequence so we don't roll backward on our last sequence and timestamp.
- if lsm == nil || removeSmb {
+ if !hasLsm || removeSmb {
if err = fs.writeTombstone(seq, lastTime); err != nil {
fs.mu.Unlock()
return err
@@ -11568,7 +11617,7 @@ func (fs *fileStore) flushStreamStateLoop(qch, done chan struct{}) {
fs.warn("File system permission denied when flushing stream state, disabling JetStream: %v", err)
// messages in block cache could be lost in the worst case.
// In the clustered mode it is very highly unlikely as a result of replication.
- fs.srv.DisableJetStream()
+ fs.srv.ShutdownJetStream()
return
}
@@ -12832,16 +12881,31 @@ func (o *consumerFileStore) UpdateAcks(dseq, sseq uint64) error {
return ErrNoAckPolicy
}
+ var kick bool
+ defer func() {
+ if kick {
+ o.kickFlusher()
+ }
+ }()
+
+ // We do this regardless.
+ if _, ok := o.state.Redelivered[sseq]; ok {
+ delete(o.state.Redelivered, sseq)
+ kick = true
+ }
+
// On restarts the old leader may get a replay from the raft logs that are old.
if dseq <= o.state.AckFloor.Consumer {
return nil
}
if len(o.state.Pending) == 0 || o.state.Pending[sseq] == nil {
- delete(o.state.Redelivered, sseq)
return ErrStoreMsgNotFound
}
+ // Done with the consistency checks, we'll always kick for below updates.
+ kick = true
+
// Check for AckAll here (or AckFlowControl which functions like AckAll).
if o.cfg.AckPolicy == AckAll || o.cfg.AckPolicy == AckFlowControl {
sgap := sseq - o.state.AckFloor.Stream
@@ -12860,7 +12924,6 @@ func (o *consumerFileStore) UpdateAcks(dseq, sseq uint64) error {
delete(o.state.Redelivered, seq)
}
}
- o.kickFlusher()
return nil
}
@@ -12892,13 +12955,27 @@ func (o *consumerFileStore) UpdateAcks(dseq, sseq uint64) error {
}
}
}
- // We do these regardless.
- delete(o.state.Redelivered, sseq)
-
- o.kickFlusher()
return nil
}
+func (o *consumerFileStore) RemoveRedeliveredBelow(seq uint64) {
+ if seq == 0 {
+ return
+ }
+ o.mu.Lock()
+ defer o.mu.Unlock()
+ var removed bool
+ for s := range o.state.Redelivered {
+ if s < seq {
+ delete(o.state.Redelivered, s)
+ removed = true
+ }
+ }
+ if removed {
+ o.kickFlusher()
+ }
+}
+
const seqsHdrSize = 6*binary.MaxVarintLen64 + hdrLen
// Encode our consumer state, version 2.
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/jetstream.go b/vendor/github.com/nats-io/nats-server/v2/server/jetstream.go
index 010b170b8d..f686864668 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/jetstream.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/jetstream.go
@@ -209,9 +209,6 @@ func (s *Server) EnableJetStream(config *JetStreamConfig) error {
maxStore, maxMem = config.MaxStore, config.MaxMemory
}
config = s.dynJetStreamConfig(storeDir, maxStore, maxMem)
- if maxMem > 0 {
- config.MaxMemory = maxMem
- }
if domain != _EMPTY_ {
config.Domain = domain
}
@@ -577,7 +574,7 @@ func (s *Server) restartJetStream() error {
err := s.EnableJetStream(&cfg)
if err != nil {
s.Warnf("Can't start JetStream: %v", err)
- return s.DisableJetStream()
+ return s.ShutdownJetStream()
}
s.updateJetStreamInfoStatus(true)
return nil
@@ -629,7 +626,7 @@ func (s *Server) handleOutOfSpace(mset *stream) {
s.Errorf("JetStream out of resources, will be DISABLED")
}
- go s.DisableJetStream()
+ go s.ShutdownJetStream()
adv := &JSServerOutOfSpaceAdvisory{
TypedEvent: TypedEvent{
@@ -648,8 +645,23 @@ func (s *Server) handleOutOfSpace(mset *stream) {
}
// DisableJetStream will turn off JetStream and signals in clustered mode
-// to have the metacontroller remove us from the peer list.
+// to have the metacontroller remove us from the peer list. Persistent
+// meta-raft state on disk is removed. For transient runtime errors where
+// the server should rejoin its existing meta group on restart, use
+// ShutdownJetStream instead.
func (s *Server) DisableJetStream() error {
+ return s.disableJetStream(true)
+}
+
+// ShutdownJetStream is like DisableJetStream but preserves persistent
+// meta-raft state on disk so the server can rejoin the existing meta
+// group on restart. Use for transient runtime errors that the operator
+// is expected to fix before restarting.
+func (s *Server) ShutdownJetStream() error {
+ return s.disableJetStream(false)
+}
+
+func (s *Server) disableJetStream(deleteState bool) error {
if !s.JetStreamEnabled() {
return nil
}
@@ -680,7 +692,12 @@ func (s *Server) DisableJetStream() error {
s.Warnf("JetStream timeout waiting for meta leader transfer")
}
}
- meta.Delete()
+ if deleteState {
+ meta.Delete()
+ } else {
+ meta.Stop()
+ meta.WaitForStop()
+ }
}
}
@@ -1811,13 +1828,15 @@ func diffCheckedLimits(a, b map[string]JetStreamAccountLimits) map[string]JetStr
// Lock should be held.
func (jsa *jsAccount) reservedStorage(tier string) (mem, store uint64) {
for _, mset := range jsa.streams {
- cfg := &mset.cfg
- if (tier == _EMPTY_ || tier == tierName(cfg.Replicas)) && cfg.MaxBytes > 0 {
- switch cfg.Storage {
+ mset.cfgMu.RLock()
+ storage, replicas, maxBytes := mset.cfg.Storage, mset.cfg.Replicas, mset.cfg.MaxBytes
+ mset.cfgMu.RUnlock()
+ if (tier == _EMPTY_ || tier == tierName(replicas)) && maxBytes > 0 {
+ switch storage {
case FileStorage:
- store += uint64(cfg.MaxBytes)
+ store += uint64(maxBytes)
case MemoryStorage:
- mem += uint64(cfg.MaxBytes)
+ mem += uint64(maxBytes)
}
}
}
@@ -2332,9 +2351,9 @@ func tierName(replicas int) string {
return fmt.Sprintf("R%d", replicas)
}
-func isSameTier(cfgA, cfgB *StreamConfig) bool {
- a := max(1, cfgA.Replicas)
- b := max(1, cfgB.Replicas)
+func isSameTier(replicasA, replicasB int) bool {
+ a := max(1, replicasA)
+ b := max(1, replicasB)
// TODO (mh) this is where we could select based off a placement tag as well "qos:tier"
return a == b
}
@@ -2360,9 +2379,12 @@ func (jsa *jsAccount) selectLimits(replicas int) (JetStreamAccountLimits, string
// Lock should be held.
func (jsa *jsAccount) countStreams(tier string, cfg *StreamConfig) (streams int) {
- for _, sa := range jsa.streams {
+ for _, mset := range jsa.streams {
+ mset.cfgMu.RLock()
+ name, replicas := mset.cfg.Name, mset.cfg.Replicas
+ mset.cfgMu.RUnlock()
// Don't count the stream toward the limit if it already exists.
- if (tier == _EMPTY_ || isSameTier(&sa.cfg, cfg)) && sa.cfg.Name != cfg.Name {
+ if (tier == _EMPTY_ || isSameTier(replicas, cfg.Replicas)) && name != cfg.Name {
streams++
}
}
@@ -2426,53 +2448,69 @@ func (jsa *jsAccount) wouldExceedLimits(storeType StorageType, tierName string,
// Check account limits.
// Read Lock should be held
-func (js *jetStream) checkAccountLimits(selected *JetStreamAccountLimits, config *StreamConfig, currentRes int64) error {
- return js.checkLimits(selected, config, false, currentRes, 0)
+func (js *jetStream) checkAccountLimits(selected *JetStreamAccountLimits, tier string, config *StreamConfig, currentRes int64) error {
+ return js.checkLimits(selected, tier, config, false, currentRes, 0)
}
// Check account and server limits.
// Read Lock should be held
-func (js *jetStream) checkAllLimits(selected *JetStreamAccountLimits, config *StreamConfig, currentRes, maxBytesOffset int64) error {
- return js.checkLimits(selected, config, true, currentRes, maxBytesOffset)
+func (js *jetStream) checkAllLimits(selected *JetStreamAccountLimits, tier string, config *StreamConfig, currentRes, maxBytesOffset int64) error {
+ return js.checkLimits(selected, tier, config, true, currentRes, maxBytesOffset)
}
// Check if a new proposed msg set while exceed our account limits.
// Lock should be held.
-func (js *jetStream) checkLimits(selected *JetStreamAccountLimits, config *StreamConfig, checkServer bool, currentRes, maxBytesOffset int64) error {
+func (js *jetStream) checkLimits(selected *JetStreamAccountLimits, tier string, config *StreamConfig, checkServer bool, currentRes, maxBytesOffset int64) error {
// Check MaxConsumers
if config.MaxConsumers > 0 && selected.MaxConsumers > 0 && config.MaxConsumers > selected.MaxConsumers {
return NewJSMaximumConsumersLimitError()
}
// stream limit is checked separately on stream create only!
// Check storage, memory or disk.
- return js.checkBytesLimits(selected, config.MaxBytes, config.Storage, checkServer, currentRes, maxBytesOffset)
+ return js.checkBytesLimits(selected, tier, config.MaxBytes, config.Replicas, config.Storage, checkServer, currentRes, maxBytesOffset)
+}
+
+// accountReservation returns how many bytes count against the account limit
+// for a stream with the given replica count. Un-tiered limits are flat, so R>1
+// is counted as Replicas*bytes; tiered limits already bake in replication.
+func accountReservation(tier string, replicas int, bytes int64) int64 {
+ if bytes <= 0 {
+ return 0
+ }
+ if tier == _EMPTY_ && replicas > 1 {
+ return mulSaturate(int64(replicas), bytes)
+ }
+ return bytes
}
// Check if additional bytes will exceed our account limits and optionally the server itself.
// Read Lock should be held.
-func (js *jetStream) checkBytesLimits(selectedLimits *JetStreamAccountLimits, addBytes int64, storage StorageType, checkServer bool, currentRes, maxBytesOffset int64) error {
+func (js *jetStream) checkBytesLimits(selectedLimits *JetStreamAccountLimits, tier string, addBytes int64, replicas int, storage StorageType, checkServer bool, currentRes, maxBytesOffset int64) error {
if addBytes < 0 {
addBytes = 1
}
- totalBytes := addSaturate(addBytes, maxBytesOffset)
+ // The per-server footprint is a single replica's worth of bytes; the
+ // account footprint additionally accounts for replication in un-tiered setups.
+ serverBytes := addSaturate(addBytes, maxBytesOffset)
+ accountBytes := accountReservation(tier, replicas, serverBytes)
switch storage {
case MemoryStorage:
// Account limits defined.
- if selectedLimits.MaxMemory >= 0 && (currentRes > selectedLimits.MaxMemory || totalBytes > selectedLimits.MaxMemory-currentRes) {
+ if selectedLimits.MaxMemory >= 0 && (currentRes > selectedLimits.MaxMemory || accountBytes > selectedLimits.MaxMemory-currentRes) {
return NewJSMemoryResourcesExceededError()
}
// Check if this server can handle request.
- if checkServer && (js.memReserved > js.config.MaxMemory || totalBytes > js.config.MaxMemory-js.memReserved) {
+ if checkServer && (js.memReserved > js.config.MaxMemory || serverBytes > js.config.MaxMemory-js.memReserved) {
return NewJSMemoryResourcesExceededError()
}
case FileStorage:
// Account limits defined.
- if selectedLimits.MaxStore >= 0 && (currentRes > selectedLimits.MaxStore || totalBytes > selectedLimits.MaxStore-currentRes) {
+ if selectedLimits.MaxStore >= 0 && (currentRes > selectedLimits.MaxStore || accountBytes > selectedLimits.MaxStore-currentRes) {
return NewJSStorageResourcesExceededError()
}
// Check if this server can handle request.
- if checkServer && (js.storeReserved > js.config.MaxStore || totalBytes > js.config.MaxStore-js.storeReserved) {
+ if checkServer && (js.storeReserved > js.config.MaxStore || serverBytes > js.config.MaxStore-js.storeReserved) {
return NewJSStorageResourcesExceededError()
}
}
@@ -2682,13 +2720,13 @@ func (s *Server) dynJetStreamConfig(storeDir string, maxStore, maxMem int64) *Je
jsc.SyncInterval = opts.SyncInterval
jsc.SyncAlways = opts.SyncAlways
- if opts.maxStoreSet && maxStore >= 0 {
+ if maxStore > 0 || (opts.maxStoreSet && maxStore == 0) {
jsc.MaxStore = maxStore
} else {
jsc.MaxStore = diskAvailable(jsc.StoreDir)
}
- if opts.maxMemSet && maxMem >= 0 {
+ if maxMem > 0 || (opts.maxMemSet && maxMem == 0) {
jsc.MaxMemory = maxMem
} else {
// Estimate to 75% of total memory if we can determine system memory.
@@ -2873,7 +2911,7 @@ func (s *Server) handleWritePermissionError() {
if s.JetStreamEnabled() {
s.Errorf("File system permission denied while writing, disabling JetStream")
- go s.DisableJetStream()
+ go s.ShutdownJetStream()
//TODO Send respective advisory if needed, same as in handleOutOfSpace
}
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_api.go b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_api.go
index 53525a8bca..cd1ace7e1d 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_api.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_api.go
@@ -1352,19 +1352,16 @@ func (s *Server) jsonResponse(v any) string {
// Read lock must be held
func (jsa *jsAccount) tieredReservation(tier string, cfg *StreamConfig) int64 {
var reservation int64
- for _, sa := range jsa.streams {
+ for _, mset := range jsa.streams {
+ mset.cfgMu.RLock()
+ name, storage, replicas, maxBytes := mset.cfg.Name, mset.cfg.Storage, mset.cfg.Replicas, mset.cfg.MaxBytes
+ mset.cfgMu.RUnlock()
// Don't count the stream toward the limit if it already exists.
- if sa.cfg.Name == cfg.Name {
+ if name == cfg.Name {
continue
}
- if (tier == _EMPTY_ || isSameTier(&sa.cfg, cfg)) && sa.cfg.MaxBytes > 0 && sa.cfg.Storage == cfg.Storage {
- // If tier is empty, all storage is flat and we should adjust for replicas.
- // Otherwise if tiered, storage replication already taken into consideration.
- if tier == _EMPTY_ && sa.cfg.Replicas > 1 {
- reservation = addSaturate(reservation, mulSaturate(int64(sa.cfg.Replicas), sa.cfg.MaxBytes))
- } else {
- reservation = addSaturate(reservation, sa.cfg.MaxBytes)
- }
+ if (tier == _EMPTY_ || isSameTier(replicas, cfg.Replicas)) && maxBytes > 0 && storage == cfg.Storage {
+ reservation = addSaturate(reservation, accountReservation(tier, replicas, maxBytes))
}
}
return reservation
@@ -1699,19 +1696,23 @@ func (s *Server) jsStreamNamesRequest(sub *subscription, c *client, _ *Account,
resp.Streams = resp.Streams[:JSApiNamesLimit]
}
} else {
+ // Snapshot names once to avoid repeated cfgMu RLocks during sort+append.
msets := acc.filteredStreams(filter)
- // Since we page results order matters.
- if len(msets) > 1 {
- slices.SortFunc(msets, func(i, j *stream) int { return cmp.Compare(i.cfg.Name, j.cfg.Name) })
+ names := make([]string, len(msets))
+ for i, mset := range msets {
+ names[i] = mset.getCfgName()
+ }
+ if len(names) > 1 {
+ slices.Sort(names)
}
- numStreams = len(msets)
+ numStreams = len(names)
if offset > numStreams {
offset = numStreams
}
- for _, mset := range msets[offset:] {
- resp.Streams = append(resp.Streams, mset.cfg.Name)
+ for _, name := range names[offset:] {
+ resp.Streams = append(resp.Streams, name)
if len(resp.Streams) >= JSApiNamesLimit {
break
}
@@ -1805,21 +1806,31 @@ func (s *Server) jsStreamListRequest(sub *subscription, c *client, _ *Account, s
msets = acc.filteredStreams(filter)
}
- slices.SortFunc(msets, func(i, j *stream) int { return cmp.Compare(i.cfg.Name, j.cfg.Name) })
+ // Snapshot names once and sort the parallel slice to avoid repeated cfgMu RLocks.
+ type msetWithName struct {
+ mset *stream
+ name string
+ }
+ named := make([]msetWithName, len(msets))
+ for i, mset := range msets {
+ named[i] = msetWithName{mset, mset.getCfgName()}
+ }
+ slices.SortFunc(named, func(a, b msetWithName) int { return cmp.Compare(a.name, b.name) })
- scnt := len(msets)
+ scnt := len(named)
if offset > scnt {
offset = scnt
}
var missingNames []string
- for _, mset := range msets[offset:] {
+ for _, n := range named[offset:] {
+ mset, name := n.mset, n.name
if mset.offlineReason != _EMPTY_ {
if resp.Offline == nil {
resp.Offline = make(map[string]string, 1)
}
- resp.Offline[mset.getCfgName()] = mset.offlineReason
- missingNames = append(missingNames, mset.getCfgName())
+ resp.Offline[name] = mset.offlineReason
+ missingNames = append(missingNames, name)
continue
}
@@ -3286,12 +3297,15 @@ func (s *Server) jsMsgDeleteRequest(sub *subscription, c *client, _ *Account, su
s.sendAPIErrResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(&resp))
return
}
- if mset.cfg.Sealed {
+ mset.cfgMu.RLock()
+ sealed, denyDelete := mset.cfg.Sealed, mset.cfg.DenyDelete
+ mset.cfgMu.RUnlock()
+ if sealed {
resp.Error = NewJSStreamSealedError()
s.sendAPIErrResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(&resp))
return
}
- if mset.cfg.DenyDelete {
+ if denyDelete {
resp.Error = NewJSStreamMsgDeleteFailedError(errors.New("message delete not permitted"))
s.sendAPIErrResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(&resp))
return
@@ -3719,12 +3733,15 @@ func (s *Server) jsStreamPurgeRequest(sub *subscription, c *client, _ *Account,
s.sendAPIErrResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(&resp))
return
}
- if mset.cfg.Sealed {
+ mset.cfgMu.RLock()
+ sealed, denyPurge := mset.cfg.Sealed, mset.cfg.DenyPurge
+ mset.cfgMu.RUnlock()
+ if sealed {
resp.Error = NewJSStreamSealedError()
s.sendAPIErrResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(&resp))
return
}
- if mset.cfg.DenyPurge {
+ if denyPurge {
resp.Error = NewJSStreamPurgeFailedError(errors.New("stream purge not permitted"))
s.sendAPIErrResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(&resp))
return
@@ -3762,7 +3779,7 @@ func (acc *Account) jsNonClusteredStreamLimitsCheck(cfg *StreamConfig) *ApiError
return NewJSMaximumStreamsLimitError()
}
reserved := jsa.tieredReservation(tier, cfg)
- if err := jsa.js.checkAllLimits(selectedLimits, cfg, reserved, 0); err != nil {
+ if err := jsa.js.checkAllLimits(selectedLimits, tier, cfg, reserved, 0); err != nil {
return NewJSStreamLimitsError(err, Unless(err))
}
return nil
@@ -4297,6 +4314,8 @@ func (s *Server) streamSnapshot(acc *Account, mset *stream, sr *SnapshotResult,
var hdr []byte
chunk := make([]byte, chunkSize)
+ ackTimer := time.NewTimer(snapshotAckTimeout)
+ defer stopAndClearTimer(&ackTimer)
for index := 1; ; index++ {
select {
case <-slots:
@@ -4309,7 +4328,7 @@ func (s *Server) streamSnapshot(acc *Account, mset *stream, sr *SnapshotResult,
// The snapshotting goroutine has failed for some reason.
hdr = []byte(fmt.Sprintf("NATS/1.0 500 %s\r\n\r\n", err))
goto done
- case <-time.After(snapshotAckTimeout):
+ case <-ackTimer.C:
// It's taking a very long time for the receiver to send us acks,
// they have probably stalled or there is high loss on the link.
hdr = []byte("NATS/1.0 408 No Flow Response\r\n\r\n")
@@ -4328,6 +4347,7 @@ func (s *Server) streamSnapshot(acc *Account, mset *stream, sr *SnapshotResult,
hdr = []byte("NATS/1.0 204\r\n\r\n")
}
mset.outq.send(newJSPubMsg(reply, _EMPTY_, ackReply, nil, chunk, nil, 0))
+ ackTimer.Reset(snapshotAckTimeout)
}
done:
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_batching.go b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_batching.go
index 4b04241fad..2f4419bbf2 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_batching.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_batching.go
@@ -451,6 +451,7 @@ func (diff *batchStagedDiff) commit(mset *stream) {
if c, ok := mset.inflight[subj]; ok {
c.bytes += i.bytes
c.ops += i.ops
+ c.schedule = i.schedule
} else {
mset.inflight[subj] = i
}
@@ -530,13 +531,14 @@ func checkMsgHeadersPreClusteredProposal(
discard DiscardPolicy, discardNewPer bool, maxMsgSize int, maxMsgs int64, maxMsgsPer int64, maxBytes int64,
) ([]byte, []byte, uint64, *ApiError, error) {
var incr *big.Int
+ var hasSchedule bool
// Some header checks must be checked pre proposal.
if len(hdr) > 0 {
// Since we encode header len as u16 make sure we do not exceed.
// Again this works if it goes through but better to be pre-emptive.
if len(hdr) > math.MaxUint16 {
- err := fmt.Errorf("JetStream header size exceeds limits for '%s > %s'", jsa.acc().Name, mset.cfg.Name)
+ err := fmt.Errorf("JetStream header size exceeds limits for '%s > %s'", jsa.acc().Name, name)
return hdr, msg, 0, NewJSStreamHeaderExceedsMaximumError(), err
}
// Counter increments.
@@ -810,6 +812,7 @@ func checkMsgHeadersPreClusteredProposal(
}
return hdr, msg, 0, apiErr, apiErr
} else if !schedule.IsZero() {
+ hasSchedule = true
if !allowMsgSchedules {
apiErr := NewJSMessageSchedulesDisabledError()
return hdr, msg, 0, apiErr, apiErr
@@ -877,6 +880,26 @@ func checkMsgHeadersPreClusteredProposal(
} else if !allowMsgSchedules {
apiErr := NewJSMessageSchedulesDisabledError()
return hdr, msg, 0, apiErr, apiErr
+ } else {
+ // Check that the to-be-purged subject is a schedule message.
+ // We still allow this message through if there exists no message for this subject,
+ // to remain backward-compatible. An "expected at sequence" check can still be
+ // performed to make this stricter.
+ schedSubj := bytesToString(scheduler)
+ var invalid bool
+ if i, ok := diff.inflight[schedSubj]; ok {
+ invalid = !i.schedule
+ } else if i, ok = mset.inflight[schedSubj]; ok {
+ invalid = !i.schedule
+ } else {
+ var smv StoreMsg
+ sm, _ := mset.store.LoadLastMsg(schedSubj, &smv)
+ invalid = sm != nil && len(sliceHeader(JSSchedulePattern, sm.hdr)) == 0
+ }
+ if invalid {
+ apiErr := NewJSMessageSchedulesSchedulerInvalidError()
+ return hdr, msg, 0, apiErr, apiErr
+ }
}
} else if !sourced && len(sliceHeader(JSScheduler, hdr)) > 0 {
// Clients may only use Nats-Scheduler alongside Nats-Schedule-Next.
@@ -930,8 +953,9 @@ func checkMsgHeadersPreClusteredProposal(
if i, ok = diff.inflight[subject]; ok {
i.bytes += sz
i.ops++
+ i.schedule = hasSchedule
} else {
- i = &inflightSubjectRunningTotal{bytes: sz, ops: 1}
+ i = &inflightSubjectRunningTotal{bytes: sz, ops: 1, schedule: hasSchedule}
diff.inflight[subject] = i
}
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_cluster.go b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_cluster.go
index d406c7f1a2..48e62e1f22 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_cluster.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_cluster.go
@@ -320,6 +320,21 @@ func (ca *consumerAssignment) clearResponded() {
ca.responded.Store(false)
}
+// sameIdentity reports whether nca refers to the same logical consumer as ca.
+// Only stable identity fields (Name, Stream, Group name, Created time) are
+// compared; request-routing fields like Client/Reply and transient flags are
+// intentionally excluded since processClusterCreateConsumer may set the
+// per-object o.ca to a clone with the original requester's Client/Reply
+// preserved while the meta-layer holds the newer values.
+func (ca *consumerAssignment) sameIdentity(nca *consumerAssignment) bool {
+ return ca != nil && nca != nil &&
+ nca.Name == ca.Name &&
+ nca.Stream == ca.Stream &&
+ nca.Created.Equal(ca.Created) &&
+ nca.Group != nil && ca.Group != nil &&
+ nca.Group.Name == ca.Group.Name
+}
+
// clone returns a copy of ca. Field-explicit (rather than `*ca`) and
// pointer-returning so the embedded atomic.Bool isn't value-copied;
// responded is transferred via Load/Store. Concurrent callers may write
@@ -713,6 +728,14 @@ func (js *jetStream) isStreamHealthy(acc *Account, sa *streamAssignment) error {
js.mu.RUnlock()
return errors.New("stream assignment or group missing")
}
+ // Surface any persisted assignment-level error (e.g. failed create on this
+ // peer due to account limits) so the health check reflects the broken state
+ // instead of falling through to runtime-only checks.
+ if sa.err != nil {
+ err := sa.err
+ js.mu.RUnlock()
+ return fmt.Errorf("stream assignment error: %w", err)
+ }
streamName := sa.Config.Name
node := sa.Group.node
js.mu.RUnlock()
@@ -788,6 +811,14 @@ func (js *jetStream) isConsumerHealthy(mset *stream, consumer string, ca *consum
js.mu.RUnlock()
return errors.New("consumer assignment or group missing")
}
+ // Surface any persisted assignment-level error (e.g. failed create on this
+ // peer) so the health check reflects the broken state instead of falling
+ // through to runtime-only checks.
+ if ca.err != nil {
+ err := ca.err
+ js.mu.RUnlock()
+ return fmt.Errorf("consumer assignment error: %w", err)
+ }
created := ca.Created
node := ca.Group.node
js.mu.RUnlock()
@@ -3612,7 +3643,7 @@ func (js *jetStream) monitorStream(mset *stream, sa *streamAssignment, sendSnaps
// If we were successful lookup up our stream now.
if err == nil {
if mset, err = acc.lookupStream(sa.Config.Name); mset != nil {
- mset.monitorWg.Add(1)
+ mset.startMonitorWg()
defer mset.monitorWg.Done()
mset.checkInMonitor()
mset.setStreamAssignment(sa)
@@ -3632,6 +3663,7 @@ func (js *jetStream) monitorStream(mset *stream, sa *streamAssignment, sendSnaps
mset.delete()
}
js.mu.Lock()
+ s.Warnf("Stream restore failed for '%s > %s': %v", sa.Client.serviceAccount(), sa.Config.Name, err)
sa.err = err
if n != nil {
n.Delete()
@@ -3792,8 +3824,7 @@ func (mset *stream) resetClusteredState(err error) bool {
// Need to do the rest in a separate Go routine.
go func() {
- mset.signalMonitorQuit()
- mset.monitorWg.Wait()
+ mset.stopMonitoring()
mset.resetAndWaitOnConsumers()
// Stop our stream.
mset.stop(shouldDelete, false)
@@ -4526,6 +4557,10 @@ func (js *jetStream) processStreamLeaderChange(mset *stream, isLeader bool) {
return
}
+ // Acquire clMu before ddMu so any inflight proposals finish first, and we can
+ // clean up if they added new dedupe IDs.
+ mset.clMu.Lock()
+
// Clear inflight dedupe IDs, where seq=0.
mset.ddMu.Lock()
var removed int
@@ -4548,7 +4583,6 @@ func (js *jetStream) processStreamLeaderChange(mset *stream, isLeader bool) {
}
mset.ddMu.Unlock()
- mset.clMu.Lock()
// Clear inflight if we have it.
mset.inflight = nil
mset.inflightTransform = nil
@@ -4557,6 +4591,12 @@ func (js *jetStream) processStreamLeaderChange(mset *stream, isLeader bool) {
// Clear expected per subject state.
mset.expectedPerSubjectSequence = nil
mset.expectedPerSubjectInProcess = nil
+
+ // Clear clseq on every leader transition. recalculateClusteredSeq
+ // repopulates it on the next proposal.
+ if mset.clseq > 0 {
+ mset.clseq = 0
+ }
mset.clMu.Unlock()
js.mu.RLock()
@@ -4578,14 +4618,6 @@ func (js *jetStream) processStreamLeaderChange(mset *stream, isLeader bool) {
}
}
- // Clear clseq on every leader transition. recalculateClusteredSeq
- // repopulates it on the next proposal.
- mset.clMu.Lock()
- if mset.clseq > 0 {
- mset.clseq = 0
- }
- mset.clMu.Unlock()
-
// Tell stream to switch leader status.
mset.setLeader(isLeader)
@@ -5041,14 +5073,14 @@ func (s *Server) removeStream(mset *stream, nsa *streamAssignment) {
if js, _ := s.getJetStreamCluster(); js != nil {
js.mu.Lock()
nsa.Group.node = nil
+ nsa.err = nil
isShuttingDown = js.shuttingDown
js.mu.Unlock()
}
if !isShuttingDown {
// wait for monitor to be shutdown.
- mset.signalMonitorQuit()
- mset.monitorWg.Wait()
+ mset.stopMonitoring()
}
mset.stop(true, false)
}
@@ -5068,6 +5100,7 @@ func (js *jetStream) processClusterUpdateStream(acc *Account, osa, sa *streamAss
storage, cfg := sa.Config.Storage, sa.Config
recovering := sa.recovering
hasResponded := sa.markResponded()
+ hadErr := sa.err != nil
js.mu.RUnlock()
mset, err := acc.lookupStream(cfg.Name)
@@ -5077,8 +5110,7 @@ func (js *jetStream) processClusterUpdateStream(acc *Account, osa, sa *streamAss
s.Warnf("JetStream cluster detected stream remapping for '%s > %s' from %q to %q",
acc, cfg.Name, osa.Group.Name, sa.Group.Name)
mset.removeNode()
- mset.signalMonitorQuit()
- mset.monitorWg.Wait()
+ mset.stopMonitoring()
alreadyRunning, needsNode = false, true
// Make sure to clear from original.
js.mu.Lock()
@@ -5103,7 +5135,7 @@ func (js *jetStream) processClusterUpdateStream(acc *Account, osa, sa *streamAss
"stream": mset.name(),
})
}
- mset.monitorWg.Add(1)
+ mset.startMonitorWg()
// Start monitoring..
started := s.startGoRoutine(
func() { js.monitorStream(mset, sa, needsNode) },
@@ -5119,8 +5151,7 @@ func (js *jetStream) processClusterUpdateStream(acc *Account, osa, sa *streamAss
} else if numReplicas == 1 && alreadyRunning {
// We downgraded to R1. Make sure we cleanup the raft node and the stream monitor.
mset.removeNode()
- mset.signalMonitorQuit()
- mset.monitorWg.Wait()
+ mset.stopMonitoring()
// In case we need to shutdown the cluster specific subs, etc.
mset.mu.Lock()
// Stop responding to sync requests.
@@ -5137,9 +5168,7 @@ func (js *jetStream) processClusterUpdateStream(acc *Account, osa, sa *streamAss
mset.setStreamAssignment(sa)
// Call update.
- if err = mset.updateWithAdvisory(cfg, !recovering, false); err != nil {
- s.Warnf("JetStream cluster error updating stream %q for account %q: %v", cfg.Name, acc.Name, err)
- }
+ err = mset.updateWithAdvisory(cfg, !recovering, false)
}
// If not found we must be expanding into this node since if we are here we know we are a member.
@@ -5150,6 +5179,7 @@ func (js *jetStream) processClusterUpdateStream(acc *Account, osa, sa *streamAss
if err != nil {
js.mu.Lock()
+ s.Warnf("Stream update failed for '%s > %s': %v", sa.Client.serviceAccount(), sa.Config.Name, err)
sa.err = err
result := &streamAssignmentResult{
Account: sa.Client.serviceAccount(),
@@ -5163,6 +5193,10 @@ func (js *jetStream) processClusterUpdateStream(acc *Account, osa, sa *streamAss
// Send response to the metadata leader. They will forward to the user as needed.
s.sendInternalMsgLocked(streamAssignmentSubj, _EMPTY_, nil, result)
return
+ } else if hadErr {
+ js.mu.Lock()
+ sa.err = nil
+ js.mu.Unlock()
}
isLeader := mset.IsLeader()
@@ -5219,6 +5253,7 @@ func (js *jetStream) processClusterCreateStream(acc *Account, sa *streamAssignme
storage := sa.Config.Storage
restore := sa.Restore
recovering := sa.recovering
+ hadErr := sa.err != nil
js.mu.RUnlock()
// Process the raft group and make sure it's running if needed.
@@ -5310,7 +5345,7 @@ func (js *jetStream) processClusterCreateStream(acc *Account, sa *streamAssignme
}
} else if err == NewJSStreamNotFoundError() {
// Add in the stream here.
- mset, err = acc.addStreamWithAssignment(sa.Config, nil, sa, false, false)
+ mset, err = acc.addStreamWithAssignment(sa.Config, nil, sa, false, true)
}
if mset != nil {
mset.setCreatedTime(created)
@@ -5327,8 +5362,8 @@ func (js *jetStream) processClusterCreateStream(acc *Account, sa *streamAssignme
return
}
+ s.Warnf("Stream create failed for '%s > %s': %v", sa.Client.serviceAccount(), sa.Config.Name, err)
if IsNatsErr(err, JSStreamStoreFailedF) {
- s.Warnf("Stream create failed for '%s > %s': %v", sa.Client.serviceAccount(), sa.Config.Name, err)
err = errStreamStoreFailed
}
js.mu.Lock()
@@ -5361,6 +5396,10 @@ func (js *jetStream) processClusterCreateStream(acc *Account, sa *streamAssignme
s.sendInternalMsgLocked(streamAssignmentSubj, _EMPTY_, nil, result)
}
return
+ } else if hadErr {
+ js.mu.Lock()
+ sa.err = nil
+ js.mu.Unlock()
}
// Re-capture node.
@@ -5372,7 +5411,7 @@ func (js *jetStream) processClusterCreateStream(acc *Account, sa *streamAssignme
if node != nil {
if !alreadyRunning {
if mset != nil {
- mset.monitorWg.Add(1)
+ mset.startMonitorWg()
}
started := s.startGoRoutine(
func() { js.monitorStream(mset, sa, false) },
@@ -5408,6 +5447,7 @@ func (js *jetStream) processClusterCreateStream(acc *Account, sa *streamAssignme
mset.delete()
}
js.mu.Lock()
+ s.Warnf("Stream restore failed for '%s > %s': %v", sa.Client.serviceAccount(), sa.Config.Name, err)
sa.err = err
result := &streamAssignmentResult{
Account: sa.Client.serviceAccount(),
@@ -5559,8 +5599,7 @@ func (js *jetStream) processClusterDeleteStream(sa *streamAssignment, isMember,
n.Delete()
}
// wait for monitor to be shut down
- mset.signalMonitorQuit()
- mset.monitorWg.Wait()
+ mset.stopMonitoring()
err = mset.stop(true, wasLeader)
stopped = true
} else if isMember {
@@ -5782,8 +5821,7 @@ func (s *Server) removeConsumer(o *consumer, nca *consumerAssignment) {
if !isShuttingDown {
// wait for monitor to be shutdown.
- o.signalMonitorQuit()
- o.monitorWg.Wait()
+ o.stopMonitoring()
}
o.deleteWithoutAdvisory()
}
@@ -5890,8 +5928,7 @@ func (js *jetStream) processClusterCreateConsumer(oca, ca *consumerAssignment, s
s.Warnf("JetStream cluster detected consumer remapping for '%s > %s' from %q to %q",
acc, ca.Name, oca.Group.Name, ca.Group.Name)
o.clearNode()
- o.signalMonitorQuit()
- o.monitorWg.Wait()
+ o.stopMonitoring()
alreadyRunning = false
// Make sure to clear from original.
js.mu.Lock()
@@ -5999,13 +6036,12 @@ func (js *jetStream) processClusterCreateConsumer(oca, ca *consumerAssignment, s
return
}
+ s.Warnf("Consumer create failed for '%s > %s > %s': %v", ca.Client.serviceAccount(), ca.Stream, ca.Name, err)
if IsNatsErr(err, JSConsumerStoreFailedErrF) {
- s.Warnf("Consumer create failed for '%s > %s > %s': %v", ca.Client.serviceAccount(), ca.Stream, ca.Name, err)
err = errConsumerStoreFailed
}
js.mu.Lock()
-
ca.err = err
hasResponded := ca.hasResponded()
@@ -6048,8 +6084,14 @@ func (js *jetStream) processClusterCreateConsumer(oca, ca *consumerAssignment, s
}
} else {
js.mu.RLock()
+ hadErr := ca.err != nil
node := rg.node
js.mu.RUnlock()
+ if hadErr {
+ js.mu.Lock()
+ ca.err = nil
+ js.mu.Unlock()
+ }
if didCreate {
o.setCreatedTime(ca.Created)
@@ -6057,8 +6099,7 @@ func (js *jetStream) processClusterCreateConsumer(oca, ca *consumerAssignment, s
// Check for scale down to 1..
if node != nil && len(rg.Peers) == 1 {
o.clearNode()
- o.signalMonitorQuit()
- o.monitorWg.Wait()
+ o.stopMonitoring()
// Need to clear from rg too.
js.mu.Lock()
rg.node = nil
@@ -6097,8 +6138,7 @@ func (js *jetStream) processClusterCreateConsumer(oca, ca *consumerAssignment, s
if node == nil {
// Wait for the previous routine to stop running.
- o.signalMonitorQuit()
- o.monitorWg.Wait()
+ o.stopMonitoring()
// Single replica consumer, process manually here.
// Force response in case we think this is an update.
if !js.isMetaRecovering() && isConfigUpdate {
@@ -6129,8 +6169,7 @@ func (js *jetStream) processClusterCreateConsumer(oca, ca *consumerAssignment, s
// Start our monitoring routine if needed.
if !alreadyRunning {
// Wait for the previous routine to stop running.
- o.signalMonitorQuit()
- o.monitorWg.Wait()
+ o.stopMonitoring()
if o.shouldStartMonitor() {
started := s.startGoRoutine(
func() { js.monitorConsumer(o, ca) },
@@ -7199,9 +7238,17 @@ func (js *jetStream) processStreamAssignmentResults(sub *subscription, c *client
}
// Remove this assignment if possible.
if canDelete {
+ var apiErr *ApiError
+ if result.Response != nil {
+ apiErr = result.Response.Error
+ } else if result.Restore != nil {
+ apiErr = result.Restore.Error
+ }
+ s.Warnf("Stream assignment for '%s > %s' rejected by assigned member: %v", sa.Client.serviceAccount(), sa.Config.Name, apiErr)
sa.err = NewJSClusterNotAssignedError()
- cc.meta.Propose(encodeDeleteStreamAssignment(sa))
- cc.trackInflightStreamProposal(result.Account, sa, true)
+ if err := cc.meta.Propose(encodeDeleteStreamAssignment(sa)); err == nil {
+ cc.trackInflightStreamProposal(result.Account, sa, true)
+ }
}
}
}
@@ -7236,6 +7283,7 @@ func (js *jetStream) processConsumerAssignmentResults(sub *subscription, c *clie
// Make sure this is recent response.
if result.Response.Error != nil && result.Response.Error != NewJSConsumerNameExistError() && time.Since(ca.Created) < 2*time.Second {
// Do not list in consumer names/lists.
+ s.Warnf("Consumer assignment for '%s > %s > %s' rejected by assigned member: %v", ca.Client.serviceAccount(), ca.Stream, ca.Name, result.Response.Error)
ca.err = NewJSClusterNotAssignedError()
}
}
@@ -7866,16 +7914,10 @@ func (js *jetStream) tieredStreamAndReservationCount(accName, tier string, cfg *
if sa.Config.Name == cfg.Name {
continue
}
- if tier == _EMPTY_ || isSameTier(sa.Config, cfg) {
+ if tier == _EMPTY_ || isSameTier(sa.Config.Replicas, cfg.Replicas) {
numStreams++
if sa.Config.MaxBytes > 0 && sa.Config.Storage == cfg.Storage {
- // If tier is empty, all storage is flat and we should adjust for replicas.
- // Otherwise if tiered, storage replication already taken into consideration.
- if tier == _EMPTY_ && sa.Config.Replicas > 1 {
- reservation = addSaturate(reservation, mulSaturate(int64(sa.Config.Replicas), sa.Config.MaxBytes))
- } else {
- reservation = addSaturate(reservation, sa.Config.MaxBytes)
- }
+ reservation = addSaturate(reservation, accountReservation(tier, sa.Config.Replicas, sa.Config.MaxBytes))
}
}
}
@@ -7951,7 +7993,7 @@ func (js *jetStream) jsClusteredStreamLimitsCheck(acc *Account, cfg *StreamConfi
return NewJSMaximumStreamsLimitError()
}
// Check for account limits here before proposing.
- if err := js.checkAccountLimits(selectedLimits, cfg, reservations); err != nil {
+ if err := js.checkAccountLimits(selectedLimits, tier, cfg, reservations); err != nil {
return NewJSStreamLimitsError(err, Unless(err))
}
return nil
@@ -9423,6 +9465,11 @@ func (s *Server) jsClusteredConsumerRequest(ci *ClientInfo, acc *Account, subjec
s.sendAPIErrResponse(ci, acc, subject, reply, string(rmsg), s.jsonResponse(&resp))
return
}
+ if cfg.DeliverPolicy != DeliverAll {
+ resp.Error = NewJSConsumerWQConsumerNotDeliverAllError()
+ s.sendAPIErrResponse(ci, acc, subject, reply, string(rmsg), s.jsonResponse(&resp))
+ return
+ }
subjects := gatherSubjectFilters(cfg.FilterSubject, cfg.FilterSubjects)
for oca := range js.consumerAssignmentsOrInflightSeq(acc.Name, stream) {
if oca.Name == oname || oca.Config.Direct || oca.Config.Sourcing {
@@ -9978,7 +10025,7 @@ func (mset *stream) processClusteredInboundMsg(subject, reply string, hdr, msg [
// Check msgSize if we have a limit set there. Again this works if it goes through but better to be pre-emptive.
// Subtract to prevent against overflows.
if maxMsgSize >= 0 && (len(hdr) > maxMsgSize || len(msg) > maxMsgSize-len(hdr)) {
- err := fmt.Errorf("JetStream message size exceeds limits for '%s > %s'", jsa.acc().Name, mset.cfg.Name)
+ err := fmt.Errorf("JetStream message size exceeds limits for '%s > %s'", jsa.acc().Name, name)
s.RateLimitWarnf("%s", err.Error())
if canRespond {
var resp = &JSPubAckResponse{PubAck: &PubAck{Stream: name}}
@@ -10275,9 +10322,7 @@ func (mset *stream) processSnapshot(snap *StreamReplicatedState, index uint64) (
mset.mu.Lock()
for _, o := range mset.consumers {
o.mu.Lock()
- if o.isLeader() {
- o.streamNumPending()
- }
+ o.streamNumPending()
o.mu.Unlock()
}
mset.mu.Unlock()
@@ -10462,8 +10507,11 @@ RETRY:
return err
} else if err == NewJSInsufficientResourcesError() {
notifyLeaderStopCatchup(mrec, err)
- if mset.js.limitsExceeded(mset.cfg.Storage) {
- s.resourcesExceededError(mset.cfg.Storage)
+ mset.cfgMu.RLock()
+ storage := mset.cfg.Storage
+ mset.cfgMu.RUnlock()
+ if mset.js.limitsExceeded(storage) {
+ s.resourcesExceededError(storage)
} else {
s.Warnf("Catchup for stream '%s > %s' errored, account resources exceeded: %v", mset.account(), mset.name(), err)
}
@@ -11167,6 +11215,8 @@ func (mset *stream) runCatchup(sendSubject string, sreq *streamSyncRequest) {
// Run as long as we are still active and need catchup.
// FIXME(dlc) - Purge event? Stream delete?
+ retryTimer := time.NewTimer(500 * time.Millisecond)
+ defer stopAndClearTimer(&retryTimer)
for {
// Get this each time, will be non-nil if globally blocked and we will close to wake everyone up.
cbKick := s.cbKickChan()
@@ -11193,12 +11243,13 @@ func (mset *stream) runCatchup(sendSubject string, sreq *streamSyncRequest) {
mset.clearCatchupPeer(sreq.Peer)
return
}
- case <-time.After(500 * time.Millisecond):
+ case <-retryTimer.C:
if !sendNextBatchAndContinue(qch) {
mset.clearCatchupPeer(sreq.Peer)
return
}
}
+ retryTimer.Reset(500 * time.Millisecond)
}
}
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/leafnode.go b/vendor/github.com/nats-io/nats-server/v2/server/leafnode.go
index be84f7b190..265da58dc5 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/leafnode.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/leafnode.go
@@ -304,6 +304,13 @@ func validateLeafNode(o *Options) error {
return fmt.Errorf("remote leaf node configuration cannot have a mix of websocket and non-websocket urls: %q", redactURLList(rcfg.URLs))
}
}
+ if !wsAllowedFIPS() {
+ for _, u := range rcfg.URLs {
+ if isWSURL(u) {
+ return fmt.Errorf("remote leaf node URL %q cannot be used in FIPS-140 mode when built with this Go version, use Go 1.26 or later", redactURLString(u.String()))
+ }
+ }
+ }
// Validate compression settings
if rcfg.Compression.Mode != _EMPTY_ {
if err := validateAndNormalizeCompressionOption(&rcfg.Compression, CompressionS2Auto); err != nil {
@@ -1300,7 +1307,10 @@ func (s *Server) createLeafNode(conn net.Conn, rURL *url.URL, remote *leafNodeCf
info = s.copyLeafNodeInfo()
// For tests that want to simulate old servers, do not set the compression
// on the INFO protocol if configured with CompressionNotSupported.
- if cm := opts.LeafNode.Compression.Mode; cm != CompressionNotSupported {
+ // Also suppress it if WebSocket compression is already in use, otherwise
+ // an old soliciting peer would honor the advertised mode, switch to S2,
+ // and then wait forever for a compressed INFO response from us.
+ if cm := opts.LeafNode.Compression.Mode; cm != CompressionNotSupported && (ws == nil || !ws.compress) {
info.Compression = cm
}
// We always send a nonce for LEAF connections. Do not change that without
@@ -1721,6 +1731,15 @@ func (c *client) processLeafnodeInfo(info *Info) {
}
func (s *Server) negotiateLeafCompression(c *client, didSolicit bool, infoCompression string, co *CompressionOpts) (bool, error) {
+ // If WebSocket compression is already negotiated on this connection then
+ // we shouldn't layer S2 compression on top of it.
+ c.mu.Lock()
+ if c.ws != nil && c.ws.compress {
+ c.leaf.compression = CompressionOff
+ c.mu.Unlock()
+ return false, nil
+ }
+ c.mu.Unlock()
// Negotiate the appropriate compression mode (or no compression)
cm, err := selectCompressionMode(co.Mode, infoCompression)
if err != nil {
@@ -2027,12 +2046,14 @@ func (s *Server) addLeafNodeConnection(c *client, srvName, clusterName string, c
// In an extension use case, pin leadership to server remotes connect to.
// Therefore, server with a remote that are not already in observer mode, need to be put into it.
if solicited && meta != nil && !meta.IsObserver() {
- meta.setObserver(true, extExtended)
c.Debugf("Turning JetStream metadata controller Observer Mode on - System Account Connected")
- // Take note that the domain was not extended to avoid this state next startup.
- writePeerState(js.config.StoreDir, meta.currentPeerState())
- // If this server is the leader already, step down so a new leader can be elected (that is not an observer)
- meta.StepDown()
+ // Discard any local metagroup state accumulated before the SYS-account
+ // leaf came up (e.g. the wrong-hint case where this server bootstrapped
+ // its own metagroup). The parent's view is now authoritative; without
+ // this reset the two raft logs stay forked because the standalone log's
+ // commit prefix short-circuits the follower's AE handling.
+ meta.setObserver(true, extExtended)
+ meta.Reset()
}
} else {
// This deny is needed in all cases (system account shared or not)
@@ -2586,31 +2607,48 @@ func (acc *Account) updateLeafNodesEx(sub *subscription, delta int32, hubOnly bo
// Do this once.
subject := string(sub.subject)
- // Walk the connected leafnodes.
- for _, ln := range acc.lleafs {
+ // Walk the connected leafnodes from a random starting point to avoid
+ // concurrent callers all contending over leafs in the same order.
+ nleafs := len(acc.lleafs)
+ start := 0
+ if nleafs > 1 {
+ start = rand.Intn(nleafs)
+ }
+ for i := 0; i < nleafs; i++ {
+ ln := acc.lleafs[(start+i)%nleafs]
if ln == sub.client {
continue
}
- ln.mu.Lock()
+ ln.mu.RLock()
// Don't advertise interest from leafnodes to other isolated leafnodes.
if sub.client.kind == LEAF && ln.isIsolatedLeafNode() {
- ln.mu.Unlock()
+ ln.mu.RUnlock()
continue
}
// If `hubOnly` is true, it means that we want to update only leafnodes
// that connect to this server (so isHubLeafNode() would return `true`).
if hubOnly && !ln.isHubLeafNode() {
- ln.mu.Unlock()
+ ln.mu.RUnlock()
continue
}
// Check to make sure this sub does not have an origin cluster that matches the leafnode.
// If skipped, make sure that we still let go the "$LDS." subscription that allows
// the detection of loops as long as different cluster.
clusterDifferent := cluster != ln.remoteCluster()
- if (isLDS && clusterDifferent) || ((cluster == _EMPTY_ || clusterDifferent) && (delta <= 0 || ln.canSubscribe(subject))) {
- ln.updateSmap(sub, delta, isLDS)
+ update := (isLDS && clusterDifferent) ||
+ ((cluster == _EMPTY_ || clusterDifferent) && (delta <= 0 || ln.canSubscribeInternal(subject)))
+ ln.mu.RUnlock()
+ if update {
+ ln.mu.Lock()
+ // The leaf role, isolation mode, and remote cluster are stable
+ // for the connection. Recheck canSubscribe here since permissions
+ // can change, and to initializes mperms for wildcard subscriptions
+ // that collide with deny rules.
+ if isLDS || delta <= 0 || ln.canSubscribe(subject) {
+ ln.updateSmap(sub, delta, isLDS)
+ }
+ ln.mu.Unlock()
}
- ln.mu.Unlock()
}
}
@@ -3299,35 +3337,48 @@ func (c *client) leafMsgAllowed() bool {
return true
}
- c.mu.Lock()
- defer c.mu.Unlock()
-
+ c.mu.RLock()
if c.isSpokeLeafNode() {
// Gateway routed replies are forwarded without
// permission checks.
if isGW || c.leafReceiveAllowed(subjectToCheck) {
+ c.mu.RUnlock()
return true
}
} else if c.leafSendAllowed(subjectToCheck) {
+ c.mu.RUnlock()
return true
}
+
+ // If allow_responses is not configured, or there is no tracked reply for
+ // this subject, the answer is "denied" and we can return it while still
+ // holding only the read lock.
+ replySubject := bytesToString(wireSubject)
+ if c.perms == nil || c.perms.resp == nil || c.replies[replySubject] == nil {
+ c.mu.RUnlock()
+ return false
+ }
+ c.mu.RUnlock()
+
// Check tracked reply permissions (allow_responses).
// Use the pre-strip subject since deliverMsg tracks
// replies under the original form, which includes
// the GW routing prefix for routed requests.
- return c.responseAllowed(bytesToString(wireSubject))
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ return c.responseAllowed(replySubject)
}
// Returns true if the leaf side ACLs allow importing this subject,
// based on the permissions received over INFO and any local deny_imports.
-// Lock must be held.
+// At least a read lock must be held.
func (c *client) leafReceiveAllowed(subject []byte) bool {
- return c.canSubscribe(bytesToString(subject))
+ return c.canSubscribeInternal(bytesToString(subject))
}
// Returns true if the hub side ACLs allow the remote leaf to send
// this subject.
-// Lock must be held.
+// At least a read lock must be held.
func (c *client) leafSendAllowed(bsubject []byte) bool {
// Use the original export ACL captured for this accepted leaf.
// The live perms also contain additional JetStream denies used by
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/memstore.go b/vendor/github.com/nats-io/nats-server/v2/server/memstore.go
index 4e3f113b3b..9cce5cc184 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/memstore.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/memstore.go
@@ -961,7 +961,7 @@ func (ms *memStore) NumPendingMulti(sseq uint64, sl *gsl.SimpleSublist, lastPerS
var havePartial bool
var totalSkipped uint64
// We will track start and end sequences as we go.
- stree.IntersectGSL[SimpleState](ms.fss, sl, func(subj []byte, fss *SimpleState) {
+ stree.IntersectGSL[SimpleState](ms.fss, sl, func(subj []byte, fss *SimpleState) bool {
if fss.firstNeedsUpdate || fss.lastNeedsUpdate {
ms.recalculateForSubj(bytesToString(subj), fss)
}
@@ -975,6 +975,7 @@ func (ms *memStore) NumPendingMulti(sseq uint64, sl *gsl.SimpleSublist, lastPerS
} else {
totalSkipped += fss.Msgs
}
+ return true
})
// If we did not encounter any partials we can return here.
@@ -2595,13 +2596,15 @@ func (o *consumerMemStore) UpdateAcks(dseq, sseq uint64) error {
return ErrNoAckPolicy
}
+ // We do this regardless.
+ delete(o.state.Redelivered, sseq)
+
// On restarts the old leader may get a replay from the raft logs that are old.
if dseq <= o.state.AckFloor.Consumer {
return nil
}
if len(o.state.Pending) == 0 || o.state.Pending[sseq] == nil {
- delete(o.state.Redelivered, sseq)
return ErrStoreMsgNotFound
}
@@ -2655,12 +2658,23 @@ func (o *consumerMemStore) UpdateAcks(dseq, sseq uint64) error {
}
}
}
- // We do these regardless.
- delete(o.state.Redelivered, sseq)
return nil
}
+func (o *consumerMemStore) RemoveRedeliveredBelow(seq uint64) {
+ if seq == 0 {
+ return
+ }
+ o.mu.Lock()
+ defer o.mu.Unlock()
+ for s := range o.state.Redelivered {
+ if s < seq {
+ delete(o.state.Redelivered, s)
+ }
+ }
+}
+
func (o *consumerMemStore) UpdateConfig(cfg *ConsumerConfig) error {
o.mu.Lock()
defer o.mu.Unlock()
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/monitor.go b/vendor/github.com/nats-io/nats-server/v2/server/monitor.go
index 6d4460b238..326c0855b8 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/monitor.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/monitor.go
@@ -1271,10 +1271,14 @@ type Varz struct {
Routes int `json:"routes"` // Routes is the number of connected route servers
Remotes int `json:"remotes"` // Remotes is the configured route remote endpoints
Leafs int `json:"leafnodes"` // Leafs is the number connected leafnode clients
- InMsgs int64 `json:"in_msgs"` // InMsgs is the number of messages this server received
- OutMsgs int64 `json:"out_msgs"` // OutMsgs is the number of message this server sent
- InBytes int64 `json:"in_bytes"` // InBytes is the number of bytes this server received
- OutBytes int64 `json:"out_bytes"` // OutMsgs is the number of bytes this server sent
+ InMsgs int64 `json:"in_msgs"` // InMsgs is the total number of messages this server received. This includes messages from the clients, routers, gateways and leaf nodes
+ InBytes int64 `json:"in_bytes"` // InBytes is the total number of bytes this server received. This includes messages from the clients, routers, gateways and leaf nodes
+ InClientMsgs int64 `json:"in_client_msgs"` // InClientMsgs is the number of messages this server received from the clients
+ InClientBytes int64 `json:"in_client_bytes"` // InClientBytes is the number of bytes this server received from the clients
+ OutMsgs int64 `json:"out_msgs"` // OutMsgs is the total number of message this server sent. This includes messages sent to the clients, routers, gateways and leaf nodes
+ OutBytes int64 `json:"out_bytes"` // OutBytes is the total number of bytes this server sent. This includes messages sent to the clients, routers, gateways and leaf nodes
+ OutClientMsgs int64 `json:"out_client_msgs"` // OutClientMsgs is the number of messages this server sent to the clients
+ OutClientBytes int64 `json:"out_client_bytes"` // OutClientBytes is the number of bytes this server sent to the clients
SlowConsumers int64 `json:"slow_consumers"` // SlowConsumers is the total count of clients that were disconnected since start due to being slow consumers
StaleConnections int64 `json:"stale_connections"` // StaleConnections is the total count of stale connections that were detected
StalledClients int64 `json:"stalled_clients"` // StalledClients is the total number of times that clients have been stalled.
@@ -1877,6 +1881,10 @@ func (s *Server) updateVarzRuntimeFields(v *Varz, forceUpdate bool, pcpu float64
v.InBytes = atomic.LoadInt64(&s.inBytes)
v.OutMsgs = atomic.LoadInt64(&s.outMsgs)
v.OutBytes = atomic.LoadInt64(&s.outBytes)
+ v.InClientMsgs = atomic.LoadInt64(&s.inClientMsgs)
+ v.InClientBytes = atomic.LoadInt64(&s.inClientBytes)
+ v.OutClientMsgs = atomic.LoadInt64(&s.outClientMsgs)
+ v.OutClientBytes = atomic.LoadInt64(&s.outClientBytes)
v.SlowConsumers = atomic.LoadInt64(&s.slowConsumers)
v.StalledClients = atomic.LoadInt64(&s.stalls)
v.SlowConsumersStats = &SlowConsumersStats{
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/mqtt.go b/vendor/github.com/nats-io/nats-server/v2/server/mqtt.go
index 7b0e5c2fdb..4ff181b2ad 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/mqtt.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/mqtt.go
@@ -239,7 +239,7 @@ var (
errMQTTEmptyUsername = errors.New("empty user name not allowed")
errMQTTTopicIsEmpty = errors.New("topic cannot be empty")
errMQTTPacketIdentifierIsZero = errors.New("packet identifier cannot be 0")
- errMQTTUnsupportedCharacters = errors.New("character ' ' not supported for MQTT topics")
+ errMQTTUnsupportedCharacters = errors.New("character not supported for MQTT topics")
errMQTTInvalidSession = errors.New("invalid MQTT session")
errMQTTInvalidRetainFlags = errors.New("invalid retained message flags")
errMQTTSessionCollision = errors.New("stored session does not match client ID")
@@ -5713,8 +5713,11 @@ func mqttToNATSSubjectConversion(mt []byte, wcOk bool) ([]byte, error) {
}
res = append(res, btsep)
}
- case ' ':
- // As of now, we cannot support ' ' in the MQTT topic/filter.
+ case ' ', '\t', '\n', '\r', '\f':
+ // We cannot support whitespace in the MQTT topic/filter — these
+ // characters would also corrupt the NATS wire protocol when the
+ // subject is forwarded to other connection types (e.g. leaf
+ // nodes) where the resulting control line could be split.
return nil, errMQTTUnsupportedCharacters
case 0x7f:
// SubjectTree uses DEL as an internal pivot marker, so retained
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/raft.go b/vendor/github.com/nats-io/nats-server/v2/server/raft.go
index 5055297e71..9f33e19286 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/raft.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/raft.go
@@ -86,6 +86,7 @@ type RaftNode interface {
WaitForStop()
Delete()
IsDeleted() bool
+ Reset()
RecreateInternalSubs() error
IsSystemAccount() bool
GetTrafficAccountName() string
@@ -344,6 +345,7 @@ var (
errNoInternalClient = errors.New("raft: no internal client")
errMembershipChange = errors.New("raft: membership change in progress")
errRemoveLastNode = errors.New("raft: cannot remove the last peer")
+ errPeerNotFound = errors.New("raft: peer not found")
)
// This will bootstrap a raftNode by writing its config into the store directory.
@@ -1046,7 +1048,10 @@ func (n *raft) ProposeRemovePeer(peer string) error {
n.RUnlock()
return errMembershipChange
}
-
+ if _, ok := n.peers[peer]; !ok {
+ n.RUnlock()
+ return errPeerNotFound
+ }
if len(n.peers) <= 1 {
n.RUnlock()
return errRemoveLastNode
@@ -1383,6 +1388,11 @@ func (n *raft) installSnapshot(snap *snapshot) error {
return err
}
+ // If installing a snapshot past our commits, clear the cache.
+ if snap.lastIndex > n.commit && len(n.pae) > 0 {
+ n.pae = make(map[uint64]*appendEntry)
+ }
+
var state StreamState
n.wal.FastState(&state)
n.papplied = snap.lastIndex
@@ -1543,12 +1553,20 @@ func (c *checkpoint) InstallSnapshot(data []byte) (uint64, error) {
n.Unlock()
err := writeFileWithSync(c.snapFile, encoded, defaultFilePerms)
n.Lock()
+ // On either failure path, drop the file we just wrote so it doesn't get
+ // picked up by setupLastSnapshot on restart. Skip the remove if it's the
+ // snapshot already adopted into n.snapfile for this term/applied.
if err != nil {
+ if c.snapFile != n.snapfile {
+ os.Remove(c.snapFile)
+ }
// We could set write err here, but if this is a temporary situation, too many open files etc.
// we want to retry and snapshots are not fatal.
return 0, err
} else if !n.snapshotting {
- // The checkpoint can be aborted at any time, don't continue if that happened.
+ if c.snapFile != n.snapfile {
+ os.Remove(c.snapFile)
+ }
return 0, errSnapAborted
}
@@ -1610,6 +1628,9 @@ func termAndIndexFromSnapFile(sn string) (term, index uint64, err error) {
if n, err := fmt.Sscanf(fn, snapFileT, &term, &index); err != nil || n != 2 {
return 0, 0, errBadSnapName
}
+ if fn != fmt.Sprintf(snapFileT, term, index) {
+ return 0, 0, errBadSnapName
+ }
return term, index, nil
}
@@ -2220,6 +2241,64 @@ func (n *raft) shutdown() {
}
}
+// Reset discards this node's local raft state (log, snapshots, peer set,
+// term/vote) so it can be caught up cleanly by another group with the same
+// name. The caller is responsible for parking the node first (typically via
+// SetObserver) if it should not compete for leadership immediately after;
+// Reset itself steps the node down but does not flip observer mode.
+func (n *raft) Reset() {
+ n.Lock()
+ defer n.Unlock()
+
+ n.debug("Resetting Raft state")
+
+ n.stepdownLocked(_EMPTY_)
+
+ // Cancel any in-flight catchup so it does not race the reset.
+ n.cancelCatchup()
+
+ // Drop proposals and inbound entries; they are no longer meaningful
+ // against whatever log this node ends up following.
+ n.prop.drain()
+ n.entry.drain()
+ n.resp.drain()
+ n.apply.drain()
+ n.reqs.drain()
+ n.votes.drain()
+
+ // Remove every snapshot under our snapshots dir, not just the one referenced
+ // by n.snapfile. Orphans (e.g. from a crash between install and the previous
+ // file's removal) would otherwise be picked up by setupLastSnapshot on the
+ // next restart and reseed the state we are discarding here.
+ snapDir := filepath.Join(n.sd, snapshotsDir)
+ if err := os.RemoveAll(snapDir); err != nil {
+ n.warn("Error removing snapshots directory during reset: %v", err)
+ }
+ if err := os.MkdirAll(snapDir, defaultDirPerms); err != nil {
+ n.warn("Error recreating snapshots directory during reset: %v", err)
+ }
+ n.snapfile = _EMPTY_
+
+ // Abort any inflight async snapshot checkpoint.
+ n.snapshotting = false
+
+ // Reset the WAL, but reset these first to not trip the assertion.
+ n.commit, n.hcommit, n.applied, n.processed, n.papplied = 0, 0, 0, 0, 0
+ n.resetWAL()
+
+ // Reset peer set to just ourselves; a new leader will fold us back into
+ // the cluster's membership view via processPeerState.
+ n.peers = map[string]*lps{n.id: {time.Time{}, 0, true}}
+ n.removed = nil
+ n.adjustClusterSizeAndQuorum()
+
+ n.term, n.vote = 0, _EMPTY_
+ n.writeTermVote()
+
+ // Persist the cleared peer state so a restart picks up the reset.
+ n.writePeerState(n.currentPeerStateLocked())
+}
+
const (
raftAllSubj = "$NRG.>"
raftVoteSubj = "$NRG.V.%s"
@@ -2904,6 +2983,16 @@ func (n *raft) handleForwardedRemovePeerProposal(sub *subscription, c *client, _
n.RUnlock()
return
}
+ if _, ok := n.peers[string(msg)]; !ok {
+ n.debug("Ignoring forwarded peer removal proposal, peer not found")
+ n.RUnlock()
+ return
+ }
+ if len(n.peers) <= 1 {
+ n.debug("Ignoring forwarded peer removal proposal, remove last node")
+ n.RUnlock()
+ return
+ }
prop := n.prop
n.RUnlock()
@@ -3901,6 +3990,19 @@ func (n *raft) truncateWAL(term, index uint64) {
// Set after we know we have truncated properly.
n.pterm, n.pindex = term, index
+ // Invalidate cached entries the WAL no longer has.
+ if index == 0 {
+ if len(n.pae) > 0 {
+ n.pae = make(map[uint64]*appendEntry)
+ }
+ } else {
+ for k := range n.pae {
+ if k > index {
+ delete(n.pae, k)
+ }
+ }
+ }
+
// Check if we're truncating an uncommitted membership change.
if n.membChangeIndex > 0 && n.membChangeIndex > index {
n.membChangeIndex = 0
@@ -4255,13 +4357,10 @@ func (n *raft) processAppendEntry(ae *appendEntry, sub *subscription) {
// Inherit state from appendEntry with the leader's snapshot.
hadPreviousSnapshot := n.snapfile != _EMPTY_
- n.pindex = ae.pindex
- n.pterm = ae.pterm
- n.commit = ae.pindex
snap := &snapshot{
- lastTerm: n.pterm,
- lastIndex: n.pindex,
+ lastTerm: ae.pterm,
+ lastIndex: ae.pindex,
peerstate: encodePeerState(&peerState{n.peerNames(), n.csz, n.extSt}),
data: ae.entries[0].Data,
}
@@ -4271,6 +4370,9 @@ func (n *raft) processAppendEntry(ae *appendEntry, sub *subscription) {
n.Unlock()
return
}
+ n.pindex = ae.pindex
+ n.pterm = ae.pterm
+ n.commit = ae.pindex
n.resetInitializing()
if !hadPreviousSnapshot {
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/route.go b/vendor/github.com/nats-io/nats-server/v2/server/route.go
index a0384ad35c..19fd13c809 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/route.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/route.go
@@ -2015,7 +2015,7 @@ func (s *Server) createRoute(conn net.Conn, rURL *url.URL, rtype RouteType, goss
pingInterval = opts.Cluster.PingInterval
}
if opts.Cluster.MaxPingsOut > 0 {
- pingMax = opts.MaxPingsOut
+ pingMax = opts.Cluster.MaxPingsOut
}
c.watchForStaleConnection(adjustPingInterval(ROUTER, pingInterval), pingMax)
} else {
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/server.go b/vendor/github.com/nats-io/nats-server/v2/server/server.go
index 9612c868c1..411102f17a 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/server.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/server.go
@@ -402,9 +402,13 @@ type nodeInfo struct {
type stats struct {
inMsgs int64
- outMsgs int64
inBytes int64
+ inClientMsgs int64
+ inClientBytes int64
+ outMsgs int64
outBytes int64
+ outClientMsgs int64
+ outClientBytes int64
slowConsumers int64
staleConnections int64
stalls int64
@@ -2555,6 +2559,10 @@ func (s *Server) Shutdown() {
if s == nil {
return
}
+ // Prevent issues with multiple calls.
+ if !s.shutdown.CompareAndSwap(false, true) {
+ return
+ }
// This is for JetStream R1 Pull Consumers to allow signaling
// that pending pull requests are invalid.
s.signalPullConsumers()
@@ -2568,11 +2576,6 @@ func (s *Server) Shutdown() {
// eventing items associated with accounts.
s.shutdownEventing()
- // Prevent issues with multiple calls.
- if s.isShuttingDown() {
- return
- }
-
s.mu.Lock()
s.Noticef("Initiating Shutdown...")
@@ -2580,7 +2583,6 @@ func (s *Server) Shutdown() {
opts := s.getOpts()
- s.shutdown.Store(true)
s.running.Store(false)
s.grMu.Lock()
s.grRunning = false
@@ -3411,8 +3413,12 @@ func (s *Server) createClientEx(conn net.Conn, inProcess bool) *client {
}
}
- // Check for proxy protocol if enabled.
- if !isClosed && !tlsRequired && opts.ProxyProtocol {
+ // Check for proxy protocol if enabled. The PROXY header is sent as
+ // plaintext before any TLS handshake per the spec, so we must read it
+ // before doing TLS even when TLS is required. Any bytes read past the
+ // header are kept in `pre` and replayed into the TLS handshake (or the
+ // non-TLS protocol parser) by the tlsMixConn wrapper used below.
+ if !isClosed && opts.ProxyProtocol {
if len(pre) == 0 {
// There has been no pre-read yet, do so so we can work out
// if the client is trying to negotiate PROXY.
@@ -3640,7 +3646,11 @@ func tlsTimeout(c *client, conn *tls.Conn) {
}
cs := conn.ConnectionState()
if !cs.HandshakeComplete {
- c.Errorf("TLS handshake timeout")
+ if c.kind == CLIENT || c.kind == LEAF {
+ c.Debugf("TLS handshake timeout")
+ } else {
+ c.Errorf("TLS handshake timeout")
+ }
c.sendErr("Secure Connection - TLS Required")
c.closeConnection(TLSHandshakeError)
}
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/store.go b/vendor/github.com/nats-io/nats-server/v2/server/store.go
index 31a834efac..f30eddcddc 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/store.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/store.go
@@ -364,6 +364,7 @@ type ConsumerStore interface {
HasState() bool
UpdateDelivered(dseq, sseq, dc uint64, ts int64) error
UpdateAcks(dseq, sseq uint64) error
+ RemoveRedeliveredBelow(seq uint64)
UpdateConfig(cfg *ConsumerConfig) error
Update(*ConsumerState) error
ForceUpdate(*ConsumerState) error
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stream.go b/vendor/github.com/nats-io/nats-server/v2/server/stream.go
index 5698f3cb2a..03ac8b2def 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/stream.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/stream.go
@@ -264,7 +264,7 @@ type PubAck struct {
Duplicate bool `json:"duplicate,omitempty"`
Value string `json:"val,omitempty"`
BatchId string `json:"batch,omitempty"`
- BatchSize int `json:"count,omitempty"`
+ BatchSize uint64 `json:"count,omitempty"`
}
// CounterValue is the body of a message when used as a counter.
@@ -571,6 +571,7 @@ type stream struct {
mirrorLastBySub *subscription // Mirrors only.
monitorWg sync.WaitGroup // Wait group for the monitor routine.
+ monitorMu sync.Mutex // Serializes monitorWg's Add against Wait to prevent a WaitGroup reuse panic.
// If standalone/single-server, the offline reason needs to be stored directly in the stream.
// Otherwise, if clustered it will be part of the stream assignment.
@@ -582,8 +583,9 @@ type stream struct {
// inflightSubjectRunningTotal stores a running total of inflight messages for a specific subject.
type inflightSubjectRunningTotal struct {
- bytes uint64 // Running total of inflight bytes for inflight messages.
- ops uint64 // Inflight operations, i.e. inflight messages for this subject. If this reaches zero, we can remove the running total.
+ bytes uint64 // Running total of inflight bytes for inflight messages.
+ ops uint64 // Inflight operations, i.e. inflight messages for this subject. If this reaches zero, we can remove the running total.
+ schedule bool // Marks whether the last message is a schedule.
}
// msgCounterRunningTotal stores a running total and a number of inflight
@@ -816,7 +818,7 @@ func (a *Account) addStreamWithAssignment(config *StreamConfig, fsConfig *FileSt
if isClustered {
_, reserved = js.tieredStreamAndReservationCount(a.Name, tier, cfg)
}
- if err := js.checkAllLimits(&selected, cfg, reserved, 0); err != nil {
+ if err := js.checkAllLimits(&selected, tier, cfg, reserved, 0); err != nil {
js.mu.RUnlock()
return nil, err
}
@@ -2340,11 +2342,10 @@ func (jsa *jsAccount) configUpdateCheck(old, new *StreamConfig, s *Server, pedan
// Save the user configured MaxBytes.
newMaxBytes := cfg.MaxBytes
- maxBytesOffset := int64(0)
// We temporarily set cfg.MaxBytes to maxBytesDiff because checkAllLimits
// adds cfg.MaxBytes to the current reserved limit and checks if we've gone
- // over. However, we don't want an addition cfg.MaxBytes, we only want to
+ // over. However, we don't want an additional cfg.MaxBytes, we only want to
// reserve the difference between the new and the old values.
cfg.MaxBytes = maxBytesDiff
@@ -2371,15 +2372,13 @@ func (jsa *jsAccount) configUpdateCheck(old, new *StreamConfig, s *Server, pedan
if isClustered {
_, reserved = js.tieredStreamAndReservationCount(acc.Name, tier, &cfg)
}
- // reservation does not account for this stream, hence add the old value
- if old.MaxBytes > 0 {
- if tier == _EMPTY_ && old.Replicas > 1 {
- reserved = addSaturate(reserved, mulSaturate(int64(old.Replicas), old.MaxBytes))
- } else {
- reserved = addSaturate(reserved, old.MaxBytes)
- }
- }
- if err := js.checkAllLimits(&selected, &cfg, reserved, maxBytesOffset); err != nil {
+ // reserved covers only the other streams. checkAllLimits adds this stream's
+ // footprint via cfg.MaxBytes, which is currently maxBytesDiff, so it only
+ // adds the diff. Add the remaining (newMaxBytes - maxBytesDiff) here so the
+ // two together equal this stream's true new footprint, even when Replicas
+ // changes on update.
+ reserved = addSaturate(reserved, accountReservation(tier, cfg.Replicas, newMaxBytes-maxBytesDiff))
+ if err := js.checkAllLimits(&selected, tier, &cfg, reserved, 0); err != nil {
return nil, err
}
// Restore the user configured MaxBytes.
@@ -3018,14 +3017,20 @@ func (mset *stream) retryDisconnectedSyncConsumers() {
clientClosed := func(c *client) bool {
return c != nil && (c.flags.isSet(closeConnection) || c.flags.isSet(connMarkedClosed))
}
- // Stale sources need to be reset: we expect a heartbeat every sourceHealthHB, so missing a couple
- // is a strong signal the remote delivery is no longer reaching us and a retry is warranted.
+ // Stale sources need to be reset: if not seen past the health check interval, it's stale.
stale := func(si *sourceInfo) bool {
- return time.Since(time.Unix(0, si.last.Load())) > 2*sourceHealthHB
+ return time.Since(time.Unix(0, si.last.Load())) > sourceHealthCheckInterval
}
shouldRetry := func(si *sourceInfo) bool {
- if si != nil && (si.sip || si.sub == nil || clientClosed(si.sub.client) || stale(si)) {
- si.fails, si.sip = 0, false
+ if si != nil && !si.sip && (si.sub == nil || clientClosed(si.sub.client) || stale(si)) {
+ // Skip if a recreate is already scheduled and we can't cancel it.
+ if t, ok := mset.sourceSetupSchedules[si.iname]; ok {
+ if !t.Stop() {
+ return false
+ }
+ delete(mset.sourceSetupSchedules, si.iname)
+ }
+ si.fails = 0
mset.cancelSourceInfo(si)
return true
}
@@ -3205,7 +3210,13 @@ func (mset *stream) processInboundMirrorMsg(m *inMsg) bool {
} else {
// If the deliver sequence matches then the upstream stream has expired or deleted messages.
if dseq == mset.mirror.dseq+1 {
- mset.skipMsgs(mset.mirror.sseq+1, sseq-1)
+ if err := mset.skipMsgs(mset.mirror.sseq+1, sseq-1); err != nil {
+ mset.mirror.sseq = osseq
+ mset.mirror.dseq = odseq
+ mset.mu.Unlock()
+ mset.retryMirrorConsumer()
+ return false
+ }
mset.mirror.dseq++
mset.mirror.sseq = sseq
} else {
@@ -3263,7 +3274,7 @@ func (mset *stream) processInboundMirrorMsg(m *inMsg) bool {
if err != nil {
if strings.Contains(err.Error(), "no space left") {
s.Errorf("JetStream out of space, will be DISABLED")
- s.DisableJetStream()
+ s.ShutdownJetStream()
return false
}
if err != errLastSeqMismatch {
@@ -3274,20 +3285,18 @@ func (mset *stream) processInboundMirrorMsg(m *inMsg) bool {
accName, sname, err)
} else {
// We may have missed messages, restart.
- if lseq := mset.lastSeq(); sseq <= lseq {
- mset.mu.Lock()
+ lseq := mset.lastSeq()
+ mset.mu.Lock()
+ if mset.mirror != nil {
mset.mirror.lag = olag
- mset.mirror.sseq = lseq
- mset.mirror.dseq = odseq
- mset.mu.Unlock()
- return false
- } else {
- mset.mu.Lock()
mset.mirror.dseq = odseq
mset.mirror.sseq = osseq
- mset.mu.Unlock()
- mset.retryMirrorConsumer()
+ if sseq <= lseq {
+ mset.mirror.sseq = lseq
+ }
}
+ mset.mu.Unlock()
+ mset.retryMirrorConsumer()
}
}
return err == nil
@@ -3324,20 +3333,21 @@ func (mset *stream) retryMirrorConsumer() error {
}
// Lock should be held.
-func (mset *stream) skipMsgs(start, end uint64) {
+func (mset *stream) skipMsgs(start, end uint64) error {
node, store := mset.node, mset.store
// If we are not clustered we can short circuit now with store.SkipMsgs
if node == nil {
- store.SkipMsgs(start, end-start+1)
+ if err := store.SkipMsgs(start, end-start+1); err != nil {
+ return err
+ }
mset.lseq = end
- return
+ return nil
}
// Must only be enabled once every peer in the cluster supports receiving
// deleteRangeOp in the normal apply path; older peers panic on unknown ops.
if mset.srv.getOpts().getFeatureFlag(FeatureFlagJsRaftDeleteRange) {
- node.Propose(encodeDeleteRange(&DeleteRange{First: start, Num: end - start + 1}))
- return
+ return node.Propose(encodeDeleteRange(&DeleteRange{First: start, Num: end - start + 1}))
}
var entries []*Entry
@@ -3345,7 +3355,9 @@ func (mset *stream) skipMsgs(start, end uint64) {
entries = append(entries, newEntry(EntryNormal, encodeStreamMsg(_EMPTY_, _EMPTY_, nil, nil, seq-1, 0, false)))
// So a single message does not get too big.
if len(entries) > 10_000 {
- node.ProposeMulti(entries)
+ if err := node.ProposeMulti(entries); err != nil {
+ return err
+ }
// We need to re-create `entries` because there is a reference
// to it in the node's pae map.
entries = entries[:0]
@@ -3353,8 +3365,9 @@ func (mset *stream) skipMsgs(start, end uint64) {
}
// Send all at once.
if len(entries) > 0 {
- node.ProposeMulti(entries)
+ return node.ProposeMulti(entries)
}
+ return nil
}
const (
@@ -3718,13 +3731,25 @@ func (mset *stream) setupMirrorConsumer() error {
state = StreamState{}
mset.store.FastState(&state)
if state.LastSeq < ccr.ConsumerInfo.Delivered.Stream {
+ // Local helper: abort consumer setup, leaving the mirror in its
+ // pre-setup state so the retry path can re-create it cleanly.
+ failSetup := func(setupErr error) {
+ mset.cancelSourceInfo(mirror)
+ mirror.err = NewJSMirrorConsumerSetupFailedError(setupErr, Unless(setupErr))
+ retry = true
+ mset.mu.Unlock()
+ }
// Check to see if delivered is past our last and we have no msgs. This will help the
// case when mirroring a stream that has a very high starting sequence number.
if state.Msgs == 0 && ccr.ConsumerInfo.Delivered.Stream > state.LastSeq {
- mset.store.PurgeEx(_EMPTY_, ccr.ConsumerInfo.Delivered.Stream+1, 0)
+ if _, err := mset.store.PurgeEx(_EMPTY_, ccr.ConsumerInfo.Delivered.Stream+1, 0); err != nil {
+ failSetup(err)
+ return
+ }
mset.lseq = ccr.ConsumerInfo.Delivered.Stream
- } else {
- mset.skipMsgs(state.LastSeq+1, ccr.ConsumerInfo.Delivered.Stream)
+ } else if err := mset.skipMsgs(state.LastSeq+1, ccr.ConsumerInfo.Delivered.Stream); err != nil {
+ failSetup(err)
+ return
}
}
@@ -4399,7 +4424,7 @@ func (mset *stream) processInboundSourceMsg(si *sourceInfo, m *inMsg) bool {
s := mset.srv
if strings.Contains(err.Error(), "no space left") {
s.Errorf("JetStream out of space, will be DISABLED")
- s.DisableJetStream()
+ s.ShutdownJetStream()
} else {
mset.mu.RLock()
accName, sname, iName := mset.acc.Name, mset.cfg.Name, si.iname
@@ -5019,10 +5044,14 @@ func (mset *stream) deleteAtomicBatches(shuttingDown bool) {
// Lock should be held.
func (mset *stream) deleteBatchApplyState() {
if batch := mset.batchApply; batch != nil {
- // Need to return entries (if any) to the pool.
+ // Clear under batch.mu so a stale reference held by the stream monitor
+ // can't re-pool entries we already returned.
+ batch.mu.Lock()
for _, bce := range batch.entries {
bce.ReturnToPool()
}
+ batch.clearBatchStateLocked()
+ batch.mu.Unlock()
mset.batchApply = nil
}
}
@@ -5176,9 +5205,12 @@ func (mset *stream) storeUpdates(md, bd int64, seq uint64, subj string) {
mset.clsMu.RUnlock()
} else if md < 0 {
// Batch decrements we need to force consumers to re-calculate num pending.
+ var ss StreamState
+ mset.store.FastState(&ss)
mset.clsMu.RLock()
for _, o := range mset.cList {
o.streamNumPendingLocked()
+ o.removeRedeliveredBelow(ss.FirstSeq)
}
mset.clsMu.RUnlock()
}
@@ -5558,15 +5590,14 @@ func getFastBatch(reply string, hdr []byte) (*FastBatch, bool) {
if o = strings.LastIndexByte(reply[:o], '.'); o == -1 {
return nil, true
}
- a := parseInt64(stringToBytes(reply[o+1 : p]))
- if a < 1 {
+ seq, ok := parseUint64(stringToBytes(reply[o+1 : p]))
+ // Reject math.MaxUint64 to prevent b.lseq overflowing on the next b.lseq++.
+ if !ok || seq == 0 || seq == math.MaxUint64 {
return nil, true
}
- b.seq = uint64(a)
+ b.seq = seq
p = o
- if b.seq <= 0 {
- return nil, true
- } else if b.seq == 1 && b.commitEob {
+ if b.seq == 1 && b.commitEob {
return nil, true
}
if op == FastBatchOpStart && b.seq != 1 {
@@ -5590,7 +5621,7 @@ func getFastBatch(reply string, hdr []byte) (*FastBatch, bool) {
if o = strings.LastIndexByte(reply[:o], '.'); o == -1 {
return nil, true
}
- a = parseInt64(stringToBytes(reply[o+1 : p]))
+ a := parseInt64(stringToBytes(reply[o+1 : p]))
if a <= 0 {
a = 10
} else if a > math.MaxUint16 {
@@ -5613,7 +5644,7 @@ func getBatchSequence(hdr []byte) (uint64, bool) {
if len(bseq) == 0 {
return 0, false
}
- return uint64(parseInt64(bseq)), true
+ return parseUint64(bseq)
}
// Signal if we are clustered. Will acquire rlock.
@@ -6528,6 +6559,23 @@ func (mset *stream) processJetStreamMsgWithBatch(subject, reply string, hdr, msg
outq.sendMsg(reply, b)
}
return apiErr
+ } else {
+ // Check that the to-be-purged subject is a schedule message.
+ // We still allow this message through if there exists no message for this subject,
+ // to remain backward-compatible. An "expected at sequence" check can still be
+ // performed to make this stricter.
+ var smv StoreMsg
+ sm, _ := store.LoadLastMsg(bytesToString(scheduler), &smv)
+ if sm != nil && len(sliceHeader(JSSchedulePattern, sm.hdr)) == 0 {
+ apiErr := NewJSMessageSchedulesSchedulerInvalidError()
+ if canRespond {
+ resp.PubAck = &PubAck{Stream: name}
+ resp.Error = apiErr
+ b, _ := json.Marshal(resp)
+ outq.sendMsg(reply, b)
+ }
+ return apiErr
+ }
}
} else if !sourced && len(sliceHeader(JSScheduler, hdr)) > 0 {
// Clients may only use Nats-Scheduler alongside Nats-Schedule-Next.
@@ -6842,6 +6890,10 @@ func (mset *stream) processJetStreamMsgWithBatch(subject, reply string, hdr, msg
var thdrsOnly bool
if mset.tr != nil {
tsubj, _ = mset.tr.Match(subject)
+ if tsubj != _EMPTY_ && !IsValidPublishSubject(tsubj) {
+ s.RateLimitWarnf("Stream '%s > %s' suppressing republish with invalid subject %q", accName, name, tsubj)
+ tsubj = _EMPTY_ // ... stops the republish.
+ }
if mset.cfg.RePublish != nil {
thdrsOnly = mset.cfg.RePublish.HeadersOnly
}
@@ -6910,7 +6962,7 @@ func (mset *stream) processJetStreamMsgWithBatch(subject, reply string, hdr, msg
if isPermissionError(err) {
// messages in block cache could be lost in the worst case.
// In the clustered mode it is very highly unlikely as a result of replication.
- go mset.srv.DisableJetStream()
+ go mset.srv.ShutdownJetStream()
mset.srv.Warnf("Filesystem permission denied while writing msg, disabling JetStream: %v", err)
return err
}
@@ -8162,10 +8214,7 @@ func (mset *stream) resetAndWaitOnConsumers() {
node.StepDown()
node.Stop()
}
- if o.isMonitorRunning() {
- o.signalMonitorQuit()
- o.monitorWg.Wait()
- }
+ o.stopMonitoring()
}
}
@@ -8269,8 +8318,7 @@ func (mset *stream) stop(deleteFlag, advisory bool) error {
// but should we log?
o.stopWithFlags(deleteFlag, deleteFlag, false, advisory)
if !isShuttingDown {
- o.signalMonitorQuit()
- o.monitorWg.Wait()
+ o.stopMonitoring()
}
}
}
@@ -9000,7 +9048,7 @@ func (a *Account) RestoreStream(ncfg *StreamConfig, r io.Reader) (*stream, error
}
bc += hdr.Size
js.mu.RLock()
- err = js.checkAllLimits(&selected, &cfg, reserved, bc)
+ err = js.checkAllLimits(&selected, tier, &cfg, reserved, bc)
js.mu.RUnlock()
if err != nil {
return nil, err
@@ -9176,6 +9224,28 @@ func (mset *stream) checkConsumerReplication() {
}
}
+// startMonitorWg registers a pending monitor goroutine on monitorWg. It is
+// held under monitorMu so that the monitorWg.Add can never race a concurrent
+// monitorWg.Wait in stopMonitoring. The corresponding monitorWg.Done is done by
+// the monitor goroutine directly and must not be wrapped with monitorMu.
+func (mset *stream) startMonitorWg() {
+ mset.monitorMu.Lock()
+ mset.monitorWg.Add(1)
+ mset.monitorMu.Unlock()
+}
+
+// stopMonitoring signals any running monitor goroutine to quit and waits for
+// it to fully exit.
+func (mset *stream) stopMonitoring() {
+ // monitorMu is held across both the quit signal and the wait so that a
+ // concurrent startMonitorWg cannot slip a new monitor generation in
+ // between.
+ mset.monitorMu.Lock()
+ defer mset.monitorMu.Unlock()
+ mset.signalMonitorQuit()
+ mset.monitorWg.Wait()
+}
+
// Will check if we are running in the monitor already and if not set the appropriate flag.
func (mset *stream) checkInMonitor() bool {
mset.mu.Lock()
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/stree.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/stree.go
index 00ebdc46f2..0c257435dc 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/stree/stree.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/stree.go
@@ -485,7 +485,7 @@ func LazyIntersect[TL, TR any](tl *SubjectTree[TL], tr *SubjectTree[TR], cb func
// IntersectGSL will match all items in the given subject tree that
// have interest expressed in the given sublist. The callback will only be called
// once for each subject, regardless of overlapping subscriptions in the sublist.
-func IntersectGSL[T any, SL comparable](t *SubjectTree[T], sl *gsl.GenericSublist[SL], cb func(subject []byte, val *T)) {
+func IntersectGSL[T any, SL comparable](t *SubjectTree[T], sl *gsl.GenericSublist[SL], cb func(subject []byte, val *T) bool) {
if t == nil || t.root == nil || sl == nil {
return
}
@@ -493,14 +493,14 @@ func IntersectGSL[T any, SL comparable](t *SubjectTree[T], sl *gsl.GenericSublis
_intersectGSL(t.root, _pre[:0], sl, cb)
}
-func _intersectGSL[T any, SL comparable](n node, pre []byte, sl *gsl.GenericSublist[SL], cb func(subject []byte, val *T)) {
+func _intersectGSL[T any, SL comparable](n node, pre []byte, sl *gsl.GenericSublist[SL], cb func(subject []byte, val *T) bool) bool {
if n.isLeaf() {
ln := n.(*leaf[T])
subj := append(pre, ln.suffix...)
if sl.HasInterest(bytesToString(subj)) {
- cb(subj, &ln.value)
+ return cb(subj, &ln.value)
}
- return
+ return true
}
bn := n.base()
pre = append(pre, bn.prefix...)
@@ -512,8 +512,11 @@ func _intersectGSL[T any, SL comparable](n node, pre []byte, sl *gsl.GenericSubl
if !hasInterestForTokens(sl, subj, len(pre)) {
continue
}
- _intersectGSL(cn, pre, sl, cb)
+ if !_intersectGSL(cn, pre, sl, cb) {
+ return false
+ }
}
+ return true
}
// The subject tree can return partial tokens so we need to check starting interest
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/sublist.go b/vendor/github.com/nats-io/nats-server/v2/server/sublist.go
index 7423be0b20..8428f9dfbe 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/sublist.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/sublist.go
@@ -638,9 +638,10 @@ func (s *Sublist) hasInterest(subject string, doLock bool, np, nq *int) bool {
if doLock {
s.RLock()
}
- var matched bool
+ var matched, ok bool
if s.cache != nil {
- if r, ok := s.cache[subject]; ok {
+ var r *SublistResult
+ if r, ok = s.cache[subject]; ok {
if np != nil && nq != nil {
*np += len(r.psubs)
for _, qsub := range r.qsubs {
@@ -653,9 +654,9 @@ func (s *Sublist) hasInterest(subject string, doLock bool, np, nq *int) bool {
if doLock {
s.RUnlock()
}
- if matched {
+ if ok {
atomic.AddUint64(&s.cacheHits, 1)
- return true
+ return matched
}
tsa := [32]string{}
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/util.go b/vendor/github.com/nats-io/nats-server/v2/server/util.go
index ff90838bb8..f2c5f80d39 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/util.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/util.go
@@ -124,6 +124,26 @@ func parseInt64(d []byte) (n int64) {
return n
}
+// parseUint64 expects decimal positive numbers. Returns the value and true on success,
+// or 0 and false on invalid input or overflow.
+func parseUint64(d []byte) (uint64, bool) {
+ if len(d) == 0 {
+ return 0, false
+ }
+ var n uint64
+ for _, dec := range d {
+ if dec < asciiZero || dec > asciiNine {
+ return 0, false
+ }
+ digit := uint64(dec) - asciiZero
+ if n > math.MaxUint64/10 || (n == math.MaxUint64/10 && digit > math.MaxUint64%10) {
+ return 0, false
+ }
+ n = n*10 + digit
+ }
+ return n, true
+}
+
// Helper to move from float seconds to time.Duration
func secondsToDuration(seconds float64) time.Duration {
ttl := seconds * float64(time.Second)
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/websocket.go b/vendor/github.com/nats-io/nats-server/v2/server/websocket.go
index 0ee1ca7db9..c5963c6f28 100644
--- a/vendor/github.com/nats-io/nats-server/v2/server/websocket.go
+++ b/vendor/github.com/nats-io/nats-server/v2/server/websocket.go
@@ -1,4 +1,4 @@
-// Copyright 2020-2025 The NATS Authors
+// Copyright 2020-2026 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
@@ -16,7 +16,6 @@ package server
import (
"bytes"
crand "crypto/rand"
- "crypto/sha1"
"crypto/tls"
"encoding/base64"
"encoding/binary"
@@ -1107,15 +1106,6 @@ func wsGetHostAndPort(tls bool, hostport string) (string, string, error) {
return strings.ToLower(host), port, err
}
-// Concatenate the key sent by the client with the GUID, then computes the SHA1 hash
-// and returns it as a based64 encoded string.
-func wsAcceptKey(key string) string {
- h := sha1.New()
- h.Write([]byte(key))
- h.Write(wsGUID)
- return base64.StdEncoding.EncodeToString(h.Sum(nil))
-}
-
func wsMakeChallengeKey() (string, error) {
p := make([]byte, 16)
if _, err := io.ReadFull(crand.Reader, p); err != nil {
@@ -1131,6 +1121,9 @@ func validateWebsocketOptions(o *Options) error {
if wo.Port == 0 {
return nil
}
+ if !wsAllowedFIPS() {
+ return fmt.Errorf("websocket: cannot be used in FIPS-140 mode when built with this Go version, use Go 1.26 or later")
+ }
// Enforce TLS... unless NoTLS is set to true.
if wo.TLSConfig == nil && !wo.NoTLS {
return errors.New("websocket requires TLS configuration")
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/websocket_go125.go b/vendor/github.com/nats-io/nats-server/v2/server/websocket_go125.go
new file mode 100644
index 0000000000..3bbdfe972c
--- /dev/null
+++ b/vendor/github.com/nats-io/nats-server/v2/server/websocket_go125.go
@@ -0,0 +1,38 @@
+// Copyright 2026 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.
+
+//go:build !go1.26
+
+package server
+
+import (
+ "crypto/fips140"
+ "crypto/sha1"
+ "encoding/base64"
+)
+
+func wsAllowedFIPS() bool {
+ // SHA-1 is not permitted on Go 1.25 FIPS builds because we cannot avoid its
+ // enforcement for Sec-WebSocket-Key and Sec-WebSocket-Accept, it will result
+ // in a panic.
+ return !fips140.Enabled()
+}
+
+// Concatenate the key sent by the client with the GUID, then computes the SHA1 hash
+// and returns it as a based64 encoded string.
+func wsAcceptKey(key string) string {
+ h := sha1.New()
+ h.Write([]byte(key))
+ h.Write(wsGUID)
+ return base64.StdEncoding.EncodeToString(h.Sum(nil))
+}
diff --git a/vendor/github.com/nats-io/nats-server/v2/server/websocket_go126.go b/vendor/github.com/nats-io/nats-server/v2/server/websocket_go126.go
new file mode 100644
index 0000000000..d6f38634e3
--- /dev/null
+++ b/vendor/github.com/nats-io/nats-server/v2/server/websocket_go126.go
@@ -0,0 +1,42 @@
+// Copyright 2026 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.
+
+//go:build go1.26
+
+package server
+
+import (
+ "crypto/fips140"
+ "crypto/sha1"
+ "encoding/base64"
+)
+
+func wsAllowedFIPS() bool {
+ // As SHA-1 is only used for Sec-WebSocket-Key and Sec-WebSocket-Accept, we
+ // can continue to allow it in FIPS builds as long as they are built with
+ // Go 1.26 or later only.
+ return true
+}
+
+// Concatenate the key sent by the client with the GUID, then computes the SHA1 hash
+// and returns it as a based64 encoded string.
+func wsAcceptKey(key string) string {
+ var r []byte
+ fips140.WithoutEnforcement(func() {
+ h := sha1.New()
+ h.Write([]byte(key))
+ h.Write(wsGUID)
+ r = h.Sum(nil)
+ })
+ return base64.StdEncoding.EncodeToString(r)
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 18930bd8cd..52a2e40261 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -845,7 +845,7 @@ github.com/justinas/alice
# github.com/kevinburke/ssh_config v1.2.0
## explicit
github.com/kevinburke/ssh_config
-# github.com/klauspost/compress v1.18.5
+# github.com/klauspost/compress v1.18.6
## explicit; go 1.24
github.com/klauspost/compress
github.com/klauspost/compress/flate
@@ -1150,7 +1150,7 @@ github.com/munnerz/goautoneg
# github.com/nats-io/jwt/v2 v2.8.1
## explicit; go 1.25.0
github.com/nats-io/jwt/v2
-# github.com/nats-io/nats-server/v2 v2.14.0
+# github.com/nats-io/nats-server/v2 v2.14.1
## explicit; go 1.25.0
github.com/nats-io/nats-server/v2/conf
github.com/nats-io/nats-server/v2/internal/fastrand