diff --git a/src/go.mod b/src/go.mod index 075578189..48fedd742 100644 --- a/src/go.mod +++ b/src/go.mod @@ -8,14 +8,14 @@ require ( code.cloudfoundry.org/go-batching v0.0.0-20240911223457-d5519e55d3b9 code.cloudfoundry.org/go-diodes v0.0.0-20240911205836-e7f77fdf9650 code.cloudfoundry.org/go-envstruct v1.7.0 - code.cloudfoundry.org/go-metric-registry v0.0.0-20240911230103-8810864fa5d2 + code.cloudfoundry.org/go-metric-registry v0.0.0-20240924162441-23e55bf8b6be code.cloudfoundry.org/tlsconfig v0.5.0 github.com/cloudfoundry/dropsonde v1.1.0 github.com/cloudfoundry/sonde-go v0.0.0-20240807231527-361c7ad33dc7 github.com/onsi/gomega v1.34.2 github.com/prometheus/client_model v0.6.1 github.com/prometheus/common v0.59.1 - github.com/valyala/fasthttp v1.55.0 + github.com/valyala/fasthttp v1.56.0 golang.org/x/net v0.29.0 google.golang.org/grpc v1.67.0 gopkg.in/yaml.v2 v2.4.0 @@ -38,10 +38,10 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 // indirect + github.com/google/pprof v0.0.0-20240929191954-255acd752d31 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/compress v1.17.10 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.20.4 // indirect @@ -50,14 +50,14 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/square/certstrap v1.3.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - go.step.sm/crypto v0.52.0 // indirect + go.step.sm/crypto v0.53.0 // indirect golang.org/x/crypto v0.27.0 // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.25.0 // indirect golang.org/x/text v0.18.0 // indirect golang.org/x/tools v0.25.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240924160255-9d4c2d233b61 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/src/go.sum b/src/go.sum index bd64fa5ab..53f39af7e 100644 --- a/src/go.sum +++ b/src/go.sum @@ -7,8 +7,8 @@ code.cloudfoundry.org/go-envstruct v1.7.0 h1:6e90Z+lTPrI2jMJOoKErskL7a+Qm97GFiit code.cloudfoundry.org/go-envstruct v1.7.0/go.mod h1:xm6Eto/WB7Qq1iwEN29jUQXDeUCJ+nruwAnnrpY0s4E= code.cloudfoundry.org/go-loggregator/v9 v9.2.1 h1:S6Lgg5UJbhh2bt2TGQxs6R00CF8PrUA3GFPYDxy56Fk= code.cloudfoundry.org/go-loggregator/v9 v9.2.1/go.mod h1:FTFFruqGeOhVCDFvyLgl8EV8YW63NNwRzLhxJcporu8= -code.cloudfoundry.org/go-metric-registry v0.0.0-20240911230103-8810864fa5d2 h1:W2VwC5aKh8zHys47wGrULci0mCI/MeQ96qLCThuH9Ro= -code.cloudfoundry.org/go-metric-registry v0.0.0-20240911230103-8810864fa5d2/go.mod h1:dcrSGyc9oIIjD6dkcNBOmOcvxTLaXNmdpHhSi9MsXbo= +code.cloudfoundry.org/go-metric-registry v0.0.0-20240924162441-23e55bf8b6be h1:4X0t09FfNj9FkL57g4QGhPp6Al8FKJuQW+JqvI+Hznw= +code.cloudfoundry.org/go-metric-registry v0.0.0-20240924162441-23e55bf8b6be/go.mod h1:1HZDVeO8ym+CdBjc5hEa0+ybcSPd3Y+EnU9YLqFJZfw= code.cloudfoundry.org/tlsconfig v0.5.0 h1:wkdwdVPMPUowUyR4jzl7EttWD6j3Y8uQKcc2VxBh+Nk= code.cloudfoundry.org/tlsconfig v0.5.0/go.mod h1:hwoQOMSbbpu1doDFFDK8h5EpM/cWREyWMXEo0H1MFeA= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= @@ -71,8 +71,8 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 h1:c5FlPPgxOn7kJz3VoPLkQYQXGBS3EklQ4Zfi57uOuqQ= -github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20240929191954-255acd752d31 h1:LcRdQWywSgfi5jPsYZ1r2avbbs5IQ5wtyhMBCcokyo4= +github.com/google/pprof v0.0.0-20240929191954-255acd752d31/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= @@ -87,8 +87,8 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0= +github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -176,8 +176,8 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8= -github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM= +github.com/valyala/fasthttp v1.56.0 h1:bEZdJev/6LCBlpdORfrLu/WOZXXxvrUQSiyniuaoW8U= +github.com/valyala/fasthttp v1.56.0/go.mod h1:sReBt3XZVnudxuLOx4J/fMrJVorWRiWY2koQKgABiVI= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -185,8 +185,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= -go.step.sm/crypto v0.52.0 h1:3blUzFm0S4tPrijcvcP47tvd7VmEEGJnvzblE+sg5LI= -go.step.sm/crypto v0.52.0/go.mod h1:GcT4hMILsNiiN3dIXH/Df5fLVs/KIwGZva85faw7lYw= +go.step.sm/crypto v0.53.0 h1:+1as1ogzuCzx15/468M4mEC5juogI5a0Fzbsyh1CuYY= +go.step.sm/crypto v0.53.0/go.mod h1:AqLU78RqNUHepLzyOWZuNN/2++Lu7dZENdO9UzWOGSk= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= @@ -253,10 +253,10 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= -google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto/googleapis/api v0.0.0-20240924160255-9d4c2d233b61 h1:pAjq8XSSzXoP9ya73v/w+9QEAAJNluLrpmMq5qFJQNY= +google.golang.org/genproto/googleapis/api v0.0.0-20240924160255-9d4c2d233b61/go.mod h1:O6rP0uBq4k0mdi/b4ZEMAZjkhYWhS815kCvaMha4VN8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 h1:N9BgCIAUvn/M+p4NJccWPWb3BWh88+zyL0ll9HgbEeM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= diff --git a/src/vendor/github.com/klauspost/compress/README.md b/src/vendor/github.com/klauspost/compress/README.md index 05c7359e4..684a30853 100644 --- a/src/vendor/github.com/klauspost/compress/README.md +++ b/src/vendor/github.com/klauspost/compress/README.md @@ -16,6 +16,20 @@ This package provides various compression algorithms. # changelog +* 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 @@ -81,7 +95,7 @@ https://github.com/klauspost/compress/pull/919 https://github.com/klauspost/comp * 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: Suppport ResponseWriter Unwrap() in gzhttp handler by @jgimenez in https://github.com/klauspost/compress/pull/799 + * 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 @@ -136,7 +150,7 @@ https://github.com/klauspost/compress/pull/919 https://github.com/klauspost/comp * 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" compresssion https://github.com/klauspost/compress/pull/657 + * 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 @@ -339,7 +353,7 @@ While the release has been extensively tested, it is recommended to testing when * s2: Fix binaries. * Feb 25, 2021 (v1.11.8) - * s2: Fixed occational out-of-bounds write on amd64. Upgrade recommended. + * 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) @@ -518,7 +532,7 @@ While the release has been extensively tested, it is recommended to testing when * 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 progresssion in terms of compression. New default level is 5. +* 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. diff --git a/src/vendor/github.com/klauspost/compress/flate/deflate.go b/src/vendor/github.com/klauspost/compress/flate/deflate.go index 66d1657d2..af53fb860 100644 --- a/src/vendor/github.com/klauspost/compress/flate/deflate.go +++ b/src/vendor/github.com/klauspost/compress/flate/deflate.go @@ -861,7 +861,7 @@ func (d *compressor) reset(w io.Writer) { } switch d.compressionLevel.chain { case 0: - // level was NoCompression or ConstantCompresssion. + // level was NoCompression or ConstantCompression. d.windowEnd = 0 default: s := d.state diff --git a/src/vendor/github.com/klauspost/compress/flate/inflate.go b/src/vendor/github.com/klauspost/compress/flate/inflate.go index 2f410d64f..0d7b437f1 100644 --- a/src/vendor/github.com/klauspost/compress/flate/inflate.go +++ b/src/vendor/github.com/klauspost/compress/flate/inflate.go @@ -298,6 +298,14 @@ const ( huffmanGenericReader ) +// flushMode tells decompressor when to return data +type flushMode uint8 + +const ( + syncFlush flushMode = iota // return data after sync flush block + partialFlush // return data after each block +) + // Decompress state. type decompressor struct { // Input source. @@ -332,6 +340,8 @@ type decompressor struct { nb uint final bool + + flushMode flushMode } func (f *decompressor) nextBlock() { @@ -618,7 +628,10 @@ func (f *decompressor) dataBlock() { } if n == 0 { - f.toRead = f.dict.readFlush() + if f.flushMode == syncFlush { + f.toRead = f.dict.readFlush() + } + f.finishBlock() return } @@ -657,8 +670,12 @@ func (f *decompressor) finishBlock() { if f.dict.availRead() > 0 { f.toRead = f.dict.readFlush() } + f.err = io.EOF + } else if f.flushMode == partialFlush && f.dict.availRead() > 0 { + f.toRead = f.dict.readFlush() } + f.step = nextBlock } @@ -789,15 +806,25 @@ func (f *decompressor) Reset(r io.Reader, dict []byte) error { return nil } -// NewReader returns a new ReadCloser that can be used -// to read the uncompressed version of r. -// If r does not also implement io.ByteReader, -// the decompressor may read more data than necessary from r. -// It is the caller's responsibility to call Close on the ReadCloser -// when finished reading. -// -// The ReadCloser returned by NewReader also implements Resetter. -func NewReader(r io.Reader) io.ReadCloser { +type ReaderOpt func(*decompressor) + +// WithPartialBlock tells decompressor to return after each block, +// so it can read data written with partial flush +func WithPartialBlock() ReaderOpt { + return func(f *decompressor) { + f.flushMode = partialFlush + } +} + +// WithDict initializes the reader with a preset dictionary +func WithDict(dict []byte) ReaderOpt { + return func(f *decompressor) { + f.dict.init(maxMatchOffset, dict) + } +} + +// NewReaderOpts returns new reader with provided options +func NewReaderOpts(r io.Reader, opts ...ReaderOpt) io.ReadCloser { fixedHuffmanDecoderInit() var f decompressor @@ -806,9 +833,26 @@ func NewReader(r io.Reader) io.ReadCloser { f.codebits = new([numCodes]int) f.step = nextBlock f.dict.init(maxMatchOffset, nil) + + for _, opt := range opts { + opt(&f) + } + return &f } +// NewReader returns a new ReadCloser that can be used +// to read the uncompressed version of r. +// If r does not also implement io.ByteReader, +// the decompressor may read more data than necessary from r. +// It is the caller's responsibility to call Close on the ReadCloser +// when finished reading. +// +// The ReadCloser returned by NewReader also implements Resetter. +func NewReader(r io.Reader) io.ReadCloser { + return NewReaderOpts(r) +} + // NewReaderDict is like NewReader but initializes the reader // with a preset dictionary. The returned Reader behaves as if // the uncompressed data stream started with the given dictionary, @@ -817,13 +861,5 @@ func NewReader(r io.Reader) io.ReadCloser { // // The ReadCloser returned by NewReader also implements Resetter. func NewReaderDict(r io.Reader, dict []byte) io.ReadCloser { - fixedHuffmanDecoderInit() - - var f decompressor - f.r = makeReader(r) - f.bits = new([maxNumLit + maxNumDist]int) - f.codebits = new([numCodes]int) - f.step = nextBlock - f.dict.init(maxMatchOffset, dict) - return &f + return NewReaderOpts(r, WithDict(dict)) } diff --git a/src/vendor/github.com/klauspost/compress/fse/decompress.go b/src/vendor/github.com/klauspost/compress/fse/decompress.go index cc05d0f7e..0c7dd4ffe 100644 --- a/src/vendor/github.com/klauspost/compress/fse/decompress.go +++ b/src/vendor/github.com/klauspost/compress/fse/decompress.go @@ -15,7 +15,7 @@ const ( // It is possible, but by no way guaranteed that corrupt data will // return an error. // It is up to the caller to verify integrity of the returned data. -// Use a predefined Scrach to set maximum acceptable output size. +// Use a predefined Scratch to set maximum acceptable output size. func Decompress(b []byte, s *Scratch) ([]byte, error) { s, err := s.prepare(b) if err != nil { diff --git a/src/vendor/github.com/klauspost/compress/huff0/decompress.go b/src/vendor/github.com/klauspost/compress/huff0/decompress.go index 54bd08b25..0f56b02d7 100644 --- a/src/vendor/github.com/klauspost/compress/huff0/decompress.go +++ b/src/vendor/github.com/klauspost/compress/huff0/decompress.go @@ -1136,7 +1136,7 @@ func (s *Scratch) matches(ct cTable, w io.Writer) { errs++ } if errs > 0 { - fmt.Fprintf(w, "%d errros in base, stopping\n", errs) + fmt.Fprintf(w, "%d errors in base, stopping\n", errs) continue } // Ensure that all combinations are covered. @@ -1152,7 +1152,7 @@ func (s *Scratch) matches(ct cTable, w io.Writer) { errs++ } if errs > 20 { - fmt.Fprintf(w, "%d errros, stopping\n", errs) + fmt.Fprintf(w, "%d errors, stopping\n", errs) break } } diff --git a/src/vendor/github.com/klauspost/compress/zstd/blockdec.go b/src/vendor/github.com/klauspost/compress/zstd/blockdec.go index 03744fbc7..9c28840c3 100644 --- a/src/vendor/github.com/klauspost/compress/zstd/blockdec.go +++ b/src/vendor/github.com/klauspost/compress/zstd/blockdec.go @@ -598,7 +598,9 @@ func (b *blockDec) prepareSequences(in []byte, hist *history) (err error) { printf("RLE set to 0x%x, code: %v", symb, v) } case compModeFSE: - println("Reading table for", tableIndex(i)) + if debugDecoder { + println("Reading table for", tableIndex(i)) + } if seq.fse == nil || seq.fse.preDefined { seq.fse = fseDecoderPool.Get().(*fseDecoder) } diff --git a/src/vendor/github.com/klauspost/compress/zstd/enc_better.go b/src/vendor/github.com/klauspost/compress/zstd/enc_better.go index a4f5bf91f..84a79fde7 100644 --- a/src/vendor/github.com/klauspost/compress/zstd/enc_better.go +++ b/src/vendor/github.com/klauspost/compress/zstd/enc_better.go @@ -179,9 +179,9 @@ encodeLoop: if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) { // Consider history as well. var seq seq - lenght := 4 + e.matchlen(s+4+repOff, repIndex+4, src) + length := 4 + e.matchlen(s+4+repOff, repIndex+4, src) - seq.matchLen = uint32(lenght - zstdMinMatch) + seq.matchLen = uint32(length - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. @@ -210,12 +210,12 @@ encodeLoop: // Index match start+1 (long) -> s - 1 index0 := s + repOff - s += lenght + repOff + s += length + repOff nextEmit = s if s >= sLimit { if debugEncoder { - println("repeat ended", s, lenght) + println("repeat ended", s, length) } break encodeLoop @@ -241,9 +241,9 @@ encodeLoop: if false && repIndex >= 0 && load6432(src, repIndex) == load6432(src, s+repOff) { // Consider history as well. var seq seq - lenght := 8 + e.matchlen(s+8+repOff2, repIndex+8, src) + length := 8 + e.matchlen(s+8+repOff2, repIndex+8, src) - seq.matchLen = uint32(lenght - zstdMinMatch) + seq.matchLen = uint32(length - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. @@ -270,11 +270,11 @@ encodeLoop: } blk.sequences = append(blk.sequences, seq) - s += lenght + repOff2 + s += length + repOff2 nextEmit = s if s >= sLimit { if debugEncoder { - println("repeat ended", s, lenght) + println("repeat ended", s, length) } break encodeLoop @@ -708,9 +708,9 @@ encodeLoop: if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) { // Consider history as well. var seq seq - lenght := 4 + e.matchlen(s+4+repOff, repIndex+4, src) + length := 4 + e.matchlen(s+4+repOff, repIndex+4, src) - seq.matchLen = uint32(lenght - zstdMinMatch) + seq.matchLen = uint32(length - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. @@ -738,12 +738,12 @@ encodeLoop: blk.sequences = append(blk.sequences, seq) // Index match start+1 (long) -> s - 1 - s += lenght + repOff + s += length + repOff nextEmit = s if s >= sLimit { if debugEncoder { - println("repeat ended", s, lenght) + println("repeat ended", s, length) } break encodeLoop @@ -772,9 +772,9 @@ encodeLoop: if false && repIndex >= 0 && load6432(src, repIndex) == load6432(src, s+repOff) { // Consider history as well. var seq seq - lenght := 8 + e.matchlen(s+8+repOff2, repIndex+8, src) + length := 8 + e.matchlen(s+8+repOff2, repIndex+8, src) - seq.matchLen = uint32(lenght - zstdMinMatch) + seq.matchLen = uint32(length - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. @@ -801,11 +801,11 @@ encodeLoop: } blk.sequences = append(blk.sequences, seq) - s += lenght + repOff2 + s += length + repOff2 nextEmit = s if s >= sLimit { if debugEncoder { - println("repeat ended", s, lenght) + println("repeat ended", s, length) } break encodeLoop diff --git a/src/vendor/github.com/klauspost/compress/zstd/enc_dfast.go b/src/vendor/github.com/klauspost/compress/zstd/enc_dfast.go index a154c18f7..d36be7bd8 100644 --- a/src/vendor/github.com/klauspost/compress/zstd/enc_dfast.go +++ b/src/vendor/github.com/klauspost/compress/zstd/enc_dfast.go @@ -138,9 +138,9 @@ encodeLoop: if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) { // Consider history as well. var seq seq - lenght := 4 + e.matchlen(s+4+repOff, repIndex+4, src) + length := 4 + e.matchlen(s+4+repOff, repIndex+4, src) - seq.matchLen = uint32(lenght - zstdMinMatch) + seq.matchLen = uint32(length - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. @@ -166,11 +166,11 @@ encodeLoop: println("repeat sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) - s += lenght + repOff + s += length + repOff nextEmit = s if s >= sLimit { if debugEncoder { - println("repeat ended", s, lenght) + println("repeat ended", s, length) } break encodeLoop @@ -798,9 +798,9 @@ encodeLoop: if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) { // Consider history as well. var seq seq - lenght := 4 + e.matchlen(s+4+repOff, repIndex+4, src) + length := 4 + e.matchlen(s+4+repOff, repIndex+4, src) - seq.matchLen = uint32(lenght - zstdMinMatch) + seq.matchLen = uint32(length - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. @@ -826,11 +826,11 @@ encodeLoop: println("repeat sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) - s += lenght + repOff + s += length + repOff nextEmit = s if s >= sLimit { if debugEncoder { - println("repeat ended", s, lenght) + println("repeat ended", s, length) } break encodeLoop diff --git a/src/vendor/github.com/klauspost/compress/zstd/encoder.go b/src/vendor/github.com/klauspost/compress/zstd/encoder.go index 72af7ef0f..a79c4a527 100644 --- a/src/vendor/github.com/klauspost/compress/zstd/encoder.go +++ b/src/vendor/github.com/klauspost/compress/zstd/encoder.go @@ -202,7 +202,7 @@ func (e *Encoder) nextBlock(final bool) error { return nil } if final && len(s.filling) > 0 { - s.current = e.EncodeAll(s.filling, s.current[:0]) + s.current = e.encodeAll(s.encoder, s.filling, s.current[:0]) var n2 int n2, s.err = s.w.Write(s.current) if s.err != nil { @@ -469,6 +469,15 @@ func (e *Encoder) Close() error { // Data compressed with EncodeAll can be decoded with the Decoder, // using either a stream or DecodeAll. func (e *Encoder) EncodeAll(src, dst []byte) []byte { + e.init.Do(e.initialize) + enc := <-e.encoders + defer func() { + e.encoders <- enc + }() + return e.encodeAll(enc, src, dst) +} + +func (e *Encoder) encodeAll(enc encoder, src, dst []byte) []byte { if len(src) == 0 { if e.o.fullZero { // Add frame header. @@ -491,13 +500,7 @@ func (e *Encoder) EncodeAll(src, dst []byte) []byte { } return dst } - e.init.Do(e.initialize) - enc := <-e.encoders - defer func() { - // Release encoder reference to last block. - // If a non-single block is needed the encoder will reset again. - e.encoders <- enc - }() + // Use single segments when above minimum window and below window size. single := len(src) <= e.o.windowSize && len(src) > MinWindowSize if e.o.single != nil { diff --git a/src/vendor/github.com/klauspost/compress/zstd/framedec.go b/src/vendor/github.com/klauspost/compress/zstd/framedec.go index 53e160f7e..e47af66e7 100644 --- a/src/vendor/github.com/klauspost/compress/zstd/framedec.go +++ b/src/vendor/github.com/klauspost/compress/zstd/framedec.go @@ -146,7 +146,9 @@ func (d *frameDec) reset(br byteBuffer) error { } return err } - printf("raw: %x, mantissa: %d, exponent: %d\n", wd, wd&7, wd>>3) + if debugDecoder { + printf("raw: %x, mantissa: %d, exponent: %d\n", wd, wd&7, wd>>3) + } windowLog := 10 + (wd >> 3) windowBase := uint64(1) << windowLog windowAdd := (windowBase / 8) * uint64(wd&0x7) diff --git a/src/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go b/src/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go index 8adabd828..c59f17e07 100644 --- a/src/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go +++ b/src/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go @@ -146,7 +146,7 @@ func (s *sequenceDecs) decodeSyncSimple(hist []byte) (bool, error) { return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) default: - return true, fmt.Errorf("sequenceDecs_decode returned erronous code %d", errCode) + return true, fmt.Errorf("sequenceDecs_decode returned erroneous code %d", errCode) } s.seqSize += ctx.litRemain @@ -292,7 +292,7 @@ func (s *sequenceDecs) decode(seqs []seqVals) error { return io.ErrUnexpectedEOF } - return fmt.Errorf("sequenceDecs_decode_amd64 returned erronous code %d", errCode) + return fmt.Errorf("sequenceDecs_decode_amd64 returned erroneous code %d", errCode) } if ctx.litRemain < 0 { diff --git a/src/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s b/src/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s index 5b06174b8..f5591fa1e 100644 --- a/src/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s +++ b/src/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s @@ -1814,7 +1814,7 @@ TEXT ·sequenceDecs_decodeSync_amd64(SB), $64-32 MOVQ 40(SP), AX ADDQ AX, 48(SP) - // Calculate poiter to s.out[cap(s.out)] (a past-end pointer) + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) ADDQ R10, 32(SP) // outBase += outPosition @@ -2376,7 +2376,7 @@ TEXT ·sequenceDecs_decodeSync_bmi2(SB), $64-32 MOVQ 40(SP), CX ADDQ CX, 48(SP) - // Calculate poiter to s.out[cap(s.out)] (a past-end pointer) + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) ADDQ R9, 32(SP) // outBase += outPosition @@ -2896,7 +2896,7 @@ TEXT ·sequenceDecs_decodeSync_safe_amd64(SB), $64-32 MOVQ 40(SP), AX ADDQ AX, 48(SP) - // Calculate poiter to s.out[cap(s.out)] (a past-end pointer) + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) ADDQ R10, 32(SP) // outBase += outPosition @@ -3560,7 +3560,7 @@ TEXT ·sequenceDecs_decodeSync_safe_bmi2(SB), $64-32 MOVQ 40(SP), CX ADDQ CX, 48(SP) - // Calculate poiter to s.out[cap(s.out)] (a past-end pointer) + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) ADDQ R9, 32(SP) // outBase += outPosition diff --git a/src/vendor/github.com/valyala/fasthttp/README.md b/src/vendor/github.com/valyala/fasthttp/README.md index cbdb9de23..f43ea6dc6 100644 --- a/src/vendor/github.com/valyala/fasthttp/README.md +++ b/src/vendor/github.com/valyala/fasthttp/README.md @@ -600,7 +600,7 @@ This is an **unsafe** way, the result string and `[]byte` buffer share the same * *Which GO versions are supported by fasthttp?* - Go 1.18.x. Older versions won't be supported. + Go 1.21.x and newer. Older versions might work, but won't officially be supported. * *Please provide real benchmark data and server information* diff --git a/src/vendor/github.com/valyala/fasthttp/brotli.go b/src/vendor/github.com/valyala/fasthttp/brotli.go index 30b7d66d4..1e8e0f08a 100644 --- a/src/vendor/github.com/valyala/fasthttp/brotli.go +++ b/src/vendor/github.com/valyala/fasthttp/brotli.go @@ -97,7 +97,7 @@ var ( // - CompressBrotliBestCompression // - CompressBrotliDefaultCompression func AppendBrotliBytesLevel(dst, src []byte, level int) []byte { - w := &byteSliceWriter{dst} + w := &byteSliceWriter{b: dst} WriteBrotliLevel(w, src, level) //nolint:errcheck return w.b } @@ -167,7 +167,7 @@ func AppendBrotliBytes(dst, src []byte) []byte { // WriteUnbrotli writes unbrotlied p to w and returns the number of uncompressed // bytes written to w. func WriteUnbrotli(w io.Writer, p []byte) (int, error) { - r := &byteSliceReader{p} + r := &byteSliceReader{b: p} zr, err := acquireBrotliReader(r) if err != nil { return 0, err @@ -183,7 +183,7 @@ func WriteUnbrotli(w io.Writer, p []byte) (int, error) { // AppendUnbrotliBytes appends unbrotlied src to dst and returns the resulting dst. func AppendUnbrotliBytes(dst, src []byte) ([]byte, error) { - w := &byteSliceWriter{dst} + w := &byteSliceWriter{b: dst} _, err := WriteUnbrotli(w, src) return w.b, err } diff --git a/src/vendor/github.com/valyala/fasthttp/bytesconv.go b/src/vendor/github.com/valyala/fasthttp/bytesconv.go index dddf24fdf..053da6a70 100644 --- a/src/vendor/github.com/valyala/fasthttp/bytesconv.go +++ b/src/vendor/github.com/valyala/fasthttp/bytesconv.go @@ -8,8 +8,8 @@ import ( "errors" "fmt" "io" - "math" "net" + "strconv" "sync" "time" ) @@ -127,21 +127,7 @@ func AppendUint(dst []byte, n int) []byte { panic("BUG: int must be positive") } - var b [20]byte - buf := b[:] - i := len(buf) - var q int - for n >= 10 { - i-- - q = n / 10 - buf[i] = '0' + byte(n-q*10) - n = q - } - i-- - buf[i] = '0' + byte(n) - - dst = append(dst, buf[i:]...) - return dst + return strconv.AppendUint(dst, uint64(n), 10) } // ParseUint parses uint from buf. @@ -185,61 +171,19 @@ func parseUintBuf(b []byte) (int, int, error) { return v, n, nil } -var ( - errEmptyFloat = errors.New("empty float number") - errDuplicateFloatPoint = errors.New("duplicate point found in float number") - errUnexpectedFloatEnd = errors.New("unexpected end of float number") - errInvalidFloatExponent = errors.New("invalid float number exponent") - errUnexpectedFloatChar = errors.New("unexpected char found in float number") -) - // ParseUfloat parses unsigned float from buf. func ParseUfloat(buf []byte) (float64, error) { - if len(buf) == 0 { - return -1, errEmptyFloat + // The implementation of parsing a float string is not easy. + // We believe that the conservative approach is to call strconv.ParseFloat. + // https://github.com/valyala/fasthttp/pull/1865 + res, err := strconv.ParseFloat(b2s(buf), 64) + if res < 0 { + return -1, errors.New("negative input is invalid") } - b := buf - var v uint64 - offset := 1.0 - var pointFound bool - for i, c := range b { - if c < '0' || c > '9' { - if c == '.' { - if pointFound { - return -1, errDuplicateFloatPoint - } - pointFound = true - continue - } - if c == 'e' || c == 'E' { - if i+1 >= len(b) { - return -1, errUnexpectedFloatEnd - } - b = b[i+1:] - minus := -1 - switch b[0] { - case '+': - b = b[1:] - minus = 1 - case '-': - b = b[1:] - default: - minus = 1 - } - vv, err := ParseUint(b) - if err != nil { - return -1, errInvalidFloatExponent - } - return float64(v) * offset * math.Pow10(minus*vv), nil - } - return -1, errUnexpectedFloatChar - } - v = 10*v + uint64(c-'0') - if pointFound { - offset /= 10 - } + if err != nil { + return -1, err } - return float64(v) * offset, nil + return res, err } var ( diff --git a/src/vendor/github.com/valyala/fasthttp/bytesconv_table.go b/src/vendor/github.com/valyala/fasthttp/bytesconv_table.go index 5b230f1a5..591470fc5 100644 --- a/src/vendor/github.com/valyala/fasthttp/bytesconv_table.go +++ b/src/vendor/github.com/valyala/fasthttp/bytesconv_table.go @@ -9,3 +9,5 @@ const toUpperTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11 const quotedArgShouldEscapeTable = "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" const quotedPathShouldEscapeTable = "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x00\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" const validHeaderFieldByteTable = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x01\x01\x01\x01\x00\x00\x01\x01\x00\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x00\x01\x00" +const validHeaderValueByteTable = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" +const validMethodValueByteTable = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x01\x01\x01\x01\x00\x00\x01\x01\x00\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" diff --git a/src/vendor/github.com/valyala/fasthttp/client.go b/src/vendor/github.com/valyala/fasthttp/client.go index 5cae78dd7..84a7f93ed 100644 --- a/src/vendor/github.com/valyala/fasthttp/client.go +++ b/src/vendor/github.com/valyala/fasthttp/client.go @@ -175,14 +175,8 @@ var defaultClient Client type Client struct { noCopy noCopy - // Client name. Used in User-Agent request header. - // - // Default client name is used if not set. - Name string - - // NoDefaultUserAgentHeader when set to true, causes the default - // User-Agent header to be excluded from the Request. - NoDefaultUserAgentHeader bool + readerPool sync.Pool + writerPool sync.Pool // Callback for establishing new connections to hosts. // @@ -197,20 +191,36 @@ type Client struct { // If not set, DialTimeout is used. Dial DialFunc - // Attempt to connect to both ipv4 and ipv6 addresses if set to true. - // - // This option is used only if default TCP dialer is used, - // i.e. if Dial is blank. - // - // By default client connects only to ipv4 addresses, - // since unfortunately ipv6 remains broken in many networks worldwide :) - DialDualStack bool - // TLS config for https connections. // // Default TLS config is used if not set. TLSConfig *tls.Config + // RetryIf controls whether a retry should be attempted after an error. + // + // By default will use isIdempotent function. + // + // Deprecated: Use RetryIfErr instead. + // This field is only effective when the `RetryIfErr` field is not set. + RetryIf RetryIfFunc + + // When the client encounters an error during a request, the behavior—whether to retry + // and whether to reset the request timeout—should be determined + // based on the return value of this field. + // This field is only effective within the range of MaxIdemponentCallAttempts. + RetryIfErr RetryIfErrFunc + + // ConfigureClient configures the fasthttp.HostClient. + ConfigureClient func(hc *HostClient) error + + m map[string]*HostClient + ms map[string]*HostClient + + // Client name. Used in User-Agent request header. + // + // Default client name is used if not set. + Name string + // Maximum number of connections per each host which may be established. // // DefaultMaxConnsPerHost is used if not set. @@ -261,6 +271,30 @@ type Client struct { // By default response body size is unlimited. MaxResponseBodySize int + // Maximum duration for waiting for a free connection. + // + // By default will not waiting, return ErrNoFreeConns immediately. + MaxConnWaitTimeout time.Duration + + // Connection pool strategy. Can be either LIFO or FIFO (default). + ConnPoolStrategy ConnPoolStrategyType + + mLock sync.RWMutex + mOnce sync.Once + + // NoDefaultUserAgentHeader when set to true, causes the default + // User-Agent header to be excluded from the Request. + NoDefaultUserAgentHeader bool + + // Attempt to connect to both ipv4 and ipv6 addresses if set to true. + // + // This option is used only if default TCP dialer is used, + // i.e. if Dial is blank. + // + // By default client connects only to ipv4 addresses, + // since unfortunately ipv6 remains broken in many networks worldwide :) + DialDualStack bool + // Header names are passed as-is without normalization // if this option is set. // @@ -288,31 +322,8 @@ type Client struct { // extra slashes are removed, special characters are encoded. DisablePathNormalizing bool - // Maximum duration for waiting for a free connection. - // - // By default will not waiting, return ErrNoFreeConns immediately. - MaxConnWaitTimeout time.Duration - - // RetryIf controls whether a retry should be attempted after an error. - // - // By default will use isIdempotent function. - RetryIf RetryIfFunc - - // Connection pool strategy. Can be either LIFO or FIFO (default). - ConnPoolStrategy ConnPoolStrategyType - // StreamResponseBody enables response body streaming. StreamResponseBody bool - - // ConfigureClient configures the fasthttp.HostClient. - ConfigureClient func(hc *HostClient) error - - mLock sync.RWMutex - mOnce sync.Once - m map[string]*HostClient - ms map[string]*HostClient - readerPool sync.Pool - writerPool sync.Pool } // Get returns the status code and body of url. @@ -535,6 +546,7 @@ func (c *Client) Do(req *Request, resp *Response) error { DisablePathNormalizing: c.DisablePathNormalizing, MaxConnWaitTimeout: c.MaxConnWaitTimeout, RetryIf: c.RetryIf, + RetryIfErr: c.RetryIfErr, ConnPoolStrategy: c.ConnPoolStrategy, StreamResponseBody: c.StreamResponseBody, clientReaderPool: &c.readerPool, @@ -595,7 +607,6 @@ func (c *Client) mCleaner(m map[string]*HostClient) { c.mLock.Lock() for k, v := range m { v.connsLock.Lock() - /* #nosec G601 */ if v.connsCount == 0 && atomic.LoadInt32(&v.pendingClientRequests) == 0 { delete(m, k) } @@ -653,11 +664,28 @@ type DialFunc func(addr string) (net.Conn, error) // - foobar.com:8080 type DialFuncWithTimeout func(addr string, timeout time.Duration) (net.Conn, error) -// RetryIfFunc signature of retry if function. -// +// RetryIfFunc defines the signature of the retry if function. // Request argument passed to RetryIfFunc, if there are any request errors. type RetryIfFunc func(request *Request) bool +// RetryIfErrFunc defines an interface used for implementing the following functionality: +// When the client encounters an error during a request, the behavior—whether to retry +// and whether to reset the request timeout—should be determined +// based on the return value of this interface. +// +// attempt indicates which attempt the current retry is due to a failure of. +// The first request counts as the first attempt. +// +// err represents the error encountered while attempting the `attempts`-th request. +// +// resetTimeout indicates whether to reuse the `Request`'s timeout as the timeout interval, +// rather than using the timeout after subtracting the time spent on previous failed requests. +// This return value is meaningful only when you use `Request.SetTimeout`, `DoTimeout`, or `DoDeadline`. +// +// retry indicates whether to retry the current request. If it is false, +// the request function will immediately return with the `err`. +type RetryIfErrFunc func(request *Request, attempts int, err error) (resetTimeout bool, retry bool) + // RoundTripper wraps every request/response. type RoundTripper interface { RoundTrip(hc *HostClient, req *Request, resp *Response) (retry bool, err error) @@ -684,23 +712,11 @@ const ( type HostClient struct { noCopy noCopy - // Comma-separated list of upstream HTTP server host addresses, - // which are passed to Dial or DialTimeout in a round-robin manner. - // - // Each address may contain port if default dialer is used. - // For example, - // - // - foobar.com:80 - // - foobar.com:443 - // - foobar.com:8080 - Addr string - - // Client name. Used in User-Agent request header. - Name string + readerPool sync.Pool + writerPool sync.Pool - // NoDefaultUserAgentHeader when set to true, causes the default - // User-Agent header to be excluded from the Request. - NoDefaultUserAgentHeader bool + // Transport defines a transport-like mechanism that wraps every request/response. + Transport RoundTripper // Callback for establishing new connections to hosts. // @@ -715,21 +731,45 @@ type HostClient struct { // If not set, DialTimeout is used. Dial DialFunc - // Attempt to connect to both ipv4 and ipv6 host addresses - // if set to true. + // Optional TLS config. + TLSConfig *tls.Config + + // RetryIf controls whether a retry should be attempted after an error. + // By default, it uses the isIdempotent function. // - // This option is used only if default TCP dialer is used, - // i.e. if Dial and DialTimeout are blank. + // Deprecated: Use RetryIfErr instead. + // This field is only effective when the `RetryIfErr` field is not set. + RetryIf RetryIfFunc + + // When the client encounters an error during a request, the behavior—whether to retry + // and whether to reset the request timeout—should be determined + // based on the return value of this field. + // This field is only effective within the range of MaxIdemponentCallAttempts. + RetryIfErr RetryIfErrFunc + + connsWait *wantConnQueue + + tlsConfigMap map[string]*tls.Config + + clientReaderPool *sync.Pool + clientWriterPool *sync.Pool + + // Comma-separated list of upstream HTTP server host addresses, + // which are passed to Dial or DialTimeout in a round-robin manner. // - // By default client connects only to ipv4 addresses, - // since unfortunately ipv6 remains broken in many networks worldwide :) - DialDualStack bool + // Each address may contain port if default dialer is used. + // For example, + // + // - foobar.com:80 + // - foobar.com:443 + // - foobar.com:8080 + Addr string - // Whether to use TLS (aka SSL or HTTPS) for host connections. - IsTLS bool + // Client name. Used in User-Agent request header. + Name string - // Optional TLS config. - TLSConfig *tls.Config + conns []*clientConn + addrs []string // Maximum number of connections which may be established to all hosts // listed in Addr. @@ -753,7 +793,10 @@ type HostClient struct { // Maximum number of attempts for idempotent calls. // - // DefaultMaxIdemponentCallAttempts is used if not set. + // A value of 0 or a negative value represents using DefaultMaxIdemponentCallAttempts. + // For example, a value of 1 means the request will be executed only once, + // while 2 means the request will be executed at most twice. + // The RetryIfErr and RetryIf fields can invalidate remaining attempts. MaxIdemponentCallAttempts int // Per-connection buffer size for responses' reading. @@ -785,6 +828,47 @@ type HostClient struct { // By default response body size is unlimited. MaxResponseBodySize int + // Maximum duration for waiting for a free connection. + // + // By default will not waiting, return ErrNoFreeConns immediately + MaxConnWaitTimeout time.Duration + + // Connection pool strategy. Can be either LIFO or FIFO (default). + ConnPoolStrategy ConnPoolStrategyType + + connsCount int + + connsLock sync.Mutex + + addrsLock sync.Mutex + tlsConfigMapLock sync.Mutex + + addrIdx uint32 + lastUseTime uint32 + + pendingRequests int32 + + // pendingClientRequests counts the number of requests that a Client is currently running using this HostClient. + // It will be incremented earlier than pendingRequests and will be used by Client to see if the HostClient is still in use. + pendingClientRequests int32 + + // NoDefaultUserAgentHeader when set to true, causes the default + // User-Agent header to be excluded from the Request. + NoDefaultUserAgentHeader bool + + // Attempt to connect to both ipv4 and ipv6 host addresses + // if set to true. + // + // This option is used only if default TCP dialer is used, + // i.e. if Dial and DialTimeout are blank. + // + // By default client connects only to ipv4 addresses, + // since unfortunately ipv6 remains broken in many networks worldwide :) + DialDualStack bool + + // Whether to use TLS (aka SSL or HTTPS) for host connections. + IsTLS bool + // Header names are passed as-is without normalization // if this option is set. // @@ -820,51 +904,9 @@ type HostClient struct { // Client logs full errors by default. SecureErrorLogMessage bool - // Maximum duration for waiting for a free connection. - // - // By default will not waiting, return ErrNoFreeConns immediately - MaxConnWaitTimeout time.Duration - - // RetryIf controls whether a retry should be attempted after an error. - // - // By default will use isIdempotent function - RetryIf RetryIfFunc - - // Transport defines a transport-like mechanism that wraps every request/response. - Transport RoundTripper - - // Connection pool strategy. Can be either LIFO or FIFO (default). - ConnPoolStrategy ConnPoolStrategyType - // StreamResponseBody enables response body streaming. StreamResponseBody bool - lastUseTime uint32 - - connsLock sync.Mutex - connsCount int - conns []*clientConn - connsWait *wantConnQueue - - addrsLock sync.Mutex - addrs []string - addrIdx uint32 - - tlsConfigMap map[string]*tls.Config - tlsConfigMapLock sync.Mutex - - readerPool sync.Pool - writerPool sync.Pool - - clientReaderPool *sync.Pool - clientWriterPool *sync.Pool - - pendingRequests int32 - - // pendingClientRequests counts the number of requests that a Client is currently running using this HostClient. - // It will be incremented earlier than pendingRequests and will be used by Client to see if the HostClient is still in use. - pendingClientRequests int32 - connsCleanerRun bool } @@ -950,9 +992,9 @@ func clientGetURLTimeout(dst []byte, url string, timeout time.Duration, c client } type clientURLResponse struct { - statusCode int - body []byte err error + body []byte + statusCode int } func clientGetURLDeadline(dst []byte, url string, deadline time.Time, c clientDoer) (statusCode int, body []byte, err error) { @@ -1271,16 +1313,15 @@ func (c *HostClient) DoRedirects(req *Request, resp *Response, maxRedirectsCount // It is recommended obtaining req and resp via AcquireRequest // and AcquireResponse in performance-critical code. func (c *HostClient) Do(req *Request, resp *Response) error { - var err error - var retry bool + var ( + err error + retry bool + resetTimeout bool + ) maxAttempts := c.MaxIdemponentCallAttempts if maxAttempts <= 0 { maxAttempts = DefaultMaxIdemponentCallAttempts } - isRequestRetryable := isIdempotent - if c.RetryIf != nil { - isRequestRetryable = c.RetryIf - } attempts := 0 hasBodyStream := req.IsBodyStream() @@ -1292,6 +1333,10 @@ func (c *HostClient) Do(req *Request, resp *Response) error { if timeout > 0 { deadline = time.Now().Add(timeout) } + retryFunc := c.RetryIf + if retryFunc == nil { + retryFunc = isIdempotent + } atomic.AddInt32(&c.pendingRequests, 1) for { @@ -1313,22 +1358,23 @@ func (c *HostClient) Do(req *Request, resp *Response) error { if hasBodyStream { break } - if !isRequestRetryable(req) { - // Retry non-idempotent requests if the server closes - // the connection before sending the response. - // - // This case is possible if the server closes the idle - // keep-alive connection on timeout. - // - // Apache and nginx usually do this. - if err != io.EOF { - break - } - } + // Path prioritization based on ease of computation attempts++ + if attempts >= maxAttempts { break } + if c.RetryIfErr != nil { + resetTimeout, retry = c.RetryIfErr(req, attempts, err) + } else { + retry = retryFunc(req) + } + if !retry { + break + } + if timeout > 0 && resetTimeout { + deadline = time.Now().Add(timeout) + } } atomic.AddInt32(&c.pendingRequests, -1) @@ -1360,9 +1406,7 @@ func (c *HostClient) do(req *Request, resp *Response) (bool, error) { defer ReleaseResponse(resp) } - ok, err := c.doNonNilReqResp(req, resp) - - return ok, err + return c.doNonNilReqResp(req, resp) } func (c *HostClient) doNonNilReqResp(req *Request, resp *Response) (bool, error) { @@ -1385,7 +1429,7 @@ func (c *HostClient) doNonNilReqResp(req *Request, resp *Response) (bool, error) return false, ErrHostClientRedirectToDifferentScheme } - atomic.StoreUint32(&c.lastUseTime, uint32(time.Now().Unix()-startTimeUnix)) + atomic.StoreUint32(&c.lastUseTime, uint32(time.Now().Unix()-startTimeUnix)) // #nosec G115 // Free up resources occupied by response before sending the request, // so the GC may reclaim these resources (e.g. response body). @@ -1547,6 +1591,7 @@ func (c *HostClient) acquireConn(reqTimeout time.Duration, connectionClose bool) case <-w.ready: return w.conn, w.err case <-tc.C: + c.connsWait.failedWaiters.Add(1) if timeoutOverridden { return nil, ErrTimeout } @@ -1692,6 +1737,7 @@ func (c *HostClient) decConnsCount() { dialed = true break } + c.connsWait.failedWaiters.Add(-1) } } if !dialed { @@ -1744,8 +1790,19 @@ func (c *HostClient) releaseConn(cc *clientConn) { w := q.popFront() if w.waiting() { delivered = w.tryDeliver(cc, nil) - break + // This is the last resort to hand over conCount sema. + // We must ensure that there are no valid waiters in connsWait + // when we exit this loop. + // + // We did not apply the same looping pattern in the decConnsCount + // method because it needs to create a new time-spent connection, + // and the decConnsCount call chain will inevitably reach this point. + // When MaxConnWaitTimeout>0. + if delivered { + break + } } + c.connsWait.failedWaiters.Add(-1) } } if !delivered { @@ -1859,7 +1916,7 @@ func (c *HostClient) nextAddr() string { } addr := c.addrs[0] if len(c.addrs) > 1 { - addr = c.addrs[c.addrIdx%uint32(len(c.addrs))] + addr = c.addrs[c.addrIdx%uint32(len(c.addrs))] // #nosec G115 c.addrIdx++ } c.addrsLock.Unlock() @@ -2033,10 +2090,10 @@ func AddMissingPort(addr string, isTLS bool) string { // // Inspired by net/http/transport.go. type wantConn struct { + err error ready chan struct{} - mu sync.Mutex // protects conn, err, close(ready) conn *clientConn - err error + mu sync.Mutex // protects conn, err, close(ready) } // waiting reports whether w is still waiting for an answer (connection or error). @@ -2099,13 +2156,19 @@ type wantConnQueue struct { // in Okasaki's purely functional queue but without the // overhead of reversing the list when swapping stages. head []*wantConn - headPos int tail []*wantConn + headPos int + // failedWaiters is the number of waiters in the head or tail queue, + // but is invalid. + // These state waiters cannot truly be considered as waiters; the current + // implementation does not immediately remove them when they become + // invalid but instead only marks them. + failedWaiters atomic.Int64 } // len returns the number of items in the queue. func (q *wantConnQueue) len() int { - return len(q.head) - q.headPos + len(q.tail) + return len(q.head) - q.headPos + len(q.tail) - int(q.failedWaiters.Load()) } // pushBack adds w to the back of the queue. @@ -2149,6 +2212,7 @@ func (q *wantConnQueue) clearFront() (cleaned bool) { return cleaned } q.popFront() + q.failedWaiters.Add(-1) cleaned = true } } @@ -2168,15 +2232,26 @@ func (q *wantConnQueue) clearFront() (cleaned bool) { type PipelineClient struct { noCopy noCopy + // Logger for logging client errors. + // + // By default standard logger from log package is used. + Logger Logger + + // Callback for connection establishing to the host. + // + // Default Dial is used if not set. + Dial DialFunc + + // Optional TLS config. + TLSConfig *tls.Config + // Address of the host to connect to. Addr string // PipelineClient name. Used in User-Agent request header. Name string - // NoDefaultUserAgentHeader when set to true, causes the default - // User-Agent header to be excluded from the Request. - NoDefaultUserAgentHeader bool + connClients []*pipelineConnClient // The maximum number of concurrent connections to the Addr. // @@ -2195,10 +2270,38 @@ type PipelineClient struct { // By default requests are sent immediately to the server. MaxBatchDelay time.Duration - // Callback for connection establishing to the host. + // Idle connection to the host is closed after this duration. // - // Default Dial is used if not set. - Dial DialFunc + // By default idle connection is closed after + // DefaultMaxIdleConnDuration. + MaxIdleConnDuration time.Duration + + // Buffer size for responses' reading. + // This also limits the maximum header size. + // + // Default buffer size is used if 0. + ReadBufferSize int + + // Buffer size for requests' writing. + // + // Default buffer size is used if 0. + WriteBufferSize int + + // Maximum duration for full response reading (including body). + // + // By default response read timeout is unlimited. + ReadTimeout time.Duration + + // Maximum duration for full request writing (including body). + // + // By default request write timeout is unlimited. + WriteTimeout time.Duration + + connClientsLock sync.Mutex + + // NoDefaultUserAgentHeader when set to true, causes the default + // User-Agent header to be excluded from the Request. + NoDefaultUserAgentHeader bool // Attempt to connect to both ipv4 and ipv6 host addresses // if set to true. @@ -2239,86 +2342,51 @@ type PipelineClient struct { // Whether to use TLS (aka SSL or HTTPS) for host connections. IsTLS bool +} - // Optional TLS config. - TLSConfig *tls.Config - - // Idle connection to the host is closed after this duration. - // - // By default idle connection is closed after - // DefaultMaxIdleConnDuration. - MaxIdleConnDuration time.Duration - - // Buffer size for responses' reading. - // This also limits the maximum header size. - // - // Default buffer size is used if 0. - ReadBufferSize int +type pipelineConnClient struct { + noCopy noCopy - // Buffer size for requests' writing. - // - // Default buffer size is used if 0. - WriteBufferSize int + workPool sync.Pool - // Maximum duration for full response reading (including body). - // - // By default response read timeout is unlimited. - ReadTimeout time.Duration + Logger Logger - // Maximum duration for full request writing (including body). - // - // By default request write timeout is unlimited. - WriteTimeout time.Duration + Dial DialFunc + TLSConfig *tls.Config + chW chan *pipelineWork + chR chan *pipelineWork - // Logger for logging client errors. - // - // By default standard logger from log package is used. - Logger Logger + tlsConfig *tls.Config - connClients []*pipelineConnClient - connClientsLock sync.Mutex -} + Addr string + Name string + MaxPendingRequests int + MaxBatchDelay time.Duration + MaxIdleConnDuration time.Duration + ReadBufferSize int + WriteBufferSize int + ReadTimeout time.Duration + WriteTimeout time.Duration -type pipelineConnClient struct { - noCopy noCopy + chLock sync.Mutex - Addr string - Name string + tlsConfigLock sync.Mutex NoDefaultUserAgentHeader bool - MaxPendingRequests int - MaxBatchDelay time.Duration - Dial DialFunc DialDualStack bool DisableHeaderNamesNormalizing bool DisablePathNormalizing bool IsTLS bool - TLSConfig *tls.Config - MaxIdleConnDuration time.Duration - ReadBufferSize int - WriteBufferSize int - ReadTimeout time.Duration - WriteTimeout time.Duration - Logger Logger - - workPool sync.Pool - - chLock sync.Mutex - chW chan *pipelineWork - chR chan *pipelineWork - - tlsConfigLock sync.Mutex - tlsConfig *tls.Config } type pipelineWork struct { - reqCopy Request respCopy Response + deadline time.Time + err error req *Request resp *Response t *time.Timer - deadline time.Time - err error done chan struct{} + reqCopy Request } // DoTimeout performs the given request and waits for response during @@ -2941,8 +3009,7 @@ func (t *transport) RoundTrip(hc *HostClient, req *Request, resp *Response) (ret err = ErrTimeout } - isConnRST := isConnectionReset(err) - if err != nil && !isConnRST { + if err != nil { hc.closeConn(cc) return true, err } @@ -2977,7 +3044,7 @@ func (t *transport) RoundTrip(hc *HostClient, req *Request, resp *Response) (ret return needRetry, err } - closeConn := resetConnection || req.ConnectionClose() || resp.ConnectionClose() || isConnRST + closeConn := resetConnection || req.ConnectionClose() || resp.ConnectionClose() if customStreamBody && resp.bodyStream != nil { rbs := resp.bodyStream resp.bodyStream = newCloseReaderWithError(rbs, func(wErr error) error { diff --git a/src/vendor/github.com/valyala/fasthttp/compress.go b/src/vendor/github.com/valyala/fasthttp/compress.go index 895dc51c3..19bb4352c 100644 --- a/src/vendor/github.com/valyala/fasthttp/compress.go +++ b/src/vendor/github.com/valyala/fasthttp/compress.go @@ -141,7 +141,7 @@ var ( // - CompressDefaultCompression // - CompressHuffmanOnly func AppendGzipBytesLevel(dst, src []byte, level int) []byte { - w := &byteSliceWriter{dst} + w := &byteSliceWriter{b: dst} WriteGzipLevel(w, src, level) //nolint:errcheck return w.b } @@ -212,7 +212,7 @@ func AppendGzipBytes(dst, src []byte) []byte { // WriteGunzip writes ungzipped p to w and returns the number of uncompressed // bytes written to w. func WriteGunzip(w io.Writer, p []byte) (int, error) { - r := &byteSliceReader{p} + r := &byteSliceReader{b: p} zr, err := acquireGzipReader(r) if err != nil { return 0, err @@ -228,7 +228,7 @@ func WriteGunzip(w io.Writer, p []byte) (int, error) { // AppendGunzipBytes appends gunzipped src to dst and returns the resulting dst. func AppendGunzipBytes(dst, src []byte) ([]byte, error) { - w := &byteSliceWriter{dst} + w := &byteSliceWriter{b: dst} _, err := WriteGunzip(w, src) return w.b, err } @@ -244,7 +244,7 @@ func AppendGunzipBytes(dst, src []byte) ([]byte, error) { // - CompressDefaultCompression // - CompressHuffmanOnly func AppendDeflateBytesLevel(dst, src []byte, level int) []byte { - w := &byteSliceWriter{dst} + w := &byteSliceWriter{b: dst} WriteDeflateLevel(w, src, level) //nolint:errcheck return w.b } @@ -321,7 +321,7 @@ func AppendDeflateBytes(dst, src []byte) []byte { // WriteInflate writes inflated p to w and returns the number of uncompressed // bytes written to w. func WriteInflate(w io.Writer, p []byte) (int, error) { - r := &byteSliceReader{p} + r := &byteSliceReader{b: p} zr, err := acquireFlateReader(r) if err != nil { return 0, err @@ -337,7 +337,7 @@ func WriteInflate(w io.Writer, p []byte) (int, error) { // AppendInflateBytes appends inflated src to dst and returns the resulting dst. func AppendInflateBytes(dst, src []byte) ([]byte, error) { - w := &byteSliceWriter{dst} + w := &byteSliceWriter{b: dst} _, err := WriteInflate(w, src) return w.b, err } diff --git a/src/vendor/github.com/valyala/fasthttp/cookie.go b/src/vendor/github.com/valyala/fasthttp/cookie.go index e99ea675d..3b1fe6b98 100644 --- a/src/vendor/github.com/valyala/fasthttp/cookie.go +++ b/src/vendor/github.com/valyala/fasthttp/cookie.go @@ -67,20 +67,22 @@ var cookiePool = &sync.Pool{ type Cookie struct { noCopy noCopy + expire time.Time + key []byte value []byte - expire time.Time - maxAge int domain []byte path []byte + bufK []byte + bufV []byte + + maxAge int + + sameSite CookieSameSite httpOnly bool secure bool - sameSite CookieSameSite partitioned bool - - bufKV argsKV - buf []byte } // CopyTo copies src cookie to c. @@ -154,14 +156,14 @@ func (c *Cookie) Path() []byte { // SetPath sets cookie path. func (c *Cookie) SetPath(path string) { - c.buf = append(c.buf[:0], path...) - c.path = normalizePath(c.path, c.buf) + c.bufK = append(c.bufK[:0], path...) + c.path = normalizePath(c.path, c.bufK) } // SetPathBytes sets cookie path. func (c *Cookie) SetPathBytes(path []byte) { - c.buf = append(c.buf[:0], path...) - c.path = normalizePath(c.path, c.buf) + c.bufK = append(c.bufK[:0], path...) + c.path = normalizePath(c.path, c.bufK) } // Domain returns cookie domain. @@ -282,11 +284,11 @@ func (c *Cookie) AppendBytes(dst []byte) []byte { dst = append(dst, '=') dst = AppendUint(dst, c.maxAge) } else if !c.expire.IsZero() { - c.bufKV.value = AppendHTTPDate(c.bufKV.value[:0], c.expire) + c.bufV = AppendHTTPDate(c.bufV[:0], c.expire) dst = append(dst, ';', ' ') dst = append(dst, strCookieExpires...) dst = append(dst, '=') - dst = append(dst, c.bufKV.value...) + dst = append(dst, c.bufV...) } if len(c.domain) > 0 { dst = appendCookiePart(dst, strCookieDomain, c.domain) @@ -334,8 +336,8 @@ func (c *Cookie) AppendBytes(dst []byte) []byte { // The returned value is valid until the Cookie reused or released (ReleaseCookie). // Do not store references to the returned value. Make copies instead. func (c *Cookie) Cookie() []byte { - c.buf = c.AppendBytes(c.buf[:0]) - return c.buf + c.bufK = c.AppendBytes(c.bufK[:0]) + return c.bufK } // String returns cookie representation. @@ -355,8 +357,8 @@ var errNoCookies = errors.New("no cookies found") // Parse parses Set-Cookie header. func (c *Cookie) Parse(src string) error { - c.buf = append(c.buf[:0], src...) - return c.ParseBytes(c.buf) + c.bufK = append(c.bufK[:0], src...) + return c.ParseBytes(c.bufK) } // ParseBytes parses Set-Cookie header. @@ -366,21 +368,20 @@ func (c *Cookie) ParseBytes(src []byte) error { var s cookieScanner s.b = src - kv := &c.bufKV - if !s.next(kv) { + if !s.next(&c.bufK, &c.bufV) { return errNoCookies } - c.key = append(c.key, kv.key...) - c.value = append(c.value, kv.value...) + c.key = append(c.key, c.bufK...) + c.value = append(c.value, c.bufV...) - for s.next(kv) { - if len(kv.key) != 0 { + for s.next(&c.bufK, &c.bufV) { + if len(c.bufK) != 0 { // Case insensitive switch on first char - switch kv.key[0] | 0x20 { + switch c.bufK[0] | 0x20 { case 'm': - if caseInsensitiveCompare(strCookieMaxAge, kv.key) { - maxAge, err := ParseUint(kv.value) + if caseInsensitiveCompare(strCookieMaxAge, c.bufK) { + maxAge, err := ParseUint(c.bufV) if err != nil { return err } @@ -388,8 +389,8 @@ func (c *Cookie) ParseBytes(src []byte) error { } case 'e': // "expires" - if caseInsensitiveCompare(strCookieExpires, kv.key) { - v := b2s(kv.value) + if caseInsensitiveCompare(strCookieExpires, c.bufK) { + v := b2s(c.bufV) // Try the same two formats as net/http // See: https://github.com/golang/go/blob/00379be17e63a5b75b3237819392d2dc3b313a27/src/net/http/cookie.go#L133-L135 exptime, err := time.ParseInLocation(time.RFC1123, v, time.UTC) @@ -403,52 +404,52 @@ func (c *Cookie) ParseBytes(src []byte) error { } case 'd': // "domain" - if caseInsensitiveCompare(strCookieDomain, kv.key) { - c.domain = append(c.domain, kv.value...) + if caseInsensitiveCompare(strCookieDomain, c.bufK) { + c.domain = append(c.domain, c.bufV...) } case 'p': // "path" - if caseInsensitiveCompare(strCookiePath, kv.key) { - c.path = append(c.path, kv.value...) + if caseInsensitiveCompare(strCookiePath, c.bufK) { + c.path = append(c.path, c.bufV...) } case 's': // "samesite" - if caseInsensitiveCompare(strCookieSameSite, kv.key) { - if len(kv.value) > 0 { + if caseInsensitiveCompare(strCookieSameSite, c.bufK) { + if len(c.bufV) > 0 { // Case insensitive switch on first char - switch kv.value[0] | 0x20 { + switch c.bufV[0] | 0x20 { case 'l': // "lax" - if caseInsensitiveCompare(strCookieSameSiteLax, kv.value) { + if caseInsensitiveCompare(strCookieSameSiteLax, c.bufV) { c.sameSite = CookieSameSiteLaxMode } case 's': // "strict" - if caseInsensitiveCompare(strCookieSameSiteStrict, kv.value) { + if caseInsensitiveCompare(strCookieSameSiteStrict, c.bufV) { c.sameSite = CookieSameSiteStrictMode } case 'n': // "none" - if caseInsensitiveCompare(strCookieSameSiteNone, kv.value) { + if caseInsensitiveCompare(strCookieSameSiteNone, c.bufV) { c.sameSite = CookieSameSiteNoneMode } } } } } - } else if len(kv.value) != 0 { + } else if len(c.bufV) != 0 { // Case insensitive switch on first char - switch kv.value[0] | 0x20 { + switch c.bufV[0] | 0x20 { case 'h': // "httponly" - if caseInsensitiveCompare(strCookieHTTPOnly, kv.value) { + if caseInsensitiveCompare(strCookieHTTPOnly, c.bufV) { c.httpOnly = true } case 's': // "secure" - if caseInsensitiveCompare(strCookieSecure, kv.value) { + if caseInsensitiveCompare(strCookieSecure, c.bufV) { c.secure = true - } else if caseInsensitiveCompare(strCookieSameSite, kv.value) { + } else if caseInsensitiveCompare(strCookieSameSite, c.bufV) { c.sameSite = CookieSameSiteDefaultMode } case 'p': // "partitioned" - if caseInsensitiveCompare(strCookiePartitioned, kv.value) { + if caseInsensitiveCompare(strCookiePartitioned, c.bufV) { c.partitioned = true } } @@ -505,7 +506,7 @@ func parseRequestCookies(cookies []argsKV, src []byte) []argsKV { s.b = src var kv *argsKV cookies, kv = allocArg(cookies) - for s.next(kv) { + for s.next(&kv.key, &kv.value) { if len(kv.key) > 0 || len(kv.value) > 0 { cookies, kv = allocArg(cookies) } @@ -517,7 +518,7 @@ type cookieScanner struct { b []byte } -func (s *cookieScanner) next(kv *argsKV) bool { +func (s *cookieScanner) next(key, val *[]byte) bool { b := s.b if len(b) == 0 { return false @@ -530,23 +531,23 @@ func (s *cookieScanner) next(kv *argsKV) bool { case '=': if isKey { isKey = false - kv.key = decodeCookieArg(kv.key, b[:i], false) + *key = decodeCookieArg(*key, b[:i], false) k = i + 1 } case ';': if isKey { - kv.key = kv.key[:0] + *key = (*key)[:0] } - kv.value = decodeCookieArg(kv.value, b[k:i], true) + *val = decodeCookieArg(*val, b[k:i], true) s.b = b[i+1:] return true } } if isKey { - kv.key = kv.key[:0] + *key = (*key)[:0] } - kv.value = decodeCookieArg(kv.value, b[k:], true) + *val = decodeCookieArg(*val, b[k:], true) s.b = b[len(b):] return true } diff --git a/src/vendor/github.com/valyala/fasthttp/fasthttputil/inmemory_listener.go b/src/vendor/github.com/valyala/fasthttp/fasthttputil/inmemory_listener.go index 1aaa8e1bf..2df46640b 100644 --- a/src/vendor/github.com/valyala/fasthttp/fasthttputil/inmemory_listener.go +++ b/src/vendor/github.com/valyala/fasthttp/fasthttputil/inmemory_listener.go @@ -14,11 +14,11 @@ var ErrInmemoryListenerClosed = errors.New("InmemoryListener is already closed: // It may be used either for fast in-process client<->server communications // without network stack overhead or for client<->server tests. type InmemoryListener struct { - lock sync.Mutex - closed bool - conns chan acceptConn listenerAddr net.Addr + conns chan acceptConn addrLock sync.RWMutex + lock sync.Mutex + closed bool } type acceptConn struct { @@ -117,7 +117,7 @@ func (ln *InmemoryListener) DialWithLocalAddr(local net.Addr) (net.Conn, error) ln.lock.Lock() accepted := make(chan struct{}) if !ln.closed { - ln.conns <- acceptConn{sConn, accepted} + ln.conns <- acceptConn{conn: sConn, accepted: accepted} // Wait until the connection has been accepted. <-accepted } else { diff --git a/src/vendor/github.com/valyala/fasthttp/fasthttputil/pipeconns.go b/src/vendor/github.com/valyala/fasthttp/fasthttputil/pipeconns.go index d401fe92b..f4466f557 100644 --- a/src/vendor/github.com/valyala/fasthttp/fasthttputil/pipeconns.go +++ b/src/vendor/github.com/valyala/fasthttp/fasthttputil/pipeconns.go @@ -42,9 +42,9 @@ func NewPipeConns() *PipeConns { // // PipeConns is NOT safe for concurrent use by multiple goroutines! type PipeConns struct { + stopCh chan struct{} c1 pipeConn c2 pipeConn - stopCh chan struct{} stopChLock sync.Mutex } @@ -93,8 +93,9 @@ func (pc *PipeConns) Close() error { } type pipeConn struct { - b *byteBuffer - bb []byte + localAddr net.Addr + remoteAddr net.Addr + b *byteBuffer rCh chan *byteBuffer wCh chan *byteBuffer @@ -106,11 +107,11 @@ type pipeConn struct { readDeadlineCh <-chan time.Time writeDeadlineCh <-chan time.Time - readDeadlineChLock sync.Mutex + bb []byte - localAddr net.Addr - remoteAddr net.Addr - addrLock sync.RWMutex + addrLock sync.RWMutex + + readDeadlineChLock sync.Mutex } func (c *pipeConn) Write(p []byte) (int, error) { diff --git a/src/vendor/github.com/valyala/fasthttp/fs.go b/src/vendor/github.com/valyala/fasthttp/fs.go index 7738ca2a0..22f821786 100644 --- a/src/vendor/github.com/valyala/fasthttp/fs.go +++ b/src/vendor/github.com/valyala/fasthttp/fs.go @@ -256,14 +256,46 @@ type FS struct { // FS is filesystem to serve files from. eg: embed.FS os.DirFS FS fs.FS + // Path rewriting function. + // + // By default request path is not modified. + PathRewrite PathRewriteFunc + + // PathNotFound fires when file is not found in filesystem + // this functions tries to replace "Cannot open requested path" + // server response giving to the programmer the control of server flow. + // + // By default PathNotFound returns + // "Cannot open requested path" + PathNotFound RequestHandler + + // Suffixes list to add to compressedFileSuffix depending on encoding + // + // This value has sense only if Compress is set. + // + // FSCompressedFileSuffixes is used by default. + CompressedFileSuffixes map[string]string + + // If CleanStop is set, the channel can be closed to stop the cleanup handlers + // for the FS RequestHandlers created with NewRequestHandler. + // NEVER close this channel while the handler is still being used! + CleanStop chan struct{} + + h RequestHandler + // Path to the root directory to serve files from. Root string - // AllowEmptyRoot controls what happens when Root is empty. When false (default) it will default to the - // current working directory. An empty root is mostly useful when you want to use absolute paths - // on windows that are on different filesystems. On linux setting your Root to "/" already allows you to use - // absolute paths on any filesystem. - AllowEmptyRoot bool + // Path to the compressed root directory to serve files from. If this value + // is empty, Root is used. + CompressRoot string + + // Suffix to add to the name of cached compressed file. + // + // This value has sense only if Compress is set. + // + // FSCompressedFileSuffix is used by default. + CompressedFileSuffix string // List of index file names to try opening during directory access. // @@ -276,6 +308,26 @@ type FS struct { // By default the list is empty. IndexNames []string + // Expiration duration for inactive file handlers. + // + // FSHandlerCacheDuration is used by default. + CacheDuration time.Duration + + once sync.Once + + // AllowEmptyRoot controls what happens when Root is empty. When false (default) it will default to the + // current working directory. An empty root is mostly useful when you want to use absolute paths + // on windows that are on different filesystems. On linux setting your Root to "/" already allows you to use + // absolute paths on any filesystem. + AllowEmptyRoot bool + + // Uses brotli encoding and fallbacks to gzip in responses if set to true, uses gzip if set to false. + // + // This value has sense only if Compress is set. + // + // Brotli encoding is disabled by default. + CompressBrotli bool + // Index pages for directories without files matching IndexNames // are automatically generated if set. // @@ -298,66 +350,15 @@ type FS struct { // Transparent compression is disabled by default. Compress bool - // Uses brotli encoding and fallbacks to gzip in responses if set to true, uses gzip if set to false. - // - // This value has sense only if Compress is set. - // - // Brotli encoding is disabled by default. - CompressBrotli bool - - // Path to the compressed root directory to serve files from. If this value - // is empty, Root is used. - CompressRoot string - // Enables byte range requests if set to true. // // Byte range requests are disabled by default. AcceptByteRange bool - // Path rewriting function. - // - // By default request path is not modified. - PathRewrite PathRewriteFunc - - // PathNotFound fires when file is not found in filesystem - // this functions tries to replace "Cannot open requested path" - // server response giving to the programmer the control of server flow. - // - // By default PathNotFound returns - // "Cannot open requested path" - PathNotFound RequestHandler - // SkipCache if true, will cache no file handler. // // By default is false. SkipCache bool - - // Expiration duration for inactive file handlers. - // - // FSHandlerCacheDuration is used by default. - CacheDuration time.Duration - - // Suffix to add to the name of cached compressed file. - // - // This value has sense only if Compress is set. - // - // FSCompressedFileSuffix is used by default. - CompressedFileSuffix string - - // Suffixes list to add to compressedFileSuffix depending on encoding - // - // This value has sense only if Compress is set. - // - // FSCompressedFileSuffixes is used by default. - CompressedFileSuffixes map[string]string - - // If CleanStop is set, the channel can be closed to stop the cleanup handlers - // for the FS RequestHandlers created with NewRequestHandler. - // NEVER close this channel while the handler is still being used! - CleanStop chan struct{} - - once sync.Once - h RequestHandler } // FSCompressedFileSuffix is the suffix FS adds to the original file names @@ -502,40 +503,41 @@ func (fs *FS) initRequestHandler() { } type fsHandler struct { - filesystem fs.FS - root string - indexNames []string + smallFileReaderPool sync.Pool + filesystem fs.FS + + cacheManager cacheManager + pathRewrite PathRewriteFunc pathNotFound RequestHandler - generateIndexPages bool - compress bool - compressBrotli bool - compressRoot string - acceptByteRange bool compressedFileSuffixes map[string]string - cacheManager cacheManager - - smallFileReaderPool sync.Pool + root string + compressRoot string + indexNames []string + generateIndexPages bool + compress bool + compressBrotli bool + acceptByteRange bool } type fsFile struct { - h *fsHandler - f fs.File - filename string // fs.FileInfo.Name() return filename, isn't filepath. - dirIndex []byte - contentType string - contentLength int - compressed bool - - lastModified time.Time + lastModified time.Time + + t time.Time + f fs.File + h *fsHandler + filename string // fs.FileInfo.Name() return filename, isn't filepath. + contentType string + dirIndex []byte lastModifiedStr []byte - t time.Time - readersCount int + bigFiles []*bigFileReader + contentLength int + readersCount int - bigFiles []*bigFileReader bigFilesLock sync.Mutex + compressed bool } func (ff *fsFile) NewReader() (io.Reader, error) { @@ -817,6 +819,7 @@ func newCacheManager(fs *FS) cacheManager { cache: make(map[string]*fsFile), cacheBrotli: make(map[string]*fsFile), cacheGzip: make(map[string]*fsFile), + cacheZstd: make(map[string]*fsFile), } go instance.handleCleanCache(fs.CleanStop) @@ -845,10 +848,11 @@ func (*noopCacheManager) SetFileToCache(cacheKind CacheKind, path string, ff *fs } type inMemoryCacheManager struct { - cacheDuration time.Duration cache map[string]*fsFile cacheBrotli map[string]*fsFile cacheGzip map[string]*fsFile + cacheZstd map[string]*fsFile + cacheDuration time.Duration cacheLock sync.Mutex } @@ -867,6 +871,8 @@ func (cm *inMemoryCacheManager) getFsCache(cacheKind CacheKind) map[string]*fsFi fileCache = cm.cacheBrotli case gzipCacheKind: fileCache = cm.cacheGzip + case zstdCacheKind: + fileCache = cm.cacheZstd } return fileCache @@ -957,6 +963,7 @@ func (cm *inMemoryCacheManager) cleanCache(pendingFiles []*fsFile) []*fsFile { pendingFiles, filesToRelease = cleanCacheNolock(cm.cache, pendingFiles, filesToRelease, cm.cacheDuration) pendingFiles, filesToRelease = cleanCacheNolock(cm.cacheBrotli, pendingFiles, filesToRelease, cm.cacheDuration) pendingFiles, filesToRelease = cleanCacheNolock(cm.cacheGzip, pendingFiles, filesToRelease, cm.cacheDuration) + pendingFiles, filesToRelease = cleanCacheNolock(cm.cacheZstd, pendingFiles, filesToRelease, cm.cacheDuration) cm.cacheLock.Unlock() @@ -1015,7 +1022,6 @@ func (h *fsHandler) handleRequest(ctx *RequestCtx) { path = ctx.Path() } hasTrailingSlash := len(path) > 0 && path[len(path)-1] == '/' - path = stripTrailingSlashes(path) if n := bytes.IndexByte(path, 0); n >= 0 { ctx.Logger().Printf("cannot serve path with nil byte at position %d: %q", n, path) @@ -1054,9 +1060,13 @@ func (h *fsHandler) handleRequest(ctx *RequestCtx) { } } - pathStr := string(path) + originalPathStr := string(path) + pathStr := originalPathStr + if hasTrailingSlash { + pathStr = originalPathStr[:len(originalPathStr)-1] + } - ff, ok := h.cacheManager.GetFileFromCache(fileCacheKind, pathStr) + ff, ok := h.cacheManager.GetFileFromCache(fileCacheKind, originalPathStr) if !ok { filePath := h.pathToFilePath(pathStr) @@ -1091,7 +1101,7 @@ func (h *fsHandler) handleRequest(ctx *RequestCtx) { return } - ff = h.cacheManager.SetFileToCache(fileCacheKind, pathStr, ff) + ff = h.cacheManager.SetFileToCache(fileCacheKind, originalPathStr, ff) } if !ctx.IfModifiedSince(ff.lastModified) { @@ -1396,7 +1406,7 @@ func (h *fsHandler) compressAndOpenFSFile(filePath, fileEncoding string) (*fsFil } if compressedFilePath != filePath { - if err := os.MkdirAll(filepath.Dir(compressedFilePath), os.ModePerm); err != nil { + if err := os.MkdirAll(filepath.Dir(compressedFilePath), 0o750); err != nil { return nil, err } } diff --git a/src/vendor/github.com/valyala/fasthttp/header.go b/src/vendor/github.com/valyala/fasthttp/header.go index b02629447..b6d8ffec9 100644 --- a/src/vendor/github.com/valyala/fasthttp/header.go +++ b/src/vendor/github.com/valyala/fasthttp/header.go @@ -26,18 +26,9 @@ const ( type ResponseHeader struct { noCopy noCopy - disableNormalizing bool - noHTTP11 bool - connectionClose bool - noDefaultContentType bool - noDefaultDate bool - - statusCode int - statusMessage []byte - protocol []byte - contentLength int - contentLengthBytes []byte - secureErrorLogMessage bool + statusMessage []byte + protocol []byte + contentLengthBytes []byte contentType []byte contentEncoding []byte @@ -46,9 +37,20 @@ type ResponseHeader struct { h []argsKV trailer []argsKV - bufKV argsKV cookies []argsKV + bufK []byte + bufV []byte + + statusCode int + contentLength int + + disableNormalizing bool + noHTTP11 bool + connectionClose bool + noDefaultContentType bool + noDefaultDate bool + secureErrorLogMessage bool } // RequestHeader represents HTTP request header. @@ -61,19 +63,7 @@ type ResponseHeader struct { type RequestHeader struct { noCopy noCopy - disableNormalizing bool - noHTTP11 bool - connectionClose bool - noDefaultContentType bool - disableSpecialHeader bool - - // These two fields have been moved close to other bool fields - // for reducing RequestHeader object size. - cookiesCollected bool - - contentLength int - contentLengthBytes []byte - secureErrorLogMessage bool + contentLengthBytes []byte method []byte requestURI []byte @@ -85,19 +75,34 @@ type RequestHeader struct { h []argsKV trailer []argsKV - bufKV argsKV cookies []argsKV // stores an immutable copy of headers as they were received from the // wire. rawHeaders []byte + bufK []byte + bufV []byte + + contentLength int + + disableNormalizing bool + noHTTP11 bool + connectionClose bool + noDefaultContentType bool + disableSpecialHeader bool + + // These two fields have been moved close to other bool fields + // for reducing RequestHeader object size. + cookiesCollected bool + + secureErrorLogMessage bool } // SetContentRange sets 'Content-Range: bytes startPos-endPos/contentLength' // header. func (h *ResponseHeader) SetContentRange(startPos, endPos, contentLength int) { - b := h.bufKV.value[:0] + b := h.bufV[:0] b = append(b, strBytes...) b = append(b, ' ') b = AppendUint(b, startPos) @@ -105,9 +110,9 @@ func (h *ResponseHeader) SetContentRange(startPos, endPos, contentLength int) { b = AppendUint(b, endPos) b = append(b, '/') b = AppendUint(b, contentLength) - h.bufKV.value = b + h.bufV = b - h.setNonSpecial(strContentRange, h.bufKV.value) + h.setNonSpecial(strContentRange, h.bufV) } // SetByteRange sets 'Range: bytes=startPos-endPos' header. @@ -115,7 +120,7 @@ func (h *ResponseHeader) SetContentRange(startPos, endPos, contentLength int) { // - If startPos is negative, then 'bytes=-startPos' value is set. // - If endPos is negative, then 'bytes=startPos-' value is set. func (h *RequestHeader) SetByteRange(startPos, endPos int) { - b := h.bufKV.value[:0] + b := h.bufV[:0] b = append(b, strBytes...) b = append(b, '=') if startPos >= 0 { @@ -127,9 +132,9 @@ func (h *RequestHeader) SetByteRange(startPos, endPos int) { if endPos >= 0 { b = AppendUint(b, endPos) } - h.bufKV.value = b + h.bufV = b - h.setNonSpecial(strRange, h.bufKV.value) + h.setNonSpecial(strRange, h.bufV) } // StatusCode returns response status code. @@ -170,8 +175,8 @@ func (h *ResponseHeader) SetProtocol(protocol []byte) { // SetLastModified sets 'Last-Modified' header to the given value. func (h *ResponseHeader) SetLastModified(t time.Time) { - h.bufKV.value = AppendHTTPDate(h.bufKV.value[:0], t) - h.setNonSpecial(strLastModified, h.bufKV.value) + h.bufV = AppendHTTPDate(h.bufV[:0], t) + h.setNonSpecial(strLastModified, h.bufV) } // ConnectionClose returns true if 'Connection: close' header is set. @@ -408,30 +413,30 @@ func (h *RequestHeader) SetContentEncodingBytes(contentEncoding []byte) { // 'multipart/form-data; boundary=...' // where ... is substituted by the given boundary. func (h *RequestHeader) SetMultipartFormBoundary(boundary string) { - b := h.bufKV.value[:0] + b := h.bufV[:0] b = append(b, strMultipartFormData...) b = append(b, ';', ' ') b = append(b, strBoundary...) b = append(b, '=') b = append(b, boundary...) - h.bufKV.value = b + h.bufV = b - h.SetContentTypeBytes(h.bufKV.value) + h.SetContentTypeBytes(h.bufV) } // SetMultipartFormBoundaryBytes sets the following Content-Type: // 'multipart/form-data; boundary=...' // where ... is substituted by the given boundary. func (h *RequestHeader) SetMultipartFormBoundaryBytes(boundary []byte) { - b := h.bufKV.value[:0] + b := h.bufV[:0] b = append(b, strMultipartFormData...) b = append(b, ';', ' ') b = append(b, strBoundary...) b = append(b, '=') b = append(b, boundary...) - h.bufKV.value = b + h.bufV = b - h.SetContentTypeBytes(h.bufKV.value) + h.SetContentTypeBytes(h.bufV) } // SetTrailer sets header Trailer value for chunked response @@ -537,20 +542,26 @@ func (h *ResponseHeader) AddTrailerBytes(trailer []byte) error { err = ErrBadTrailer continue } - h.bufKV.key = append(h.bufKV.key[:0], key...) - normalizeHeaderKey(h.bufKV.key, h.disableNormalizing) - h.trailer = appendArgBytes(h.trailer, h.bufKV.key, nil, argsNoValue) + h.bufK = append(h.bufK[:0], key...) + normalizeHeaderKey(h.bufK, h.disableNormalizing) + h.trailer = appendArgBytes(h.trailer, h.bufK, nil, argsNoValue) } return err } -// validHeaderFieldByte returns true if c is a valid tchar as defined -// by section 5.6.2 of [RFC9110]. +// validHeaderFieldByte returns true if c valid header field byte +// as defined by RFC 7230. func validHeaderFieldByte(c byte) bool { return c < 128 && validHeaderFieldByteTable[c] == 1 } +// validHeaderValueByte returns true if c valid header value byte +// as defined by RFC 7230. +func validHeaderValueByte(c byte) bool { + return validHeaderValueByteTable[c] == 1 +} + // VisitHeaderParams calls f for each parameter in the given header bytes. // It stops processing when f returns false or an invalid parameter is found. // Parameter values may be quoted, in which case \ is treated as an escape @@ -879,9 +890,9 @@ func (h *RequestHeader) AddTrailerBytes(trailer []byte) error { err = ErrBadTrailer continue } - h.bufKV.key = append(h.bufKV.key[:0], key...) - normalizeHeaderKey(h.bufKV.key, h.disableNormalizing) - h.trailer = appendArgBytes(h.trailer, h.bufKV.key, nil, argsNoValue) + h.bufK = append(h.bufK[:0], key...) + normalizeHeaderKey(h.bufK, h.disableNormalizing) + h.trailer = appendArgBytes(h.trailer, h.bufK, nil, argsNoValue) } return err @@ -945,8 +956,8 @@ func (h *ResponseHeader) IsHTTP11() bool { // HasAcceptEncoding returns true if the header contains // the given Accept-Encoding value. func (h *RequestHeader) HasAcceptEncoding(acceptEncoding string) bool { - h.bufKV.value = append(h.bufKV.value[:0], acceptEncoding...) - return h.HasAcceptEncodingBytes(h.bufKV.value) + h.bufV = append(h.bufV[:0], acceptEncoding...) + return h.HasAcceptEncodingBytes(h.bufV) } // HasAcceptEncodingBytes returns true if the header contains @@ -1275,8 +1286,8 @@ func (h *RequestHeader) VisitAll(f func(key, value []byte)) { h.collectCookies() if len(h.cookies) > 0 { - h.bufKV.value = appendRequestCookieBytes(h.bufKV.value[:0], h.cookies) - f(strCookie, h.bufKV.value) + h.bufV = appendRequestCookieBytes(h.bufV[:0], h.cookies) + f(strCookie, h.bufV) } visitArgs(h.h, f) if h.ConnectionClose() { @@ -1304,15 +1315,15 @@ func (h *RequestHeader) VisitAllInOrder(f func(key, value []byte)) { // Del deletes header with the given key. func (h *ResponseHeader) Del(key string) { - k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing) - h.del(k) + h.bufK = getHeaderKeyBytes(h.bufK, key, h.disableNormalizing) + h.del(h.bufK) } // DelBytes deletes header with the given key. func (h *ResponseHeader) DelBytes(key []byte) { - h.bufKV.key = append(h.bufKV.key[:0], key...) - normalizeHeaderKey(h.bufKV.key, h.disableNormalizing) - h.del(h.bufKV.key) + h.bufK = append(h.bufK[:0], key...) + normalizeHeaderKey(h.bufK, h.disableNormalizing) + h.del(h.bufK) } func (h *ResponseHeader) del(key []byte) { @@ -1338,15 +1349,15 @@ func (h *ResponseHeader) del(key []byte) { // Del deletes header with the given key. func (h *RequestHeader) Del(key string) { - k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing) - h.del(k) + h.bufK = getHeaderKeyBytes(h.bufK, key, h.disableNormalizing) + h.del(h.bufK) } // DelBytes deletes header with the given key. func (h *RequestHeader) DelBytes(key []byte) { - h.bufKV.key = append(h.bufKV.key[:0], key...) - normalizeHeaderKey(h.bufKV.key, h.disableNormalizing) - h.del(h.bufKV.key) + h.bufK = append(h.bufK[:0], key...) + normalizeHeaderKey(h.bufK, h.disableNormalizing) + h.del(h.bufK) } func (h *RequestHeader) del(key []byte) { @@ -1554,8 +1565,8 @@ func (h *ResponseHeader) AddBytesKV(key, value []byte) { return } - k := getHeaderKeyBytes(&h.bufKV, b2s(key), h.disableNormalizing) - h.h = appendArgBytes(h.h, k, value, argsHasValue) + h.bufK = getHeaderKeyBytes(h.bufK, b2s(key), h.disableNormalizing) + h.h = appendArgBytes(h.h, h.bufK, value, argsHasValue) } // Set sets the given 'key: value' header. @@ -1565,8 +1576,8 @@ func (h *ResponseHeader) AddBytesKV(key, value []byte) { // // Use Add for setting multiple header values under the same key. func (h *ResponseHeader) Set(key, value string) { - initHeaderKV(&h.bufKV, key, value, h.disableNormalizing) - h.SetCanonical(h.bufKV.key, h.bufKV.value) + h.bufK, h.bufV = initHeaderKV(h.bufK, h.bufV, key, value, h.disableNormalizing) + h.SetCanonical(h.bufK, h.bufV) } // SetBytesK sets the given 'key: value' header. @@ -1576,8 +1587,8 @@ func (h *ResponseHeader) Set(key, value string) { // // Use AddBytesK for setting multiple header values under the same key. func (h *ResponseHeader) SetBytesK(key []byte, value string) { - h.bufKV.value = append(h.bufKV.value[:0], value...) - h.SetBytesKV(key, h.bufKV.value) + h.bufV = append(h.bufV[:0], value...) + h.SetBytesKV(key, h.bufV) } // SetBytesV sets the given 'key: value' header. @@ -1587,8 +1598,8 @@ func (h *ResponseHeader) SetBytesK(key []byte, value string) { // // Use AddBytesV for setting multiple header values under the same key. func (h *ResponseHeader) SetBytesV(key string, value []byte) { - k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing) - h.SetCanonical(k, value) + h.bufK = getHeaderKeyBytes(h.bufK, key, h.disableNormalizing) + h.SetCanonical(h.bufK, value) } // SetBytesKV sets the given 'key: value' header. @@ -1598,9 +1609,9 @@ func (h *ResponseHeader) SetBytesV(key string, value []byte) { // // Use AddBytesKV for setting multiple header values under the same key. func (h *ResponseHeader) SetBytesKV(key, value []byte) { - h.bufKV.key = append(h.bufKV.key[:0], key...) - normalizeHeaderKey(h.bufKV.key, h.disableNormalizing) - h.SetCanonical(h.bufKV.key, value) + h.bufK = append(h.bufK[:0], key...) + normalizeHeaderKey(h.bufK, h.disableNormalizing) + h.SetCanonical(h.bufK, value) } // SetCanonical sets the given 'key: value' header assuming that @@ -1765,8 +1776,8 @@ func (h *RequestHeader) AddBytesKV(key, value []byte) { return } - k := getHeaderKeyBytes(&h.bufKV, b2s(key), h.disableNormalizing) - h.h = appendArgBytes(h.h, k, value, argsHasValue) + h.bufK = getHeaderKeyBytes(h.bufK, b2s(key), h.disableNormalizing) + h.h = appendArgBytes(h.h, h.bufK, value, argsHasValue) } // Set sets the given 'key: value' header. @@ -1776,8 +1787,8 @@ func (h *RequestHeader) AddBytesKV(key, value []byte) { // // Use Add for setting multiple header values under the same key. func (h *RequestHeader) Set(key, value string) { - initHeaderKV(&h.bufKV, key, value, h.disableNormalizing) - h.SetCanonical(h.bufKV.key, h.bufKV.value) + h.bufK, h.bufV = initHeaderKV(h.bufK, h.bufV, key, value, h.disableNormalizing) + h.SetCanonical(h.bufK, h.bufV) } // SetBytesK sets the given 'key: value' header. @@ -1787,8 +1798,8 @@ func (h *RequestHeader) Set(key, value string) { // // Use AddBytesK for setting multiple header values under the same key. func (h *RequestHeader) SetBytesK(key []byte, value string) { - h.bufKV.value = append(h.bufKV.value[:0], value...) - h.SetBytesKV(key, h.bufKV.value) + h.bufV = append(h.bufV[:0], value...) + h.SetBytesKV(key, h.bufV) } // SetBytesV sets the given 'key: value' header. @@ -1798,8 +1809,8 @@ func (h *RequestHeader) SetBytesK(key []byte, value string) { // // Use AddBytesV for setting multiple header values under the same key. func (h *RequestHeader) SetBytesV(key string, value []byte) { - k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing) - h.SetCanonical(k, value) + h.bufK = getHeaderKeyBytes(h.bufK, key, h.disableNormalizing) + h.SetCanonical(h.bufK, value) } // SetBytesKV sets the given 'key: value' header. @@ -1809,9 +1820,9 @@ func (h *RequestHeader) SetBytesV(key string, value []byte) { // // Use AddBytesKV for setting multiple header values under the same key. func (h *RequestHeader) SetBytesKV(key, value []byte) { - h.bufKV.key = append(h.bufKV.key[:0], key...) - normalizeHeaderKey(h.bufKV.key, h.disableNormalizing) - h.SetCanonical(h.bufKV.key, value) + h.bufK = append(h.bufK[:0], key...) + normalizeHeaderKey(h.bufK, h.disableNormalizing) + h.SetCanonical(h.bufK, value) } // SetCanonical sets the given 'key: value' header assuming that @@ -1832,8 +1843,8 @@ func (h *RequestHeader) SetCanonical(key, value []byte) { // either though ReleaseResponse or your request handler returning. // Do not store references to the returned value. Make copies instead. func (h *ResponseHeader) Peek(key string) []byte { - k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing) - return h.peek(k) + h.bufK = getHeaderKeyBytes(h.bufK, key, h.disableNormalizing) + return h.peek(h.bufK) } // PeekBytes returns header value for the given key. @@ -1842,9 +1853,9 @@ func (h *ResponseHeader) Peek(key string) []byte { // either though ReleaseResponse or your request handler returning. // Do not store references to returned value. Make copies instead. func (h *ResponseHeader) PeekBytes(key []byte) []byte { - h.bufKV.key = append(h.bufKV.key[:0], key...) - normalizeHeaderKey(h.bufKV.key, h.disableNormalizing) - return h.peek(h.bufKV.key) + h.bufK = append(h.bufK[:0], key...) + normalizeHeaderKey(h.bufK, h.disableNormalizing) + return h.peek(h.bufK) } // Peek returns header value for the given key. @@ -1853,8 +1864,8 @@ func (h *ResponseHeader) PeekBytes(key []byte) []byte { // either though ReleaseRequest or your request handler returning. // Do not store references to returned value. Make copies instead. func (h *RequestHeader) Peek(key string) []byte { - k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing) - return h.peek(k) + h.bufK = getHeaderKeyBytes(h.bufK, key, h.disableNormalizing) + return h.peek(h.bufK) } // PeekBytes returns header value for the given key. @@ -1863,9 +1874,9 @@ func (h *RequestHeader) Peek(key string) []byte { // either though ReleaseRequest or your request handler returning. // Do not store references to returned value. Make copies instead. func (h *RequestHeader) PeekBytes(key []byte) []byte { - h.bufKV.key = append(h.bufKV.key[:0], key...) - normalizeHeaderKey(h.bufKV.key, h.disableNormalizing) - return h.peek(h.bufKV.key) + h.bufK = append(h.bufK[:0], key...) + normalizeHeaderKey(h.bufK, h.disableNormalizing) + return h.peek(h.bufK) } func (h *ResponseHeader) peek(key []byte) []byte { @@ -1926,8 +1937,8 @@ func (h *RequestHeader) peek(key []byte) []byte { // Any future calls to the Peek* will modify the returned value. // Do not store references to returned value. Make copies instead. func (h *RequestHeader) PeekAll(key string) [][]byte { - k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing) - return h.peekAll(k) + h.bufK = getHeaderKeyBytes(h.bufK, key, h.disableNormalizing) + return h.peekAll(h.bufK) } func (h *RequestHeader) peekAll(key []byte) [][]byte { @@ -1974,8 +1985,8 @@ func (h *RequestHeader) peekAll(key []byte) [][]byte { // Any future calls to the Peek* will modify the returned value. // Do not store references to returned value. Make copies instead. func (h *ResponseHeader) PeekAll(key string) [][]byte { - k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing) - return h.peekAll(k) + h.bufK = getHeaderKeyBytes(h.bufK, key, h.disableNormalizing) + return h.peekAll(h.bufK) } func (h *ResponseHeader) peekAll(key []byte) [][]byte { @@ -2323,7 +2334,7 @@ func (h *RequestHeader) tryRead(r *bufio.Reader, n int) error { // n == 1 on the first read for the request. if n == 1 { // We didn't read a single byte. - return ErrNothingRead{err} + return ErrNothingRead{error: err} } return fmt.Errorf("error when reading request headers: %w", err) @@ -2403,8 +2414,8 @@ func (h *ResponseHeader) WriteTo(w io.Writer) (int64, error) { // either though ReleaseRequest or your request handler returning. // Do not store references to returned value. Make copies instead. func (h *ResponseHeader) Header() []byte { - h.bufKV.value = h.AppendBytes(h.bufKV.value[:0]) - return h.bufKV.value + h.bufV = h.AppendBytes(h.bufV[:0]) + return h.bufV } // writeTrailer writes response trailer to w. @@ -2421,13 +2432,13 @@ func (h *ResponseHeader) writeTrailer(w *bufio.Writer) error { // either though ReleaseRequest or your request handler returning. // Do not store references to returned value. Make copies instead. func (h *ResponseHeader) TrailerHeader() []byte { - h.bufKV.value = h.bufKV.value[:0] + h.bufV = h.bufV[:0] for _, t := range h.trailer { value := h.peek(t.key) - h.bufKV.value = appendHeaderLine(h.bufKV.value, t.key, value) + h.bufV = appendHeaderLine(h.bufV, t.key, value) } - h.bufKV.value = append(h.bufKV.value, strCRLF...) - return h.bufKV.value + h.bufV = append(h.bufV, strCRLF...) + return h.bufV } // String returns response header representation. @@ -2535,8 +2546,8 @@ func (h *RequestHeader) WriteTo(w io.Writer) (int64, error) { // either though ReleaseRequest or your request handler returning. // Do not store references to returned value. Make copies instead. func (h *RequestHeader) Header() []byte { - h.bufKV.value = h.AppendBytes(h.bufKV.value[:0]) - return h.bufKV.value + h.bufV = h.AppendBytes(h.bufV[:0]) + return h.bufV } // writeTrailer writes request trailer to w. @@ -2553,13 +2564,13 @@ func (h *RequestHeader) writeTrailer(w *bufio.Writer) error { // either though ReleaseRequest or your request handler returning. // Do not store references to returned value. Make copies instead. func (h *RequestHeader) TrailerHeader() []byte { - h.bufKV.value = h.bufKV.value[:0] + h.bufV = h.bufV[:0] for _, t := range h.trailer { value := h.peek(t.key) - h.bufKV.value = appendHeaderLine(h.bufKV.value, t.key, value) + h.bufV = appendHeaderLine(h.bufV, t.key, value) } - h.bufKV.value = append(h.bufKV.value, strCRLF...) - return h.bufKV.value + h.bufV = append(h.bufV, strCRLF...) + return h.bufV } // RawHeaders returns raw header key/value bytes. @@ -2849,6 +2860,15 @@ func (h *ResponseHeader) parseFirstLine(buf []byte) (int, error) { return len(buf) - len(bNext), nil } +func isValidMethod(method []byte) bool { + for _, ch := range method { + if validMethodValueByteTable[ch] == 0 { + return false + } + } + return true +} + func (h *RequestHeader) parseFirstLine(buf []byte) (int, error) { bNext := buf var b []byte @@ -2868,6 +2888,14 @@ func (h *RequestHeader) parseFirstLine(buf []byte) (int, error) { return 0, fmt.Errorf("cannot find http request method in %q", buf) } h.method = append(h.method[:0], b[:n]...) + + if !isValidMethod(h.method) { + if h.secureErrorLogMessage { + return 0, errors.New("unsupported http request method") + } + return 0, fmt.Errorf("unsupported http request method %q in %q", h.method, buf) + } + b = b[n+1:] // parse requestURI @@ -2945,75 +2973,90 @@ func (h *ResponseHeader) parseHeaders(buf []byte) (int, error) { var s headerScanner s.b = buf s.disableNormalizing = h.disableNormalizing - var err error var kv *argsKV -outer: for s.next() { - if len(s.key) > 0 { - for _, ch := range s.key { - if !validHeaderFieldByte(ch) { - err = fmt.Errorf("invalid header key %q", s.key) - continue outer - } + if len(s.key) == 0 { + h.connectionClose = true + return 0, fmt.Errorf("invalid header key %q", s.key) + } + + for _, ch := range s.key { + if !validHeaderFieldByte(ch) { + h.connectionClose = true + return 0, fmt.Errorf("invalid header key %q", s.key) + } + } + for _, ch := range s.value { + if !validHeaderValueByte(ch) { + h.connectionClose = true + return 0, fmt.Errorf("invalid header value %q", s.value) } + } - switch s.key[0] | 0x20 { - case 'c': - if caseInsensitiveCompare(s.key, strContentType) { - h.contentType = append(h.contentType[:0], s.value...) - continue - } - if caseInsensitiveCompare(s.key, strContentEncoding) { - h.contentEncoding = append(h.contentEncoding[:0], s.value...) - continue - } - if caseInsensitiveCompare(s.key, strContentLength) { - if h.contentLength != -1 { - if h.contentLength, err = parseContentLength(s.value); err != nil { - h.contentLength = -2 - } else { - h.contentLengthBytes = append(h.contentLengthBytes[:0], s.value...) - } - } - continue - } - if caseInsensitiveCompare(s.key, strConnection) { - if bytes.Equal(s.value, strClose) { + switch s.key[0] | 0x20 { + case 'c': + if caseInsensitiveCompare(s.key, strContentType) { + h.contentType = append(h.contentType[:0], s.value...) + continue + } + if caseInsensitiveCompare(s.key, strContentEncoding) { + h.contentEncoding = append(h.contentEncoding[:0], s.value...) + continue + } + if caseInsensitiveCompare(s.key, strContentLength) { + if h.contentLength != -1 { + var err error + h.contentLength, err = parseContentLength(s.value) + if err != nil { + h.contentLength = -2 h.connectionClose = true - } else { - h.connectionClose = false - h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue) + return 0, err } - continue - } - case 's': - if caseInsensitiveCompare(s.key, strServer) { - h.server = append(h.server[:0], s.value...) - continue + h.contentLengthBytes = append(h.contentLengthBytes[:0], s.value...) } - if caseInsensitiveCompare(s.key, strSetCookie) { - h.cookies, kv = allocArg(h.cookies) - kv.key = getCookieKey(kv.key, s.value) - kv.value = append(kv.value[:0], s.value...) - continue + continue + } + if caseInsensitiveCompare(s.key, strConnection) { + if bytes.Equal(s.value, strClose) { + h.connectionClose = true + } else { + h.connectionClose = false + h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue) } - case 't': - if caseInsensitiveCompare(s.key, strTransferEncoding) { - if len(s.value) > 0 && !bytes.Equal(s.value, strIdentity) { - h.contentLength = -1 - h.h = setArgBytes(h.h, strTransferEncoding, strChunked, argsHasValue) - } - continue + continue + } + case 's': + if caseInsensitiveCompare(s.key, strServer) { + h.server = append(h.server[:0], s.value...) + continue + } + if caseInsensitiveCompare(s.key, strSetCookie) { + h.cookies, kv = allocArg(h.cookies) + kv.key = getCookieKey(kv.key, s.value) + kv.value = append(kv.value[:0], s.value...) + continue + } + case 't': + if caseInsensitiveCompare(s.key, strTransferEncoding) { + if len(s.value) > 0 && !bytes.Equal(s.value, strIdentity) { + h.contentLength = -1 + h.h = setArgBytes(h.h, strTransferEncoding, strChunked, argsHasValue) } - if caseInsensitiveCompare(s.key, strTrailer) { - err = h.SetTrailerBytes(s.value) - continue + continue + } + if caseInsensitiveCompare(s.key, strTrailer) { + err := h.SetTrailerBytes(s.value) + if err != nil { + h.connectionClose = true + return 0, err } + continue } - h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue) } + h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue) } + if s.err != nil { h.connectionClose = true return 0, s.err @@ -3032,7 +3075,7 @@ outer: h.connectionClose = !hasHeaderValue(v, strKeepAlive) } - return len(buf) - len(s.b), err + return len(buf) - len(s.b), nil } func (h *RequestHeader) parseHeaders(buf []byte) (int, error) { @@ -3043,103 +3086,109 @@ func (h *RequestHeader) parseHeaders(buf []byte) (int, error) { var s headerScanner s.b = buf s.disableNormalizing = h.disableNormalizing - var err error -outer: for s.next() { - if len(s.key) > 0 { - for _, ch := range s.key { - if !validHeaderFieldByte(ch) { - err = fmt.Errorf("invalid header key %q", s.key) - continue outer - } + if len(s.key) == 0 { + h.connectionClose = true + return 0, fmt.Errorf("invalid header key %q", s.key) + } + + for _, ch := range s.key { + if !validHeaderFieldByte(ch) { + h.connectionClose = true + return 0, fmt.Errorf("invalid header key %q", s.key) } + } + for _, ch := range s.value { + if !validHeaderValueByte(ch) { + h.connectionClose = true + return 0, fmt.Errorf("invalid header value %q", s.value) + } + } + + if h.disableSpecialHeader { + h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue) + continue + } - if h.disableSpecialHeader { - h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue) + switch s.key[0] | 0x20 { + case 'h': + if caseInsensitiveCompare(s.key, strHost) { + h.host = append(h.host[:0], s.value...) continue } - - switch s.key[0] | 0x20 { - case 'h': - if caseInsensitiveCompare(s.key, strHost) { - h.host = append(h.host[:0], s.value...) - continue - } - case 'u': - if caseInsensitiveCompare(s.key, strUserAgent) { - h.userAgent = append(h.userAgent[:0], s.value...) - continue - } - case 'c': - if caseInsensitiveCompare(s.key, strContentType) { - h.contentType = append(h.contentType[:0], s.value...) - continue - } - if caseInsensitiveCompare(s.key, strContentLength) { - if contentLengthSeen { - return 0, errors.New("duplicate Content-Length header") - } - contentLengthSeen = true - - if h.contentLength != -1 { - var nerr error - if h.contentLength, nerr = parseContentLength(s.value); nerr != nil { - if err == nil { - err = nerr - } - h.contentLength = -2 - } else { - h.contentLengthBytes = append(h.contentLengthBytes[:0], s.value...) - } - } - continue + case 'u': + if caseInsensitiveCompare(s.key, strUserAgent) { + h.userAgent = append(h.userAgent[:0], s.value...) + continue + } + case 'c': + if caseInsensitiveCompare(s.key, strContentType) { + h.contentType = append(h.contentType[:0], s.value...) + continue + } + if caseInsensitiveCompare(s.key, strContentLength) { + if contentLengthSeen { + h.connectionClose = true + return 0, errors.New("duplicate Content-Length header") } - if caseInsensitiveCompare(s.key, strConnection) { - if bytes.Equal(s.value, strClose) { + contentLengthSeen = true + + if h.contentLength != -1 { + var err error + h.contentLength, err = parseContentLength(s.value) + if err != nil { + h.contentLength = -2 h.connectionClose = true - } else { - h.connectionClose = false - h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue) + return 0, err } - continue + h.contentLengthBytes = append(h.contentLengthBytes[:0], s.value...) + } + continue + } + if caseInsensitiveCompare(s.key, strConnection) { + if bytes.Equal(s.value, strClose) { + h.connectionClose = true + } else { + h.connectionClose = false + h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue) } - case 't': - if caseInsensitiveCompare(s.key, strTransferEncoding) { - isIdentity := caseInsensitiveCompare(s.value, strIdentity) - isChunked := caseInsensitiveCompare(s.value, strChunked) - - if !isIdentity && !isChunked { - if h.secureErrorLogMessage { - return 0, errors.New("unsupported Transfer-Encoding") - } - return 0, fmt.Errorf("unsupported Transfer-Encoding: %q", s.value) + continue + } + case 't': + if caseInsensitiveCompare(s.key, strTransferEncoding) { + isIdentity := caseInsensitiveCompare(s.value, strIdentity) + isChunked := caseInsensitiveCompare(s.value, strChunked) + + if !isIdentity && !isChunked { + h.connectionClose = true + if h.secureErrorLogMessage { + return 0, errors.New("unsupported Transfer-Encoding") } + return 0, fmt.Errorf("unsupported Transfer-Encoding: %q", s.value) + } - if isChunked { - h.contentLength = -1 - h.h = setArgBytes(h.h, strTransferEncoding, strChunked, argsHasValue) - } - continue + if isChunked { + h.contentLength = -1 + h.h = setArgBytes(h.h, strTransferEncoding, strChunked, argsHasValue) } - if caseInsensitiveCompare(s.key, strTrailer) { - if nerr := h.SetTrailerBytes(s.value); nerr != nil { - if err == nil { - err = nerr - } - } - continue + continue + } + if caseInsensitiveCompare(s.key, strTrailer) { + err := h.SetTrailerBytes(s.value) + if err != nil { + h.connectionClose = true + return 0, err } + continue } } h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue) } - if s.err != nil && err == nil { - err = s.err - } - if err != nil { + + if s.err != nil { h.connectionClose = true - return 0, err + return 0, s.err } if h.contentLength < 0 { @@ -3187,16 +3236,15 @@ func parseContentLength(b []byte) (int, error) { } type headerScanner struct { + err error + b []byte key []byte value []byte - err error // hLen stores header subslice len hLen int - disableNormalizing bool - // by checking whether the next line contains a colon or not to tell // it's a header entry or a multi line value of current header entry. // the side effect of this operation is that we know the index of the @@ -3205,7 +3253,8 @@ type headerScanner struct { nextColon int nextNewLine int - initialized bool + disableNormalizing bool + initialized bool } func (s *headerScanner) next() bool { @@ -3253,7 +3302,7 @@ func (s *headerScanner) next() bool { s.key = s.b[:n] normalizeHeaderKey(s.key, s.disableNormalizing) n++ - for len(s.b) > n && s.b[n] == ' ' { + for len(s.b) > n && (s.b[n] == ' ' || s.b[n] == '\t') { n++ // the newline index is a relative index, and lines below trimmed `s.b` by `n`, // so the relative newline index also shifted forward. it's safe to decrease @@ -3307,13 +3356,14 @@ func (s *headerScanner) next() bool { if n > 0 && s.value[n-1] == rChar { n-- } - for n > 0 && s.value[n-1] == ' ' { + for n > 0 && (s.value[n-1] == ' ' || s.value[n-1] == '\t') { n-- } s.value = s.value[:n] if isMultiLineValue { s.value, s.b, s.hLen = normalizeHeaderValue(s.value, oldB, s.hLen) } + return true } @@ -3371,17 +3421,18 @@ func nextLine(b []byte) ([]byte, []byte, error) { return b[:n], b[nNext+1:], nil } -func initHeaderKV(kv *argsKV, key, value string, disableNormalizing bool) { - kv.key = getHeaderKeyBytes(kv, key, disableNormalizing) +func initHeaderKV(bufK, bufV []byte, key, value string, disableNormalizing bool) ([]byte, []byte) { + bufK = getHeaderKeyBytes(bufK, key, disableNormalizing) // https://tools.ietf.org/html/rfc7230#section-3.2.4 - kv.value = append(kv.value[:0], value...) - kv.value = removeNewLines(kv.value) + bufV = append(bufV[:0], value...) + bufV = removeNewLines(bufV) + return bufK, bufV } -func getHeaderKeyBytes(kv *argsKV, key string, disableNormalizing bool) []byte { - kv.key = append(kv.key[:0], key...) - normalizeHeaderKey(kv.key, disableNormalizing) - return kv.key +func getHeaderKeyBytes(bufK []byte, key string, disableNormalizing bool) []byte { + bufK = append(bufK[:0], key...) + normalizeHeaderKey(bufK, disableNormalizing) + return bufK } func normalizeHeaderValue(ov, ob []byte, headerLength int) (nv, nb []byte, nhl int) { @@ -3392,6 +3443,7 @@ func normalizeHeaderValue(ov, ob []byte, headerLength int) (nv, nb []byte, nhl i } write := 0 shrunk := 0 + once := false lineStart := false for read := 0; read < length; read++ { c := ov[read] @@ -3400,10 +3452,17 @@ func normalizeHeaderValue(ov, ob []byte, headerLength int) (nv, nb []byte, nhl i shrunk++ if c == nChar { lineStart = true + once = false } continue - case lineStart && c == '\t': - c = ' ' + case lineStart && (c == '\t' || c == ' '): + if !once { + c = ' ' + once = true + } else { + shrunk++ + continue + } default: lineStart = false } diff --git a/src/vendor/github.com/valyala/fasthttp/headers.go b/src/vendor/github.com/valyala/fasthttp/headers.go index 4f9162909..e06b7349a 100644 --- a/src/vendor/github.com/valyala/fasthttp/headers.go +++ b/src/vendor/github.com/valyala/fasthttp/headers.go @@ -136,7 +136,7 @@ const ( // WebSockets. HeaderSecWebSocketAccept = "Sec-WebSocket-Accept" - HeaderSecWebSocketExtensions = "Sec-WebSocket-Extensions" /* #nosec G101 */ + HeaderSecWebSocketExtensions = "Sec-WebSocket-Extensions" // #nosec G101 HeaderSecWebSocketKey = "Sec-WebSocket-Key" HeaderSecWebSocketProtocol = "Sec-WebSocket-Protocol" HeaderSecWebSocketVersion = "Sec-WebSocket-Version" diff --git a/src/vendor/github.com/valyala/fasthttp/http.go b/src/vendor/github.com/valyala/fasthttp/http.go index 75d8b6ca0..f8217014b 100644 --- a/src/vendor/github.com/valyala/fasthttp/http.go +++ b/src/vendor/github.com/valyala/fasthttp/http.go @@ -38,21 +38,28 @@ func SetBodySizePoolLimit(reqBodyLimit, respBodyLimit int) { type Request struct { noCopy noCopy - // Request header. - // - // Copying Header by value is forbidden. Use pointer to Header instead. - Header RequestHeader - - uri URI - postArgs Args - bodyStream io.Reader w requestBodyWriter body *bytebufferpool.ByteBuffer - bodyRaw []byte multipartForm *multipart.Form multipartFormBoundary string + + postArgs Args + + bodyRaw []byte + + uri URI + + // Request header. + // + // Copying Header by value is forbidden. Use pointer to Header instead. + Header RequestHeader + + // Request timeout. Usually set by DoDeadline or DoTimeout + // if <= 0, means not set + timeout time.Duration + secureErrorLogMessage bool // Group bool members in order to reduce Request object size. @@ -65,10 +72,6 @@ type Request struct { // Client/HostClient shouldn't use this field but should depend on the uri.scheme instead. isTLS bool - // Request timeout. Usually set by DoDeadline or DoTimeout - // if <= 0, means not set - timeout time.Duration - // Use Host header (request.Header.SetHost) instead of the host from SetRequestURI, SetHost, or URI().SetHost UseHostHeader bool @@ -88,6 +91,17 @@ type Request struct { type Response struct { noCopy noCopy + bodyStream io.Reader + + // Remote TCPAddr from concurrently net.Conn. + raddr net.Addr + // Local TCPAddr from concurrently net.Conn. + laddr net.Addr + w responseBodyWriter + body *bytebufferpool.ByteBuffer + + bodyRaw []byte + // Response header. // // Copying Header by value is forbidden. Use pointer to Header instead. @@ -101,11 +115,6 @@ type Response struct { // Use SetBodyStream to set the body stream. StreamBody bool - bodyStream io.Reader - w responseBodyWriter - body *bytebufferpool.ByteBuffer - bodyRaw []byte - // Response.Read() skips reading body if set to true. // Use it for reading HEAD responses. // @@ -115,11 +124,6 @@ type Response struct { keepBodyBuffer bool secureErrorLogMessage bool - - // Remote TCPAddr from concurrently net.Conn. - raddr net.Addr - // Local TCPAddr from concurrently net.Conn. - laddr net.Addr } // SetHost sets host for the request. @@ -1435,19 +1439,14 @@ func (resp *Response) ReadLimitBody(r *bufio.Reader, maxBodySize int) error { if !resp.mustSkipBody() { err = resp.ReadBody(r, maxBodySize) if err != nil { - if isConnectionReset(err) { - return nil - } return err } } - if resp.Header.ContentLength() == -1 && !resp.StreamBody { + // A response without a body can't have trailers. + if resp.Header.ContentLength() == -1 && !resp.StreamBody && !resp.mustSkipBody() { err = resp.Header.ReadTrailer(r) if err != nil && err != io.EOF { - if isConnectionReset(err) { - return nil - } return err } } @@ -1596,10 +1595,10 @@ func (req *Request) Write(w *bufio.Writer) error { nl := len(uri.username) + len(uri.password) + 1 nb := nl + len(strBasicSpace) tl := nb + base64.StdEncoding.EncodedLen(nl) - if tl > cap(req.Header.bufKV.value) { - req.Header.bufKV.value = make([]byte, 0, tl) + if tl > cap(req.Header.bufV) { + req.Header.bufV = make([]byte, 0, tl) } - buf := req.Header.bufKV.value[:0] + buf := req.Header.bufV[:0] buf = append(buf, uri.username...) buf = append(buf, strColon...) buf = append(buf, uri.password...) @@ -2279,12 +2278,13 @@ func readBodyWithStreaming(r *bufio.Reader, contentLength, maxBodySize int, dst readN = 8 * 1024 } - if contentLength >= 0 && maxBodySize >= contentLength { - b, err = appendBodyFixedSize(r, dst, readN) - } else { - b, err = readBodyIdentity(r, readN, dst) - } - + // A fixed-length pre-read function should be used here; otherwise, + // it may read content beyond the request body into areas outside + // the br buffer. This could affect the handling of the next request + // in the br buffer, if there is one. The original two branches can + // be handled with this single branch. by the way, + // fix issue: https://github.com/valyala/fasthttp/issues/1816 + b, err = appendBodyFixedSize(r, dst, readN) if err != nil { return b, err } diff --git a/src/vendor/github.com/valyala/fasthttp/lbclient.go b/src/vendor/github.com/valyala/fasthttp/lbclient.go index a9a40a2b3..690f4d0c0 100644 --- a/src/vendor/github.com/valyala/fasthttp/lbclient.go +++ b/src/vendor/github.com/valyala/fasthttp/lbclient.go @@ -27,10 +27,6 @@ type BalancingClient interface { type LBClient struct { noCopy noCopy - // Clients must contain non-zero clients list. - // Incoming requests are balanced among these clients. - Clients []BalancingClient - // HealthCheck is a callback called after each request. // // The request, response and the error returned by the client @@ -42,15 +38,20 @@ type LBClient struct { // By default HealthCheck returns false if err != nil. HealthCheck func(req *Request, resp *Response, err error) bool + // Clients must contain non-zero clients list. + // Incoming requests are balanced among these clients. + Clients []BalancingClient + + cs []*lbClient + // Timeout is the request timeout used when calling LBClient.Do. // // DefaultLBClientTimeout is used by default. Timeout time.Duration - cs []*lbClient + mu sync.RWMutex once sync.Once - mu sync.RWMutex } // DefaultLBClientTimeout is the default request timeout used by LBClient @@ -138,7 +139,7 @@ func (cc *LBClient) get() *lbClient { minT := atomic.LoadUint64(&minC.total) for _, c := range cs[1:] { n := c.PendingRequests() - t := atomic.LoadUint64(&c.total) /* #nosec G601 */ + t := atomic.LoadUint64(&c.total) if n < minN || (n == minN && t < minT) { minC = c minN = n diff --git a/src/vendor/github.com/valyala/fasthttp/peripconn.go b/src/vendor/github.com/valyala/fasthttp/peripconn.go index 46bddbf85..d09aaf839 100644 --- a/src/vendor/github.com/valyala/fasthttp/peripconn.go +++ b/src/vendor/github.com/valyala/fasthttp/peripconn.go @@ -9,8 +9,8 @@ import ( type perIPConnCounter struct { perIPConnPool sync.Pool perIPTLSConnPool sync.Pool - lock sync.Mutex m map[uint32]int + lock sync.Mutex } func (cc *perIPConnCounter) Register(ip uint32) int { @@ -41,29 +41,31 @@ func (cc *perIPConnCounter) Unregister(ip uint32) { type perIPConn struct { net.Conn - ip uint32 perIPConnCounter *perIPConnCounter + + ip uint32 } type perIPTLSConn struct { *tls.Conn - ip uint32 perIPConnCounter *perIPConnCounter + + ip uint32 } func acquirePerIPConn(conn net.Conn, ip uint32, counter *perIPConnCounter) net.Conn { - if tlcConn, ok := conn.(*tls.Conn); ok { + if tlsConn, ok := conn.(*tls.Conn); ok { v := counter.perIPTLSConnPool.Get() if v == nil { return &perIPTLSConn{ perIPConnCounter: counter, - Conn: tlcConn, + Conn: tlsConn, ip: ip, } } - c := v.(*perIPConn) - c.Conn = conn + c := v.(*perIPTLSConn) + c.Conn = tlsConn c.ip = ip return c } diff --git a/src/vendor/github.com/valyala/fasthttp/round2_64.go b/src/vendor/github.com/valyala/fasthttp/round2_64.go index a05df2329..05e88df27 100644 --- a/src/vendor/github.com/valyala/fasthttp/round2_64.go +++ b/src/vendor/github.com/valyala/fasthttp/round2_64.go @@ -12,12 +12,12 @@ func roundUpForSliceCap(n int) int { return n } - x := uint64(n - 1) + x := uint64(n - 1) // #nosec G115 x |= x >> 1 x |= x >> 2 x |= x >> 4 x |= x >> 8 x |= x >> 16 - return int(x + 1) + return int(x + 1) // #nosec G115 } diff --git a/src/vendor/github.com/valyala/fasthttp/server.go b/src/vendor/github.com/valyala/fasthttp/server.go index fe194a55e..8cd6fef5f 100644 --- a/src/vendor/github.com/valyala/fasthttp/server.go +++ b/src/vendor/github.com/valyala/fasthttp/server.go @@ -148,6 +148,18 @@ type ServeHandler func(c net.Conn) error type Server struct { noCopy noCopy + perIPConnCounter perIPConnCounter + + ctxPool sync.Pool + readerPool sync.Pool + writerPool sync.Pool + hijackConnPool sync.Pool + + // Logger, which is used by RequestCtx.Logger(). + // + // By default standard logger from log package is used. + Logger Logger + // Handler for processing incoming requests. // // Take into account that no `panic` recovery is done by `fasthttp` (thus any `panic` will take down the entire server). @@ -181,11 +193,43 @@ type Server struct { // like they are normal requests. ContinueHandler func(header *RequestHeader) bool + // ConnState specifies an optional callback function that is + // called when a client connection changes state. See the + // ConnState type and associated constants for details. + ConnState func(net.Conn, ConnState) + + // TLSConfig optionally provides a TLS configuration for use + // by ServeTLS, ServeTLSEmbed, ListenAndServeTLS, ListenAndServeTLSEmbed, + // AppendCert, AppendCertEmbed and NextProto. + // + // Note that this value is cloned by ServeTLS, ServeTLSEmbed, ListenAndServeTLS + // and ListenAndServeTLSEmbed, so it's not possible to modify the configuration + // with methods like tls.Config.SetSessionTicketKeys. + // To use SetSessionTicketKeys, use Server.Serve with a TLS Listener + // instead. + TLSConfig *tls.Config + + // FormValueFunc, which is used by RequestCtx.FormValue and support for customizing + // the behaviour of the RequestCtx.FormValue function. + // + // NetHttpFormValueFunc gives a FormValueFunc func implementation that is consistent with net/http. + FormValueFunc FormValueFunc + + nextProtos map[string]ServeHandler + + concurrencyCh chan struct{} + + idleConns map[net.Conn]time.Time + done chan struct{} + // Server name for sending in response headers. // // Default server name is used if left blank. Name string + // We need to know our listeners and idle connections so we can close them in Shutdown(). + ln []net.Listener + // The maximum number of concurrent connections the server may serve. // // DefaultConcurrency is used if not set. @@ -262,6 +306,21 @@ type Server struct { // Request body size is limited by DefaultMaxRequestBodySize by default. MaxRequestBodySize int + // SleepWhenConcurrencyLimitsExceeded is a duration to be slept of if + // the concurrency limit in exceeded (default [when is 0]: don't sleep + // and accept new connections immediately). + SleepWhenConcurrencyLimitsExceeded time.Duration + + idleConnsMu sync.Mutex + + mu sync.Mutex + + concurrency uint32 + open int32 + stop int32 + + rejectedRequestsCount uint32 + // Whether to disable keep-alive connections. // // The server will close all the incoming connections after sending @@ -340,11 +399,6 @@ type Server struct { // * cONTENT-lenGTH -> Content-Length DisableHeaderNamesNormalizing bool - // SleepWhenConcurrencyLimitsExceeded is a duration to be slept of if - // the concurrency limit in exceeded (default [when is 0]: don't sleep - // and accept new connections immediately). - SleepWhenConcurrencyLimitsExceeded time.Duration - // NoDefaultServerHeader, when set to true, causes the default Server header // to be excluded from the Response. // @@ -382,57 +436,6 @@ type Server struct { // and calls the handler sooner when given body is // larger than the current limit. StreamRequestBody bool - - // ConnState specifies an optional callback function that is - // called when a client connection changes state. See the - // ConnState type and associated constants for details. - ConnState func(net.Conn, ConnState) - - // Logger, which is used by RequestCtx.Logger(). - // - // By default standard logger from log package is used. - Logger Logger - - // TLSConfig optionally provides a TLS configuration for use - // by ServeTLS, ServeTLSEmbed, ListenAndServeTLS, ListenAndServeTLSEmbed, - // AppendCert, AppendCertEmbed and NextProto. - // - // Note that this value is cloned by ServeTLS, ServeTLSEmbed, ListenAndServeTLS - // and ListenAndServeTLSEmbed, so it's not possible to modify the configuration - // with methods like tls.Config.SetSessionTicketKeys. - // To use SetSessionTicketKeys, use Server.Serve with a TLS Listener - // instead. - TLSConfig *tls.Config - - // FormValueFunc, which is used by RequestCtx.FormValue and support for customizing - // the behaviour of the RequestCtx.FormValue function. - // - // NetHttpFormValueFunc gives a FormValueFunc func implementation that is consistent with net/http. - FormValueFunc FormValueFunc - - nextProtos map[string]ServeHandler - - concurrency uint32 - concurrencyCh chan struct{} - perIPConnCounter perIPConnCounter - - ctxPool sync.Pool - readerPool sync.Pool - writerPool sync.Pool - hijackConnPool sync.Pool - - // We need to know our listeners and idle connections so we can close them in Shutdown(). - ln []net.Listener - - idleConns map[net.Conn]time.Time - idleConnsMu sync.Mutex - - mu sync.Mutex - open int32 - stop int32 - done chan struct{} - - rejectedRequestsCount uint32 } // TimeoutHandler creates RequestHandler, which returns StatusRequestTimeout @@ -585,37 +588,39 @@ func CompressHandlerBrotliLevel(h RequestHandler, brotliLevel, otherLevel int) R type RequestCtx struct { noCopy noCopy - // Incoming request. - // - // Copying Request by value is forbidden. Use pointer to Request instead. - Request Request - // Outgoing response. // // Copying Response by value is forbidden. Use pointer to Response instead. Response Response - userValues userData - - connID uint64 - connRequestNum uint64 - connTime time.Time - remoteAddr net.Addr + connTime time.Time time time.Time - logger ctxLogger - s *Server - c net.Conn - fbr firstByteReader + logger ctxLogger + remoteAddr net.Addr + + c net.Conn + s *Server timeoutResponse *Response timeoutCh chan struct{} timeoutTimer *time.Timer - hijackHandler HijackHandler + hijackHandler HijackHandler + formValueFunc FormValueFunc + fbr firstByteReader + + userValues userData + + // Incoming request. + // + // Copying Request by value is forbidden. Use pointer to Request instead. + Request Request + + connID uint64 + connRequestNum uint64 hijackNoResponse bool - formValueFunc FormValueFunc } // HijackHandler must process the hijacked connection c. @@ -1592,14 +1597,14 @@ func (s *Server) NextProto(key string, nph ServeHandler) { func (s *Server) getNextProto(c net.Conn) (proto string, err error) { if tlsConn, ok := c.(connTLSer); ok { if s.ReadTimeout > 0 { - if err := c.SetReadDeadline(time.Now().Add(s.ReadTimeout)); err != nil { - panic(fmt.Sprintf("BUG: error in SetReadDeadline(%v): %v", s.ReadTimeout, err)) + if err = c.SetReadDeadline(time.Now().Add(s.ReadTimeout)); err != nil { + return } } if s.WriteTimeout > 0 { - if err := c.SetWriteDeadline(time.Now().Add(s.WriteTimeout)); err != nil { - panic(fmt.Sprintf("BUG: error in SetWriteDeadline(%v): %v", s.WriteTimeout, err)) + if err = c.SetWriteDeadline(time.Now().Add(s.WriteTimeout)); err != nil { + return } } @@ -2029,8 +2034,8 @@ func (s *Server) ServeConn(c net.Conn) error { c = pic } - n := atomic.AddUint32(&s.concurrency, 1) - if n > uint32(s.getConcurrency()) { + n := int(atomic.AddUint32(&s.concurrency, 1)) // #nosec G115 + if n > s.getConcurrency() { atomic.AddUint32(&s.concurrency, ^uint32(0)) s.writeFastError(c, StatusServiceUnavailable, "The connection cannot be served because Server.Concurrency limit exceeded") c.Close() @@ -2132,8 +2137,8 @@ func (s *Server) serveConn(c net.Conn) (err error) { // Remove read or write deadlines that might have previously been set. // The next handler is responsible for setting its own deadlines. if s.ReadTimeout > 0 || s.WriteTimeout > 0 { - if err := c.SetDeadline(zeroTime); err != nil { - panic(fmt.Sprintf("BUG: error in SetDeadline(zeroTime): %v", err)) + if err = c.SetDeadline(zeroTime); err != nil { + return } } @@ -2172,7 +2177,7 @@ func (s *Server) serveConn(c net.Conn) (err error) { // If this is a keep-alive connection set the idle timeout. if connRequestNum > 1 { if d := s.idleTimeout(); d > 0 { - if err := c.SetReadDeadline(time.Now().Add(d)); err != nil { + if err = c.SetReadDeadline(time.Now().Add(d)); err != nil { break } } @@ -2192,7 +2197,7 @@ func (s *Server) serveConn(c net.Conn) (err error) { // If reading from a keep-alive connection returns nothing it means // the connection was closed (either timeout or from the other side). if err != io.EOF { - err = ErrNothingRead{err} + err = ErrNothingRead{error: err} } } } @@ -2216,13 +2221,13 @@ func (s *Server) serveConn(c net.Conn) (err error) { s.setState(c, StateActive) if s.ReadTimeout > 0 { - if err := c.SetReadDeadline(time.Now().Add(s.ReadTimeout)); err != nil { + if err = c.SetReadDeadline(time.Now().Add(s.ReadTimeout)); err != nil { break } } else if s.IdleTimeout > 0 && connRequestNum > 1 { // If this was an idle connection and the server has an IdleTimeout but // no ReadTimeout then we should remove the ReadTimeout. - if err := c.SetReadDeadline(zeroTime); err != nil { + if err = c.SetReadDeadline(zeroTime); err != nil { break } } @@ -2256,8 +2261,8 @@ func (s *Server) serveConn(c net.Conn) (err error) { reqConf := onHdrRecv(&ctx.Request.Header) if reqConf.ReadTimeout > 0 { deadline := time.Now().Add(reqConf.ReadTimeout) - if err := c.SetReadDeadline(deadline); err != nil { - panic(fmt.Sprintf("BUG: error in SetReadDeadline(%v): %v", deadline, err)) + if err = c.SetReadDeadline(deadline); err != nil { + break } } switch { @@ -2281,8 +2286,9 @@ func (s *Server) serveConn(c net.Conn) (err error) { err = ctx.Request.readLimitBody(br, maxRequestBodySize, s.GetOnly, !s.DisablePreParseMultipartForm) } } - - if (s.ReduceMemoryUsage && br.Buffered() == 0) || err != nil { + // When StreamRequestBody is set to true, we cannot safely release br. + // For example, when using chunked encoding, it's possible that br has only read the request headers. + if (!s.StreamRequestBody && s.ReduceMemoryUsage && br.Buffered() == 0) || err != nil { releaseReader(s, br) br = nil } @@ -2353,7 +2359,7 @@ func (s *Server) serveConn(c net.Conn) (err error) { } else { err = ctx.Request.ContinueReadBody(br, maxRequestBodySize, !s.DisablePreParseMultipartForm) } - if (s.ReduceMemoryUsage && br.Buffered() == 0) || err != nil { + if (!s.StreamRequestBody && s.ReduceMemoryUsage && br.Buffered() == 0) || err != nil { releaseReader(s, br) br = nil } @@ -2396,20 +2402,20 @@ func (s *Server) serveConn(c net.Conn) (err error) { ctx.hijackNoResponse = false if writeTimeout > 0 { - if err := c.SetWriteDeadline(time.Now().Add(writeTimeout)); err != nil { - panic(fmt.Sprintf("BUG: error in SetWriteDeadline(%v): %v", writeTimeout, err)) + if err = c.SetWriteDeadline(time.Now().Add(writeTimeout)); err != nil { + break } previousWriteTimeout = writeTimeout } else if previousWriteTimeout > 0 { // We don't want a write timeout but we previously set one, remove it. - if err := c.SetWriteDeadline(zeroTime); err != nil { - panic(fmt.Sprintf("BUG: error in SetWriteDeadline(zeroTime): %v", err)) + if err = c.SetWriteDeadline(zeroTime); err != nil { + break } previousWriteTimeout = 0 } connectionClose = connectionClose || - (s.MaxRequestsPerConn > 0 && connRequestNum >= uint64(s.MaxRequestsPerConn)) || + (s.MaxRequestsPerConn > 0 && connRequestNum >= uint64(s.MaxRequestsPerConn)) || // #nosec G115 ctx.Response.Header.ConnectionClose() || (s.CloseOnShutdown && atomic.LoadInt32(&s.stop) == 1) if connectionClose { @@ -2743,7 +2749,15 @@ func (ctx *RequestCtx) Deadline() (deadline time.Time, ok bool) { // Note: Because creating a new channel for every request is just too expensive, so // RequestCtx.s.done is only closed when the server is shutting down. func (ctx *RequestCtx) Done() <-chan struct{} { - return ctx.s.done + // fix use new variables to prevent panic caused by modifying the original done chan to nil. + done := ctx.s.done + + if done == nil { + done = make(chan struct{}, 1) + done <- struct{}{} + return done + } + return done } // Err returns a non-nil error value after Done is closed, @@ -2757,7 +2771,7 @@ func (ctx *RequestCtx) Done() <-chan struct{} { // RequestCtx.s.done is only closed when the server is shutting down. func (ctx *RequestCtx) Err() error { select { - case <-ctx.s.done: + case <-ctx.Done(): return context.Canceled default: return nil @@ -2959,7 +2973,7 @@ const ( StateClosed ) -var stateName = map[ConnState]string{ +var stateName = []string{ StateNew: "new", StateActive: "active", StateIdle: "idle", diff --git a/src/vendor/github.com/valyala/fasthttp/stackless/writer.go b/src/vendor/github.com/valyala/fasthttp/stackless/writer.go index 28dbedf50..2a6841ac1 100644 --- a/src/vendor/github.com/valyala/fasthttp/stackless/writer.go +++ b/src/vendor/github.com/valyala/fasthttp/stackless/writer.go @@ -41,12 +41,13 @@ func NewWriter(dstW io.Writer, newWriter NewWriterFunc) Writer { type writer struct { dstW io.Writer zw Writer - xw xWriter err error - n int + xw xWriter + + p []byte + n int - p []byte op op } diff --git a/src/vendor/github.com/valyala/fasthttp/tcp.go b/src/vendor/github.com/valyala/fasthttp/tcp.go deleted file mode 100644 index 7e804374f..000000000 --- a/src/vendor/github.com/valyala/fasthttp/tcp.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build !windows - -package fasthttp - -import ( - "errors" - "syscall" -) - -func isConnectionReset(err error) bool { - return errors.Is(err, syscall.ECONNRESET) -} diff --git a/src/vendor/github.com/valyala/fasthttp/tcp_windows.go b/src/vendor/github.com/valyala/fasthttp/tcp_windows.go deleted file mode 100644 index d71950b94..000000000 --- a/src/vendor/github.com/valyala/fasthttp/tcp_windows.go +++ /dev/null @@ -1,10 +0,0 @@ -package fasthttp - -import ( - "errors" - "syscall" -) - -func isConnectionReset(err error) bool { - return errors.Is(err, syscall.WSAECONNRESET) -} diff --git a/src/vendor/github.com/valyala/fasthttp/tcpdialer.go b/src/vendor/github.com/valyala/fasthttp/tcpdialer.go index e5f06bd01..9b648daf6 100644 --- a/src/vendor/github.com/valyala/fasthttp/tcpdialer.go +++ b/src/vendor/github.com/valyala/fasthttp/tcpdialer.go @@ -126,19 +126,6 @@ type Resolver interface { // TCPDialer contains options to control a group of Dial calls. type TCPDialer struct { - // Concurrency controls the maximum number of concurrent Dials - // that can be performed using this object. - // Setting this to 0 means unlimited. - // - // WARNING: This can only be changed before the first Dial. - // Changes made after the first Dial will not affect anything. - Concurrency int - - // LocalAddr is the local address to use when dialing an - // address. - // If nil, a local address is automatically chosen. - LocalAddr *net.TCPAddr - // This may be used to override DNS resolving policy, like this: // var dialer = &fasthttp.TCPDialer{ // Resolver: &net.Resolver{ @@ -152,16 +139,30 @@ type TCPDialer struct { // } Resolver Resolver - // DisableDNSResolution may be used to disable DNS resolution - DisableDNSResolution bool - // DNSCacheDuration may be used to override the default DNS cache duration (DefaultDNSCacheDuration) - DNSCacheDuration time.Duration + // LocalAddr is the local address to use when dialing an + // address. + // If nil, a local address is automatically chosen. + LocalAddr *net.TCPAddr + + concurrencyCh chan struct{} tcpAddrsMap sync.Map - concurrencyCh chan struct{} + // Concurrency controls the maximum number of concurrent Dials + // that can be performed using this object. + // Setting this to 0 means unlimited. + // + // WARNING: This can only be changed before the first Dial. + // Changes made after the first Dial will not affect anything. + Concurrency int + + // DNSCacheDuration may be used to override the default DNS cache duration (DefaultDNSCacheDuration) + DNSCacheDuration time.Duration once sync.Once + + // DisableDNSResolution may be used to disable DNS resolution + DisableDNSResolution bool } // Dial dials the given TCP addr using tcp4. @@ -297,7 +298,7 @@ func (d *TCPDialer) dial(addr string, dualStack bool, timeout time.Duration) (ne return nil, err } var conn net.Conn - n := uint32(len(addrs)) + n := uint32(len(addrs)) // #nosec G115 for n > 0 { conn, err = d.tryDial(network, addrs[idx%n].String(), deadline, d.concurrencyCh) if err == nil { @@ -371,8 +372,8 @@ var ErrDialTimeout = errors.New("dialing to the given TCP address timed out") // upstream = dialErr.Upstream // 34.206.39.153:80 // } type ErrDialWithUpstream struct { - Upstream string wrapErr error + Upstream string } func (e *ErrDialWithUpstream) Error() string { @@ -395,11 +396,11 @@ func wrapDialWithUpstream(err error, upstream string) error { const DefaultDialTimeout = 3 * time.Second type tcpAddrEntry struct { - addrs []net.TCPAddr - addrsIdx uint32 - - pending int32 resolveTime time.Time + addrs []net.TCPAddr + addrsIdx uint32 + + pending int32 } // DefaultDNSCacheDuration is the duration for caching resolved TCP addresses diff --git a/src/vendor/github.com/valyala/fasthttp/uri.go b/src/vendor/github.com/valyala/fasthttp/uri.go index 19ceb694c..7ddadfcbe 100644 --- a/src/vendor/github.com/valyala/fasthttp/uri.go +++ b/src/vendor/github.com/valyala/fasthttp/uri.go @@ -42,6 +42,8 @@ var uriPool = &sync.Pool{ type URI struct { noCopy noCopy + queryArgs Args + pathOriginal []byte scheme []byte path []byte @@ -49,7 +51,11 @@ type URI struct { hash []byte host []byte - queryArgs Args + fullURI []byte + requestURI []byte + + username []byte + password []byte parsedQueryArgs bool // Path values are sent as-is without normalization. @@ -60,12 +66,6 @@ type URI struct { // By default path values are normalized, i.e. // extra slashes are removed, special characters are encoded. DisablePathNormalizing bool - - fullURI []byte - requestURI []byte - - username []byte - password []byte } // CopyTo copies uri contents to dst. diff --git a/src/vendor/github.com/valyala/fasthttp/userdata.go b/src/vendor/github.com/valyala/fasthttp/userdata.go index 38cca864f..20366b631 100644 --- a/src/vendor/github.com/valyala/fasthttp/userdata.go +++ b/src/vendor/github.com/valyala/fasthttp/userdata.go @@ -77,6 +77,8 @@ func (d *userData) Reset() { if vc, ok := v.(io.Closer); ok { vc.Close() } + (*d)[i].value = nil + (*d)[i].key = nil } *d = (*d)[:0] } @@ -92,6 +94,7 @@ func (d *userData) Remove(key any) { if kv.key == key { n-- args[i], args[n] = args[n], args[i] + args[n].key = nil args[n].value = nil args = args[:n] *d = args diff --git a/src/vendor/github.com/valyala/fasthttp/workerpool.go b/src/vendor/github.com/valyala/fasthttp/workerpool.go index 235eec10a..9ecd9481d 100644 --- a/src/vendor/github.com/valyala/fasthttp/workerpool.go +++ b/src/vendor/github.com/valyala/fasthttp/workerpool.go @@ -15,29 +15,30 @@ import ( // // Such a scheme keeps CPU caches hot (in theory). type workerPool struct { + workerChanPool sync.Pool + + Logger Logger + // Function for serving server connections. // It must leave c unclosed. WorkerFunc ServeHandler - MaxWorkersCount int - - LogAllErrors bool + stopCh chan struct{} - MaxIdleWorkerDuration time.Duration + connState func(net.Conn, ConnState) - Logger Logger + ready []*workerChan - lock sync.Mutex - workersCount int - mustStop bool + MaxWorkersCount int - ready []*workerChan + MaxIdleWorkerDuration time.Duration - stopCh chan struct{} + workersCount int - workerChanPool sync.Pool + lock sync.Mutex - connState func(net.Conn, ConnState) + LogAllErrors bool + mustStop bool } type workerChan struct { diff --git a/src/vendor/github.com/valyala/fasthttp/zstd.go b/src/vendor/github.com/valyala/fasthttp/zstd.go index 226a12632..0ee2cca1c 100644 --- a/src/vendor/github.com/valyala/fasthttp/zstd.go +++ b/src/vendor/github.com/valyala/fasthttp/zstd.go @@ -102,7 +102,7 @@ func releaseRealZstdWrter(zw *zstd.Encoder, level int) { } func AppendZstdBytesLevel(dst, src []byte, level int) []byte { - w := &byteSliceWriter{dst} + w := &byteSliceWriter{b: dst} WriteZstdLevel(w, src, level) //nolint:errcheck return w.b } @@ -155,7 +155,7 @@ func AppendZstdBytes(dst, src []byte) []byte { // WriteUnzstd writes unzstd p to w and returns the number of uncompressed // bytes written to w. func WriteUnzstd(w io.Writer, p []byte) (int, error) { - r := &byteSliceReader{p} + r := &byteSliceReader{b: p} zr, err := acquireZstdReader(r) if err != nil { return 0, err @@ -171,7 +171,7 @@ func WriteUnzstd(w io.Writer, p []byte) (int, error) { // AppendUnzstdBytes appends unzstd src to dst and returns the resulting dst. func AppendUnzstdBytes(dst, src []byte) ([]byte, error) { - w := &byteSliceWriter{dst} + w := &byteSliceWriter{b: dst} _, err := WriteUnzstd(w, src) return w.b, err } diff --git a/src/vendor/modules.txt b/src/vendor/modules.txt index deeb1cd64..e99ccbb2b 100644 --- a/src/vendor/modules.txt +++ b/src/vendor/modules.txt @@ -13,7 +13,7 @@ code.cloudfoundry.org/go-loggregator/v9 code.cloudfoundry.org/go-loggregator/v9/conversion code.cloudfoundry.org/go-loggregator/v9/rfc5424 code.cloudfoundry.org/go-loggregator/v9/rpc/loggregator_v2 -# code.cloudfoundry.org/go-metric-registry v0.0.0-20240911230103-8810864fa5d2 +# code.cloudfoundry.org/go-metric-registry v0.0.0-20240924162441-23e55bf8b6be ## explicit; go 1.22 code.cloudfoundry.org/go-metric-registry code.cloudfoundry.org/go-metric-registry/testhelpers @@ -65,7 +65,7 @@ github.com/google/go-cmp/cmp/internal/diff github.com/google/go-cmp/cmp/internal/flags github.com/google/go-cmp/cmp/internal/function github.com/google/go-cmp/cmp/internal/value -# github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 +# github.com/google/pprof v0.0.0-20240929191954-255acd752d31 ## explicit; go 1.22 github.com/google/pprof/profile # github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 @@ -76,8 +76,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2/utilities # github.com/inconshreveable/mousetrap v1.1.0 ## explicit; go 1.18 github.com/inconshreveable/mousetrap -# github.com/klauspost/compress v1.17.9 -## explicit; go 1.20 +# github.com/klauspost/compress v1.17.10 +## explicit; go 1.21 github.com/klauspost/compress github.com/klauspost/compress/flate github.com/klauspost/compress/fse @@ -162,7 +162,7 @@ github.com/square/certstrap/pkix # github.com/valyala/bytebufferpool v1.0.0 ## explicit github.com/valyala/bytebufferpool -# github.com/valyala/fasthttp v1.55.0 +# github.com/valyala/fasthttp v1.56.0 ## explicit; go 1.20 github.com/valyala/fasthttp github.com/valyala/fasthttp/fasthttputil @@ -173,7 +173,7 @@ go.opentelemetry.io/proto/otlp/collector/metrics/v1 go.opentelemetry.io/proto/otlp/common/v1 go.opentelemetry.io/proto/otlp/metrics/v1 go.opentelemetry.io/proto/otlp/resource/v1 -# go.step.sm/crypto v0.52.0 +# go.step.sm/crypto v0.53.0 ## explicit; go 1.22 go.step.sm/crypto/fingerprint go.step.sm/crypto/internal/bcrypt_pbkdf @@ -263,10 +263,10 @@ golang.org/x/tools/internal/stdlib golang.org/x/tools/internal/tokeninternal golang.org/x/tools/internal/typesinternal golang.org/x/tools/internal/versions -# google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 +# google.golang.org/genproto/googleapis/api v0.0.0-20240924160255-9d4c2d233b61 ## explicit; go 1.21 google.golang.org/genproto/googleapis/api/httpbody -# google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 +# google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 ## explicit; go 1.21 google.golang.org/genproto/googleapis/rpc/status # google.golang.org/grpc v1.67.0