mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-03-11 15:34:56 +00:00
vendor: use github.com/VictoriaMetrics/fasthttp instead of github.com/fasthttp/fasthttp
The upstream fasthttp may contain issues like 996610f021
,
plus a code that isn't used by VictoriaMetrics. So let's use a private copy under our control instead.
This commit is contained in:
parent
473188f4fd
commit
b6d88bac04
67 changed files with 1619 additions and 4579 deletions
|
@ -14,8 +14,8 @@ import (
|
|||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/netutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/persistentqueue"
|
||||
"github.com/VictoriaMetrics/fasthttp"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"net"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/VictoriaMetrics/fasthttp"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
func statDial(addr string) (net.Conn, error) {
|
||||
|
|
5
go.mod
5
go.mod
|
@ -4,6 +4,10 @@ require (
|
|||
cloud.google.com/go v0.56.0 // indirect
|
||||
cloud.google.com/go/storage v1.6.0
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7
|
||||
|
||||
// Do not use the original github.com/valyala/fasthttp because of issues
|
||||
// like https://github.com/valyala/fasthttp/commit/996610f021ff45fdc98c2ce7884d5fa4e7f9199b
|
||||
github.com/VictoriaMetrics/fasthttp v1.0.1
|
||||
github.com/VictoriaMetrics/metrics v1.11.2
|
||||
github.com/VictoriaMetrics/metricsql v0.1.0
|
||||
github.com/aws/aws-sdk-go v1.30.13
|
||||
|
@ -12,7 +16,6 @@ require (
|
|||
github.com/golang/snappy v0.0.1
|
||||
github.com/klauspost/compress v1.10.5
|
||||
github.com/lithammer/go-jump-consistent-hash v1.0.1
|
||||
github.com/valyala/fasthttp v1.12.0
|
||||
github.com/valyala/fastjson v1.5.1
|
||||
github.com/valyala/fastrand v1.0.0
|
||||
github.com/valyala/gozstd v1.7.0
|
||||
|
|
36
go.sum
36
go.sum
|
@ -8,28 +8,23 @@ cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg
|
|||
cloud.google.com/go v0.50.0 h1:0E3eE8MX426vUOs7aHfI7aN1BrIzzzf4ccKCSfSjGmc=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0 h1:MZQCQQaRwOrAcuKjiHWHrgKykt4fZyuwF2dtiG3fGW8=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.56.0 h1:WRz29PgAsVEyPSDHyk+0fpEkwEFyfhHn+JbksT6gIL4=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0 h1:sAbMqjY1PEQKZBWfbu6Y6bsupJ9c4QdHnzg/VvYTLcE=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0 h1:xE3CPsOgttP4ACBePh79zTKALtXwn/Edhcr16R5hMWU=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0 h1:9/vpR43S4aJaROxqQHQ3nH9lfyKKV0dC3vOmnw8ebQQ=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0 h1:Lpy6hKgdcl7a3WGSfJIFmxmcdjSpP6OmBEfcOv1Y680=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0 h1:RPUcBvDeYgQFMfQu1eBMq6piD1SXmLH+vK3qjewZPus=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0 h1:UDpwYIwla4jHGzZJaEJYx1tOejbgSoNqsAfHAUYe2r8=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
|
@ -39,6 +34,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
|||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw=
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8=
|
||||
github.com/VictoriaMetrics/fasthttp v1.0.1 h1:I7YdbswTIW63WxoFoUOSNxeOEGB46rdKULQhn+jt+AY=
|
||||
github.com/VictoriaMetrics/fasthttp v1.0.1/go.mod h1:BqgsieH90PR7x97c89j+eqZDloKkDhAEQTwhLw6jw/4=
|
||||
github.com/VictoriaMetrics/metrics v1.11.2 h1:t/ceLP6SvagUqypCKU7cI7+tQn54+TIV/tGoxihHvx8=
|
||||
github.com/VictoriaMetrics/metrics v1.11.2/go.mod h1:LU2j9qq7xqZYXz8tF3/RQnB2z2MbZms5TDiIg9/NHiQ=
|
||||
github.com/VictoriaMetrics/metricsql v0.1.0 h1:IoyG84PCwFY15rNuxpr2nQ+YZBYIhnd7zTiaGo5BNpc=
|
||||
|
@ -66,10 +63,8 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9
|
|||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
@ -80,11 +75,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
|||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
|
@ -99,7 +91,6 @@ 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.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
|
@ -146,7 +137,6 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
|||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
|
@ -170,7 +160,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
|
@ -188,9 +177,7 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587 h1:5Uz0rkjCFu9BC9gCRN7EkwVvh
|
|||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd h1:zkO/Lhoka23X63N9OSzpSeROEUQ5ODw47tM3YWjygbs=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
|
@ -202,9 +189,7 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl
|
|||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367 h1:0IiAsCRByjO2QjX7ZPkw5oU9x+n1YqRL802rjC0c3Aw=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
|
@ -212,7 +197,6 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU
|
|||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||
|
@ -232,12 +216,9 @@ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd h1:QPwSajcTUrFriMF1nJ3XzgoqakqQEsnZf9LdXdi2nkI=
|
||||
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
|
@ -255,7 +236,6 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -274,9 +254,7 @@ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -333,12 +311,9 @@ google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEn
|
|||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0 h1:uMf5uLi4eQMRrMKhCplNik4U4H8Z6C1br3zOtAa/aDE=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0 h1:yzlyyDW/J0w8yNFJIhiAJy4kq74S+1DOLdawELNxFMA=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0 h1:0q95w+VuFtv4PAx4PZVQdBMmYbaCHbnfKaEiDIcVyag=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0 h1:J1Pl9P2lnmYFSJvgs70DKELqHNh8CNWXPbud4njEE2s=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
|
@ -346,7 +321,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
|
|||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
|
@ -365,7 +339,6 @@ google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvx
|
|||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce h1:1mbrb1tUU+Zmt5C94IGKADBTJZjZXAd+BubWi7r9EiI=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
|
@ -376,12 +349,9 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq
|
|||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
|
@ -395,7 +365,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
|||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
@ -403,7 +372,6 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
|
|||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/netutil"
|
||||
"github.com/VictoriaMetrics/fasthttp"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -46,17 +46,17 @@ func newClient(sw *ScrapeWork) *client {
|
|||
}
|
||||
}
|
||||
hc := &fasthttp.HostClient{
|
||||
Addr: host,
|
||||
Name: "vm_promscrape",
|
||||
Dial: statDial,
|
||||
DialDualStack: netutil.TCP6Enabled(),
|
||||
IsTLS: isTLS,
|
||||
TLSConfig: tlsCfg,
|
||||
MaxIdleConnDuration: 2 * sw.ScrapeInterval,
|
||||
ReadTimeout: sw.ScrapeTimeout,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
MaxResponseBodySize: *maxScrapeSize,
|
||||
MaxIdemponentCallAttempts: 1,
|
||||
Addr: host,
|
||||
Name: "vm_promscrape",
|
||||
Dial: statDial,
|
||||
DialDualStack: netutil.TCP6Enabled(),
|
||||
IsTLS: isTLS,
|
||||
TLSConfig: tlsCfg,
|
||||
MaxIdleConnDuration: 2 * sw.ScrapeInterval,
|
||||
ReadTimeout: sw.ScrapeTimeout,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
MaxResponseBodySize: *maxScrapeSize,
|
||||
MaxIdempotentRequestAttempts: 1,
|
||||
}
|
||||
return &client{
|
||||
hc: hc,
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/netutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promauth"
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/VictoriaMetrics/fasthttp"
|
||||
)
|
||||
|
||||
// apiConfig contains config for API server
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"net"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/VictoriaMetrics/fasthttp"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
func statDial(addr string) (net.Conn, error) {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
tags
|
||||
*.pprof
|
||||
*.fasthttp.gz
|
||||
.idea
|
16
vendor/github.com/VictoriaMetrics/fasthttp/.travis.yml
generated
vendored
Normal file
16
vendor/github.com/VictoriaMetrics/fasthttp/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.9.x
|
||||
- 1.8.x
|
||||
|
||||
script:
|
||||
# build test for supported platforms
|
||||
- GOOS=linux go build
|
||||
- GOOS=darwin go build
|
||||
- GOOS=freebsd go build
|
||||
- GOOS=windows go build
|
||||
- GOARCH=386 go build
|
||||
|
||||
# run tests on a standard platform
|
||||
- go test -v ./...
|
22
vendor/github.com/VictoriaMetrics/fasthttp/LICENSE
generated
vendored
Normal file
22
vendor/github.com/VictoriaMetrics/fasthttp/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2016 Aliaksandr Valialkin, VertaMedia
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
5
vendor/github.com/VictoriaMetrics/fasthttp/README.md
generated
vendored
Normal file
5
vendor/github.com/VictoriaMetrics/fasthttp/README.md
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
Private copy of [fasthttp](https://github.com/valyala/fasthttp) for VictoriaMetrics usage.
|
||||
|
||||
It contains only the functionality required for [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics).
|
||||
|
||||
Do not use it in your own projects!
|
|
@ -4,15 +4,7 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/valyala/bytebufferpool"
|
||||
)
|
||||
|
||||
const (
|
||||
argsNoValue = true
|
||||
argsHasValue = false
|
||||
)
|
||||
|
||||
// AcquireArgs returns an empty Args object from the pool.
|
||||
|
@ -23,7 +15,7 @@ func AcquireArgs() *Args {
|
|||
return argsPool.Get().(*Args)
|
||||
}
|
||||
|
||||
// ReleaseArgs returns the object acquired via AcquireArgs to the pool.
|
||||
// ReleaseArgs returns the object acquired via AquireArgs to the pool.
|
||||
//
|
||||
// Do not access the released Args object, otherwise data races may occur.
|
||||
func ReleaseArgs(a *Args) {
|
||||
|
@ -44,16 +36,15 @@ var argsPool = &sync.Pool{
|
|||
//
|
||||
// Args instance MUST NOT be used from concurrently running goroutines.
|
||||
type Args struct {
|
||||
noCopy noCopy //nolint:unused,structcheck
|
||||
noCopy noCopy
|
||||
|
||||
args []argsKV
|
||||
buf []byte
|
||||
}
|
||||
|
||||
type argsKV struct {
|
||||
key []byte
|
||||
value []byte
|
||||
noValue bool
|
||||
key []byte
|
||||
value []byte
|
||||
}
|
||||
|
||||
// Reset clears query args.
|
||||
|
@ -116,29 +107,14 @@ func (a *Args) QueryString() []byte {
|
|||
return a.buf
|
||||
}
|
||||
|
||||
// Sort sorts Args by key and then value using 'f' as comparison function.
|
||||
//
|
||||
// For example args.Sort(bytes.Compare)
|
||||
func (a *Args) Sort(f func(x, y []byte) int) {
|
||||
sort.SliceStable(a.args, func(i, j int) bool {
|
||||
n := f(a.args[i].key, a.args[j].key)
|
||||
if n == 0 {
|
||||
return f(a.args[i].value, a.args[j].value) == -1
|
||||
}
|
||||
return n == -1
|
||||
})
|
||||
}
|
||||
|
||||
// AppendBytes appends query string to dst and returns the extended dst.
|
||||
func (a *Args) AppendBytes(dst []byte) []byte {
|
||||
for i, n := 0, len(a.args); i < n; i++ {
|
||||
kv := &a.args[i]
|
||||
dst = AppendQuotedArg(dst, kv.key)
|
||||
if !kv.noValue {
|
||||
if len(kv.value) > 0 {
|
||||
dst = append(dst, '=')
|
||||
if len(kv.value) > 0 {
|
||||
dst = AppendQuotedArg(dst, kv.value)
|
||||
}
|
||||
dst = AppendQuotedArg(dst, kv.value)
|
||||
}
|
||||
if i+1 < n {
|
||||
dst = append(dst, '&')
|
||||
|
@ -169,74 +145,48 @@ func (a *Args) DelBytes(key []byte) {
|
|||
//
|
||||
// Multiple values for the same key may be added.
|
||||
func (a *Args) Add(key, value string) {
|
||||
a.args = appendArg(a.args, key, value, argsHasValue)
|
||||
a.args = appendArg(a.args, key, value)
|
||||
}
|
||||
|
||||
// AddBytesK adds 'key=value' argument.
|
||||
//
|
||||
// Multiple values for the same key may be added.
|
||||
func (a *Args) AddBytesK(key []byte, value string) {
|
||||
a.args = appendArg(a.args, b2s(key), value, argsHasValue)
|
||||
a.args = appendArg(a.args, b2s(key), value)
|
||||
}
|
||||
|
||||
// AddBytesV adds 'key=value' argument.
|
||||
//
|
||||
// Multiple values for the same key may be added.
|
||||
func (a *Args) AddBytesV(key string, value []byte) {
|
||||
a.args = appendArg(a.args, key, b2s(value), argsHasValue)
|
||||
a.args = appendArg(a.args, key, b2s(value))
|
||||
}
|
||||
|
||||
// AddBytesKV adds 'key=value' argument.
|
||||
//
|
||||
// Multiple values for the same key may be added.
|
||||
func (a *Args) AddBytesKV(key, value []byte) {
|
||||
a.args = appendArg(a.args, b2s(key), b2s(value), argsHasValue)
|
||||
}
|
||||
|
||||
// AddNoValue adds only 'key' as argument without the '='.
|
||||
//
|
||||
// Multiple values for the same key may be added.
|
||||
func (a *Args) AddNoValue(key string) {
|
||||
a.args = appendArg(a.args, key, "", argsNoValue)
|
||||
}
|
||||
|
||||
// AddBytesKNoValue adds only 'key' as argument without the '='.
|
||||
//
|
||||
// Multiple values for the same key may be added.
|
||||
func (a *Args) AddBytesKNoValue(key []byte) {
|
||||
a.args = appendArg(a.args, b2s(key), "", argsNoValue)
|
||||
a.args = appendArg(a.args, b2s(key), b2s(value))
|
||||
}
|
||||
|
||||
// Set sets 'key=value' argument.
|
||||
func (a *Args) Set(key, value string) {
|
||||
a.args = setArg(a.args, key, value, argsHasValue)
|
||||
a.args = setArg(a.args, key, value)
|
||||
}
|
||||
|
||||
// SetBytesK sets 'key=value' argument.
|
||||
func (a *Args) SetBytesK(key []byte, value string) {
|
||||
a.args = setArg(a.args, b2s(key), value, argsHasValue)
|
||||
a.args = setArg(a.args, b2s(key), value)
|
||||
}
|
||||
|
||||
// SetBytesV sets 'key=value' argument.
|
||||
func (a *Args) SetBytesV(key string, value []byte) {
|
||||
a.args = setArg(a.args, key, b2s(value), argsHasValue)
|
||||
a.args = setArg(a.args, key, b2s(value))
|
||||
}
|
||||
|
||||
// SetBytesKV sets 'key=value' argument.
|
||||
func (a *Args) SetBytesKV(key, value []byte) {
|
||||
a.args = setArgBytes(a.args, key, value, argsHasValue)
|
||||
}
|
||||
|
||||
// SetNoValue sets only 'key' as argument without the '='.
|
||||
//
|
||||
// Only key in argumemt, like key1&key2
|
||||
func (a *Args) SetNoValue(key string) {
|
||||
a.args = setArg(a.args, key, "", argsNoValue)
|
||||
}
|
||||
|
||||
// SetBytesKNoValue sets 'key' argument.
|
||||
func (a *Args) SetBytesKNoValue(key []byte) {
|
||||
a.args = setArg(a.args, b2s(key), "", argsNoValue)
|
||||
a.args = setArgBytes(a.args, key, value)
|
||||
}
|
||||
|
||||
// Peek returns query arg value for the given key.
|
||||
|
@ -293,10 +243,10 @@ func (a *Args) GetUint(key string) (int, error) {
|
|||
|
||||
// SetUint sets uint value for the given key.
|
||||
func (a *Args) SetUint(key string, value int) {
|
||||
bb := bytebufferpool.Get()
|
||||
bb := AcquireByteBuffer()
|
||||
bb.B = AppendUint(bb.B[:0], value)
|
||||
a.SetBytesV(key, bb.B)
|
||||
bytebufferpool.Put(bb)
|
||||
ReleaseByteBuffer(bb)
|
||||
}
|
||||
|
||||
// SetUintBytes sets uint value for the given key.
|
||||
|
@ -337,14 +287,11 @@ func (a *Args) GetUfloatOrZero(key string) float64 {
|
|||
|
||||
// GetBool returns boolean value for the given key.
|
||||
//
|
||||
// true is returned for "1", "t", "T", "true", "TRUE", "True", "y", "yes", "Y", "YES", "Yes",
|
||||
// true is returned for '1', 'y' and 'yes' values,
|
||||
// otherwise false is returned.
|
||||
func (a *Args) GetBool(key string) bool {
|
||||
switch b2s(a.Peek(key)) {
|
||||
// Support the same true cases as strconv.ParseBool
|
||||
// See: https://github.com/golang/go/blob/4e1b11e2c9bdb0ddea1141eed487be1a626ff5be/src/strconv/atob.go#L12
|
||||
// and Y and Yes versions.
|
||||
case "1", "t", "T", "true", "TRUE", "True", "y", "yes", "Y", "YES", "Yes":
|
||||
switch string(a.Peek(key)) {
|
||||
case "1", "y", "yes":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
@ -370,12 +317,7 @@ func copyArgs(dst, src []argsKV) []argsKV {
|
|||
dstKV := &dst[i]
|
||||
srcKV := &src[i]
|
||||
dstKV.key = append(dstKV.key[:0], srcKV.key...)
|
||||
if srcKV.noValue {
|
||||
dstKV.value = dstKV.value[:0]
|
||||
} else {
|
||||
dstKV.value = append(dstKV.value[:0], srcKV.value...)
|
||||
}
|
||||
dstKV.noValue = srcKV.noValue
|
||||
dstKV.value = append(dstKV.value[:0], srcKV.value...)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
@ -398,41 +340,31 @@ func delAllArgs(args []argsKV, key string) []argsKV {
|
|||
return args
|
||||
}
|
||||
|
||||
func setArgBytes(h []argsKV, key, value []byte, noValue bool) []argsKV {
|
||||
return setArg(h, b2s(key), b2s(value), noValue)
|
||||
func setArgBytes(h []argsKV, key, value []byte) []argsKV {
|
||||
return setArg(h, b2s(key), b2s(value))
|
||||
}
|
||||
|
||||
func setArg(h []argsKV, key, value string, noValue bool) []argsKV {
|
||||
func setArg(h []argsKV, key, value string) []argsKV {
|
||||
n := len(h)
|
||||
for i := 0; i < n; i++ {
|
||||
kv := &h[i]
|
||||
if key == string(kv.key) {
|
||||
if noValue {
|
||||
kv.value = kv.value[:0]
|
||||
} else {
|
||||
kv.value = append(kv.value[:0], value...)
|
||||
}
|
||||
kv.noValue = noValue
|
||||
kv.value = append(kv.value[:0], value...)
|
||||
return h
|
||||
}
|
||||
}
|
||||
return appendArg(h, key, value, noValue)
|
||||
return appendArg(h, key, value)
|
||||
}
|
||||
|
||||
func appendArgBytes(h []argsKV, key, value []byte, noValue bool) []argsKV {
|
||||
return appendArg(h, b2s(key), b2s(value), noValue)
|
||||
func appendArgBytes(h []argsKV, key, value []byte) []argsKV {
|
||||
return appendArg(h, b2s(key), b2s(value))
|
||||
}
|
||||
|
||||
func appendArg(args []argsKV, key, value string, noValue bool) []argsKV {
|
||||
func appendArg(args []argsKV, key, value string) []argsKV {
|
||||
var kv *argsKV
|
||||
args, kv = allocArg(args)
|
||||
kv.key = append(kv.key[:0], key...)
|
||||
if noValue {
|
||||
kv.value = kv.value[:0]
|
||||
} else {
|
||||
kv.value = append(kv.value[:0], value...)
|
||||
}
|
||||
kv.noValue = noValue
|
||||
kv.value = append(kv.value[:0], value...)
|
||||
return args
|
||||
}
|
||||
|
||||
|
@ -488,7 +420,6 @@ func (s *argsScanner) next(kv *argsKV) bool {
|
|||
if len(s.b) == 0 {
|
||||
return false
|
||||
}
|
||||
kv.noValue = argsHasValue
|
||||
|
||||
isKey := true
|
||||
k := 0
|
||||
|
@ -504,7 +435,6 @@ func (s *argsScanner) next(kv *argsKV) bool {
|
|||
if isKey {
|
||||
kv.key = decodeArgAppend(kv.key[:0], s.b[:i])
|
||||
kv.value = kv.value[:0]
|
||||
kv.noValue = argsNoValue
|
||||
} else {
|
||||
kv.value = decodeArgAppend(kv.value[:0], s.b[k:i])
|
||||
}
|
||||
|
@ -516,7 +446,6 @@ func (s *argsScanner) next(kv *argsKV) bool {
|
|||
if isKey {
|
||||
kv.key = decodeArgAppend(kv.key[:0], s.b)
|
||||
kv.value = kv.value[:0]
|
||||
kv.noValue = argsNoValue
|
||||
} else {
|
||||
kv.value = decodeArgAppend(kv.value[:0], s.b[k:])
|
||||
}
|
||||
|
@ -557,7 +486,7 @@ func decodeArgAppend(dst, src []byte) []byte {
|
|||
// decodeArgAppendNoPlus is almost identical to decodeArgAppend, but it doesn't
|
||||
// substitute '+' with ' '.
|
||||
//
|
||||
// The function is copy-pasted from decodeArgAppend due to the performance
|
||||
// The function is copy-pasted from decodeArgAppend due to the preformance
|
||||
// reasons only.
|
||||
func decodeArgAppendNoPlus(dst, src []byte) []byte {
|
||||
if bytes.IndexByte(src, '%') < 0 {
|
64
vendor/github.com/VictoriaMetrics/fasthttp/bytebuffer.go
generated
vendored
Normal file
64
vendor/github.com/VictoriaMetrics/fasthttp/bytebuffer.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
package fasthttp
|
||||
|
||||
import (
|
||||
"github.com/valyala/bytebufferpool"
|
||||
)
|
||||
|
||||
// ByteBuffer provides byte buffer, which can be used with fasthttp API
|
||||
// in order to minimize memory allocations.
|
||||
//
|
||||
// ByteBuffer may be used with functions appending data to the given []byte
|
||||
// slice. See example code for details.
|
||||
//
|
||||
// Use AcquireByteBuffer for obtaining an empty byte buffer.
|
||||
//
|
||||
// ByteBuffer is deprecated. Use github.com/valyala/bytebufferpool instead.
|
||||
type ByteBuffer bytebufferpool.ByteBuffer
|
||||
|
||||
// Write implements io.Writer - it appends p to ByteBuffer.B
|
||||
func (b *ByteBuffer) Write(p []byte) (int, error) {
|
||||
return bb(b).Write(p)
|
||||
}
|
||||
|
||||
// WriteString appends s to ByteBuffer.B
|
||||
func (b *ByteBuffer) WriteString(s string) (int, error) {
|
||||
return bb(b).WriteString(s)
|
||||
}
|
||||
|
||||
// Set sets ByteBuffer.B to p
|
||||
func (b *ByteBuffer) Set(p []byte) {
|
||||
bb(b).Set(p)
|
||||
}
|
||||
|
||||
// SetString sets ByteBuffer.B to s
|
||||
func (b *ByteBuffer) SetString(s string) {
|
||||
bb(b).SetString(s)
|
||||
}
|
||||
|
||||
// Reset makes ByteBuffer.B empty.
|
||||
func (b *ByteBuffer) Reset() {
|
||||
bb(b).Reset()
|
||||
}
|
||||
|
||||
// AcquireByteBuffer returns an empty byte buffer from the pool.
|
||||
//
|
||||
// Acquired byte buffer may be returned to the pool via ReleaseByteBuffer call.
|
||||
// This reduces the number of memory allocations required for byte buffer
|
||||
// management.
|
||||
func AcquireByteBuffer() *ByteBuffer {
|
||||
return (*ByteBuffer)(defaultByteBufferPool.Get())
|
||||
}
|
||||
|
||||
// ReleaseByteBuffer returns byte buffer to the pool.
|
||||
//
|
||||
// ByteBuffer.B mustn't be touched after returning it to the pool.
|
||||
// Otherwise data races occur.
|
||||
func ReleaseByteBuffer(b *ByteBuffer) {
|
||||
defaultByteBufferPool.Put(bb(b))
|
||||
}
|
||||
|
||||
func bb(b *ByteBuffer) *bytebufferpool.ByteBuffer {
|
||||
return (*bytebufferpool.ByteBuffer)(b)
|
||||
}
|
||||
|
||||
var defaultByteBufferPool bytebufferpool.Pool
|
|
@ -1,5 +1,3 @@
|
|||
//go:generate go run bytesconv_table_gen.go
|
||||
|
||||
package fasthttp
|
||||
|
||||
import (
|
||||
|
@ -166,7 +164,7 @@ func ParseUint(buf []byte) (int, error) {
|
|||
var (
|
||||
errEmptyInt = errors.New("empty integer")
|
||||
errUnexpectedFirstChar = errors.New("unexpected first char found. Expecting 0-9")
|
||||
errUnexpectedTrailingChar = errors.New("unexpected trailing char found. Expecting 0-9")
|
||||
errUnexpectedTrailingChar = errors.New("unexpected traling char found. Expecting 0-9")
|
||||
errTooLongInt = errors.New("too long int")
|
||||
)
|
||||
|
||||
|
@ -185,8 +183,7 @@ func parseUintBuf(b []byte) (int, int, error) {
|
|||
}
|
||||
return v, i, nil
|
||||
}
|
||||
// Test for overflow.
|
||||
if v*10 < v {
|
||||
if i >= maxIntChars {
|
||||
return -1, i, errTooLongInt
|
||||
}
|
||||
v = 10*v + int(k)
|
||||
|
@ -273,9 +270,7 @@ func readHexInt(r *bufio.Reader) (int, error) {
|
|||
if i == 0 {
|
||||
return -1, errEmptyHexNum
|
||||
}
|
||||
if err := r.UnreadByte(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
r.UnreadByte()
|
||||
return n, nil
|
||||
}
|
||||
if i >= maxHexIntChars {
|
||||
|
@ -300,7 +295,7 @@ func writeHexInt(w *bufio.Writer, n int) error {
|
|||
buf := v.([]byte)
|
||||
i := len(buf) - 1
|
||||
for {
|
||||
buf[i] = lowerhex[n&0xf]
|
||||
buf[i] = int2hexbyte(n & 0xf)
|
||||
n >>= 4
|
||||
if n == 0 {
|
||||
break
|
||||
|
@ -312,10 +307,61 @@ func writeHexInt(w *bufio.Writer, n int) error {
|
|||
return err
|
||||
}
|
||||
|
||||
const (
|
||||
upperhex = "0123456789ABCDEF"
|
||||
lowerhex = "0123456789abcdef"
|
||||
)
|
||||
func int2hexbyte(n int) byte {
|
||||
if n < 10 {
|
||||
return '0' + byte(n)
|
||||
}
|
||||
return 'a' + byte(n) - 10
|
||||
}
|
||||
|
||||
func hexCharUpper(c byte) byte {
|
||||
if c < 10 {
|
||||
return '0' + c
|
||||
}
|
||||
return c - 10 + 'A'
|
||||
}
|
||||
|
||||
var hex2intTable = func() []byte {
|
||||
b := make([]byte, 256)
|
||||
for i := 0; i < 256; i++ {
|
||||
c := byte(16)
|
||||
if i >= '0' && i <= '9' {
|
||||
c = byte(i) - '0'
|
||||
} else if i >= 'a' && i <= 'f' {
|
||||
c = byte(i) - 'a' + 10
|
||||
} else if i >= 'A' && i <= 'F' {
|
||||
c = byte(i) - 'A' + 10
|
||||
}
|
||||
b[i] = c
|
||||
}
|
||||
return b
|
||||
}()
|
||||
|
||||
const toLower = 'a' - 'A'
|
||||
|
||||
var toLowerTable = func() [256]byte {
|
||||
var a [256]byte
|
||||
for i := 0; i < 256; i++ {
|
||||
c := byte(i)
|
||||
if c >= 'A' && c <= 'Z' {
|
||||
c += toLower
|
||||
}
|
||||
a[i] = c
|
||||
}
|
||||
return a
|
||||
}()
|
||||
|
||||
var toUpperTable = func() [256]byte {
|
||||
var a [256]byte
|
||||
for i := 0; i < 256; i++ {
|
||||
c := byte(i)
|
||||
if c >= 'a' && c <= 'z' {
|
||||
c -= toLower
|
||||
}
|
||||
a[i] = c
|
||||
}
|
||||
return a
|
||||
}()
|
||||
|
||||
func lowercaseBytes(b []byte) {
|
||||
for i := 0; i < len(b); i++ {
|
||||
|
@ -330,7 +376,6 @@ func lowercaseBytes(b []byte) {
|
|||
// Note it may break if string and/or slice header will change
|
||||
// in the future go versions.
|
||||
func b2s(b []byte) string {
|
||||
/* #nosec G103 */
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
|
||||
|
@ -338,15 +383,14 @@ func b2s(b []byte) string {
|
|||
//
|
||||
// Note it may break if string and/or slice header will change
|
||||
// in the future go versions.
|
||||
func s2b(s string) (b []byte) {
|
||||
/* #nosec G103 */
|
||||
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||
/* #nosec G103 */
|
||||
sh := *(*reflect.StringHeader)(unsafe.Pointer(&s))
|
||||
bh.Data = sh.Data
|
||||
bh.Len = sh.Len
|
||||
bh.Cap = sh.Len
|
||||
return b
|
||||
func s2b(s string) []byte {
|
||||
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
||||
bh := reflect.SliceHeader{
|
||||
Data: sh.Data,
|
||||
Len: sh.Len,
|
||||
Cap: sh.Len,
|
||||
}
|
||||
return *(*[]byte)(unsafe.Pointer(&bh))
|
||||
}
|
||||
|
||||
// AppendUnquotedArg appends url-decoded src to dst and returns appended dst.
|
||||
|
@ -359,30 +403,45 @@ func AppendUnquotedArg(dst, src []byte) []byte {
|
|||
// AppendQuotedArg appends url-encoded src to dst and returns appended dst.
|
||||
func AppendQuotedArg(dst, src []byte) []byte {
|
||||
for _, c := range src {
|
||||
switch {
|
||||
case c == ' ':
|
||||
dst = append(dst, '+')
|
||||
case quotedArgShouldEscapeTable[int(c)] != 0:
|
||||
dst = append(dst, '%', upperhex[c>>4], upperhex[c&0xf])
|
||||
default:
|
||||
// See http://www.w3.org/TR/html5/forms.html#form-submission-algorithm
|
||||
if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' ||
|
||||
c == '*' || c == '-' || c == '.' || c == '_' {
|
||||
dst = append(dst, c)
|
||||
} else {
|
||||
dst = append(dst, '%', hexCharUpper(c>>4), hexCharUpper(c&15))
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func appendQuotedPath(dst, src []byte) []byte {
|
||||
// Fix issue in https://github.com/golang/go/issues/11202
|
||||
if len(src) == 1 && src[0] == '*' {
|
||||
return append(dst, '*')
|
||||
}
|
||||
|
||||
for _, c := range src {
|
||||
if quotedPathShouldEscapeTable[int(c)] != 0 {
|
||||
dst = append(dst, '%', upperhex[c>>4], upperhex[c&15])
|
||||
} else {
|
||||
if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' ||
|
||||
c == '/' || c == '.' || c == ',' || c == '=' || c == ':' || c == '&' || c == '~' || c == '-' || c == '_' {
|
||||
dst = append(dst, c)
|
||||
} else {
|
||||
dst = append(dst, '%', hexCharUpper(c>>4), hexCharUpper(c&15))
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// EqualBytesStr returns true if string(b) == s.
|
||||
//
|
||||
// This function has no performance benefits comparing to string(b) == s.
|
||||
// It is left here for backwards compatibility only.
|
||||
//
|
||||
// This function is deperecated and may be deleted soon.
|
||||
func EqualBytesStr(b []byte, s string) bool {
|
||||
return string(b) == s
|
||||
}
|
||||
|
||||
// AppendBytesStr appends src to dst and returns the extended dst.
|
||||
//
|
||||
// This function has no performance benefits comparing to append(dst, src...).
|
||||
// It is left here for backwards compatibility only.
|
||||
//
|
||||
// This function is deprecated and may be deleted soon.
|
||||
func AppendBytesStr(dst []byte, src string) []byte {
|
||||
return append(dst, src...)
|
||||
}
|
|
@ -3,5 +3,6 @@
|
|||
package fasthttp
|
||||
|
||||
const (
|
||||
maxIntChars = 9
|
||||
maxHexIntChars = 7
|
||||
)
|
|
@ -3,5 +3,6 @@
|
|||
package fasthttp
|
||||
|
||||
const (
|
||||
maxIntChars = 18
|
||||
maxHexIntChars = 15
|
||||
)
|
File diff suppressed because it is too large
Load diff
28
vendor/github.com/VictoriaMetrics/fasthttp/coarseTime.go
generated
vendored
Normal file
28
vendor/github.com/VictoriaMetrics/fasthttp/coarseTime.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
package fasthttp
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CoarseTimeNow returns the current time truncated to the nearest second.
|
||||
//
|
||||
// This is a faster alternative to time.Now().
|
||||
func CoarseTimeNow() time.Time {
|
||||
tp := coarseTime.Load().(*time.Time)
|
||||
return *tp
|
||||
}
|
||||
|
||||
func init() {
|
||||
t := time.Now().Truncate(time.Second)
|
||||
coarseTime.Store(&t)
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
t := time.Now().Truncate(time.Second)
|
||||
coarseTime.Store(&t)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
var coarseTime atomic.Value
|
|
@ -7,11 +7,11 @@ import (
|
|||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/VictoriaMetrics/fasthttp/stackless"
|
||||
"github.com/klauspost/compress/flate"
|
||||
"github.com/klauspost/compress/gzip"
|
||||
"github.com/klauspost/compress/zlib"
|
||||
"github.com/valyala/bytebufferpool"
|
||||
"github.com/valyala/fasthttp/stackless"
|
||||
)
|
||||
|
||||
// Supported compression levels.
|
||||
|
@ -134,7 +134,7 @@ var (
|
|||
// * CompressHuffmanOnly
|
||||
func AppendGzipBytesLevel(dst, src []byte, level int) []byte {
|
||||
w := &byteSliceWriter{dst}
|
||||
WriteGzipLevel(w, src, level) //nolint:errcheck
|
||||
WriteGzipLevel(w, src, level)
|
||||
return w.b
|
||||
}
|
||||
|
||||
|
@ -152,6 +152,7 @@ func WriteGzipLevel(w io.Writer, p []byte, level int) (int, error) {
|
|||
switch w.(type) {
|
||||
case *byteSliceWriter,
|
||||
*bytes.Buffer,
|
||||
*ByteBuffer,
|
||||
*bytebufferpool.ByteBuffer:
|
||||
// These writers don't block, so we can just use stacklessWriteGzip
|
||||
ctx := &compressCtx{
|
||||
|
@ -230,7 +231,7 @@ func AppendGunzipBytes(dst, src []byte) ([]byte, error) {
|
|||
// * CompressHuffmanOnly
|
||||
func AppendDeflateBytesLevel(dst, src []byte, level int) []byte {
|
||||
w := &byteSliceWriter{dst}
|
||||
WriteDeflateLevel(w, src, level) //nolint:errcheck
|
||||
WriteDeflateLevel(w, src, level)
|
||||
return w.b
|
||||
}
|
||||
|
||||
|
@ -248,6 +249,7 @@ func WriteDeflateLevel(w io.Writer, p []byte, level int) (int, error) {
|
|||
switch w.(type) {
|
||||
case *byteSliceWriter,
|
||||
*bytes.Buffer,
|
||||
*ByteBuffer,
|
||||
*bytebufferpool.ByteBuffer:
|
||||
// These writers don't block, so we can just use stacklessWriteDeflate
|
||||
ctx := &compressCtx{
|
||||
|
@ -407,7 +409,7 @@ func isFileCompressible(f *os.File, minCompressRatio float64) bool {
|
|||
// Try compressing the first 4kb of of the file
|
||||
// and see if it can be compressed by more than
|
||||
// the given minCompressRatio.
|
||||
b := bytebufferpool.Get()
|
||||
b := AcquireByteBuffer()
|
||||
zw := acquireStacklessGzipWriter(b, CompressDefaultCompression)
|
||||
lr := &io.LimitedReader{
|
||||
R: f,
|
||||
|
@ -415,14 +417,14 @@ func isFileCompressible(f *os.File, minCompressRatio float64) bool {
|
|||
}
|
||||
_, err := copyZeroAlloc(zw, lr)
|
||||
releaseStacklessGzipWriter(zw, CompressDefaultCompression)
|
||||
f.Seek(0, 0) //nolint:errcheck
|
||||
f.Seek(0, 0)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
n := 4096 - lr.N
|
||||
zn := len(b.B)
|
||||
bytebufferpool.Put(b)
|
||||
ReleaseByteBuffer(b)
|
||||
return float64(zn) < float64(n)*minCompressRatio
|
||||
}
|
||||
|
|
@ -18,24 +18,6 @@ var (
|
|||
CookieExpireUnlimited = zeroTime
|
||||
)
|
||||
|
||||
// CookieSameSite is an enum for the mode in which the SameSite flag should be set for the given cookie.
|
||||
// See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 for details.
|
||||
type CookieSameSite int
|
||||
|
||||
const (
|
||||
// CookieSameSiteDisabled removes the SameSite flag
|
||||
CookieSameSiteDisabled CookieSameSite = iota
|
||||
// CookieSameSiteDefaultMode sets the SameSite flag
|
||||
CookieSameSiteDefaultMode
|
||||
// CookieSameSiteLaxMode sets the SameSite flag with the "Lax" parameter
|
||||
CookieSameSiteLaxMode
|
||||
// CookieSameSiteStrictMode sets the SameSite flag with the "Strict" parameter
|
||||
CookieSameSiteStrictMode
|
||||
// CookieSameSiteNoneMode sets the SameSite flag with the "None" parameter
|
||||
// see https://tools.ietf.org/html/draft-west-cookie-incrementalism-00
|
||||
CookieSameSiteNoneMode
|
||||
)
|
||||
|
||||
// AcquireCookie returns an empty Cookie object from the pool.
|
||||
//
|
||||
// The returned object may be returned back to the pool with ReleaseCookie.
|
||||
|
@ -65,18 +47,16 @@ var cookiePool = &sync.Pool{
|
|||
//
|
||||
// Cookie instance MUST NOT be used from concurrently running goroutines.
|
||||
type Cookie struct {
|
||||
noCopy noCopy //nolint:unused,structcheck
|
||||
noCopy noCopy
|
||||
|
||||
key []byte
|
||||
value []byte
|
||||
expire time.Time
|
||||
maxAge int
|
||||
domain []byte
|
||||
path []byte
|
||||
|
||||
httpOnly bool
|
||||
secure bool
|
||||
sameSite CookieSameSite
|
||||
|
||||
bufKV argsKV
|
||||
buf []byte
|
||||
|
@ -88,12 +68,10 @@ func (c *Cookie) CopyTo(src *Cookie) {
|
|||
c.key = append(c.key[:0], src.key...)
|
||||
c.value = append(c.value[:0], src.value...)
|
||||
c.expire = src.expire
|
||||
c.maxAge = src.maxAge
|
||||
c.domain = append(c.domain[:0], src.domain...)
|
||||
c.path = append(c.path[:0], src.path...)
|
||||
c.httpOnly = src.httpOnly
|
||||
c.secure = src.secure
|
||||
c.sameSite = src.sameSite
|
||||
}
|
||||
|
||||
// HTTPOnly returns true if the cookie is http only.
|
||||
|
@ -116,20 +94,6 @@ func (c *Cookie) SetSecure(secure bool) {
|
|||
c.secure = secure
|
||||
}
|
||||
|
||||
// SameSite returns the SameSite mode.
|
||||
func (c *Cookie) SameSite() CookieSameSite {
|
||||
return c.sameSite
|
||||
}
|
||||
|
||||
// SetSameSite sets the cookie's SameSite flag to the given value.
|
||||
// set value CookieSameSiteNoneMode will set Secure to true also to avoid browser rejection
|
||||
func (c *Cookie) SetSameSite(mode CookieSameSite) {
|
||||
c.sameSite = mode
|
||||
if mode == CookieSameSiteNoneMode {
|
||||
c.SetSecure(true)
|
||||
}
|
||||
}
|
||||
|
||||
// Path returns cookie path.
|
||||
func (c *Cookie) Path() []byte {
|
||||
return c.path
|
||||
|
@ -164,20 +128,6 @@ func (c *Cookie) SetDomainBytes(domain []byte) {
|
|||
c.domain = append(c.domain[:0], domain...)
|
||||
}
|
||||
|
||||
// MaxAge returns the seconds until the cookie is meant to expire or 0
|
||||
// if no max age.
|
||||
func (c *Cookie) MaxAge() int {
|
||||
return c.maxAge
|
||||
}
|
||||
|
||||
// SetMaxAge sets cookie expiration time based on seconds. This takes precedence
|
||||
// over any absolute expiry set on the cookie
|
||||
//
|
||||
// Set max age to 0 to unset
|
||||
func (c *Cookie) SetMaxAge(seconds int) {
|
||||
c.maxAge = seconds
|
||||
}
|
||||
|
||||
// Expire returns cookie expiration time.
|
||||
//
|
||||
// CookieExpireUnlimited is returned if cookie doesn't expire
|
||||
|
@ -238,12 +188,10 @@ func (c *Cookie) Reset() {
|
|||
c.key = c.key[:0]
|
||||
c.value = c.value[:0]
|
||||
c.expire = zeroTime
|
||||
c.maxAge = 0
|
||||
c.domain = c.domain[:0]
|
||||
c.path = c.path[:0]
|
||||
c.httpOnly = false
|
||||
c.secure = false
|
||||
c.sameSite = CookieSameSiteDisabled
|
||||
}
|
||||
|
||||
// AppendBytes appends cookie representation to dst and returns
|
||||
|
@ -255,12 +203,7 @@ func (c *Cookie) AppendBytes(dst []byte) []byte {
|
|||
}
|
||||
dst = append(dst, c.value...)
|
||||
|
||||
if c.maxAge > 0 {
|
||||
dst = append(dst, ';', ' ')
|
||||
dst = append(dst, strCookieMaxAge...)
|
||||
dst = append(dst, '=')
|
||||
dst = AppendUint(dst, c.maxAge)
|
||||
} else if !c.expire.IsZero() {
|
||||
if !c.expire.IsZero() {
|
||||
c.bufKV.value = AppendHTTPDate(c.bufKV.value[:0], c.expire)
|
||||
dst = append(dst, ';', ' ')
|
||||
dst = append(dst, strCookieExpires...)
|
||||
|
@ -281,26 +224,6 @@ func (c *Cookie) AppendBytes(dst []byte) []byte {
|
|||
dst = append(dst, ';', ' ')
|
||||
dst = append(dst, strCookieSecure...)
|
||||
}
|
||||
switch c.sameSite {
|
||||
case CookieSameSiteDefaultMode:
|
||||
dst = append(dst, ';', ' ')
|
||||
dst = append(dst, strCookieSameSite...)
|
||||
case CookieSameSiteLaxMode:
|
||||
dst = append(dst, ';', ' ')
|
||||
dst = append(dst, strCookieSameSite...)
|
||||
dst = append(dst, '=')
|
||||
dst = append(dst, strCookieSameSiteLax...)
|
||||
case CookieSameSiteStrictMode:
|
||||
dst = append(dst, ';', ' ')
|
||||
dst = append(dst, strCookieSameSite...)
|
||||
dst = append(dst, '=')
|
||||
dst = append(dst, strCookieSameSiteStrict...)
|
||||
case CookieSameSiteNoneMode:
|
||||
dst = append(dst, ';', ' ')
|
||||
dst = append(dst, strCookieSameSite...)
|
||||
dst = append(dst, '=')
|
||||
dst = append(dst, strCookieSameSiteNone...)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
|
@ -349,79 +272,29 @@ func (c *Cookie) ParseBytes(src []byte) error {
|
|||
c.value = append(c.value[:0], kv.value...)
|
||||
|
||||
for s.next(kv) {
|
||||
if len(kv.key) != 0 {
|
||||
// Case insensitive switch on first char
|
||||
switch kv.key[0] | 0x20 {
|
||||
case 'm':
|
||||
if caseInsensitiveCompare(strCookieMaxAge, kv.key) {
|
||||
maxAge, err := ParseUint(kv.value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.maxAge = maxAge
|
||||
}
|
||||
|
||||
case 'e': // "expires"
|
||||
if caseInsensitiveCompare(strCookieExpires, kv.key) {
|
||||
v := b2s(kv.value)
|
||||
// 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)
|
||||
if err != nil {
|
||||
exptime, err = time.Parse("Mon, 02-Jan-2006 15:04:05 MST", v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.expire = exptime
|
||||
}
|
||||
|
||||
case 'd': // "domain"
|
||||
if caseInsensitiveCompare(strCookieDomain, kv.key) {
|
||||
c.domain = append(c.domain[:0], kv.value...)
|
||||
}
|
||||
|
||||
case 'p': // "path"
|
||||
if caseInsensitiveCompare(strCookiePath, kv.key) {
|
||||
c.path = append(c.path[:0], kv.value...)
|
||||
}
|
||||
|
||||
case 's': // "samesite"
|
||||
if caseInsensitiveCompare(strCookieSameSite, kv.key) {
|
||||
// Case insensitive switch on first char
|
||||
switch kv.value[0] | 0x20 {
|
||||
case 'l': // "lax"
|
||||
if caseInsensitiveCompare(strCookieSameSiteLax, kv.value) {
|
||||
c.sameSite = CookieSameSiteLaxMode
|
||||
}
|
||||
case 's': // "strict"
|
||||
if caseInsensitiveCompare(strCookieSameSiteStrict, kv.value) {
|
||||
c.sameSite = CookieSameSiteStrictMode
|
||||
}
|
||||
case 'n': // "none"
|
||||
if caseInsensitiveCompare(strCookieSameSiteNone, kv.value) {
|
||||
c.sameSite = CookieSameSiteNoneMode
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(kv.key) == 0 && len(kv.value) == 0 {
|
||||
continue
|
||||
}
|
||||
switch string(kv.key) {
|
||||
case "expires":
|
||||
v := b2s(kv.value)
|
||||
exptime, err := time.ParseInLocation(time.RFC1123, v, time.UTC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else if len(kv.value) != 0 {
|
||||
// Case insensitive switch on first char
|
||||
switch kv.value[0] | 0x20 {
|
||||
case 'h': // "httponly"
|
||||
if caseInsensitiveCompare(strCookieHTTPOnly, kv.value) {
|
||||
c.httpOnly = true
|
||||
}
|
||||
|
||||
case 's': // "secure"
|
||||
if caseInsensitiveCompare(strCookieSecure, kv.value) {
|
||||
c.secure = true
|
||||
} else if caseInsensitiveCompare(strCookieSameSite, kv.value) {
|
||||
c.sameSite = CookieSameSiteDefaultMode
|
||||
}
|
||||
c.expire = exptime
|
||||
case "domain":
|
||||
c.domain = append(c.domain[:0], kv.value...)
|
||||
case "path":
|
||||
c.path = append(c.path[:0], kv.value...)
|
||||
case "":
|
||||
switch string(kv.value) {
|
||||
case "HttpOnly":
|
||||
c.httpOnly = true
|
||||
case "secure":
|
||||
c.secure = true
|
||||
}
|
||||
} // else empty or no match
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -456,19 +329,6 @@ func appendRequestCookieBytes(dst []byte, cookies []argsKV) []byte {
|
|||
return dst
|
||||
}
|
||||
|
||||
// For Response we can not use the above function as response cookies
|
||||
// already contain the key= in the value.
|
||||
func appendResponseCookieBytes(dst []byte, cookies []argsKV) []byte {
|
||||
for i, n := 0, len(cookies); i < n; i++ {
|
||||
kv := &cookies[i]
|
||||
dst = append(dst, kv.value...)
|
||||
if i+1 < n {
|
||||
dst = append(dst, ';', ' ')
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func parseRequestCookies(cookies []argsKV, src []byte) []argsKV {
|
||||
var s cookieScanner
|
||||
s.b = src
|
||||
|
@ -534,17 +394,3 @@ func decodeCookieArg(dst, src []byte, skipQuotes bool) []byte {
|
|||
}
|
||||
return append(dst[:0], src...)
|
||||
}
|
||||
|
||||
// caseInsensitiveCompare does a case insensitive equality comparison of
|
||||
// two []byte. Assumes only letters need to be matched.
|
||||
func caseInsensitiveCompare(a, b []byte) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i]|0x20 != b[i]|0x20 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
|
@ -7,6 +7,9 @@ Fasthttp provides the following features:
|
|||
concurrent keep-alive connections on modern hardware.
|
||||
* Optimized for low memory usage.
|
||||
* Easy 'Connection: Upgrade' support via RequestCtx.Hijack.
|
||||
* Server supports requests' pipelining. Multiple requests may be read from
|
||||
a single network packet and multiple responses may be sent in a single
|
||||
network packet. This may be useful for highly loaded REST services.
|
||||
* Server provides the following anti-DoS limits:
|
||||
|
||||
* The number of concurrent connections.
|
|
@ -1,33 +1,25 @@
|
|||
package fasthttputil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ErrInmemoryListenerClosed indicates that the InmemoryListener is already closed.
|
||||
var ErrInmemoryListenerClosed = errors.New("InmemoryListener is already closed: use of closed network connection")
|
||||
|
||||
// InmemoryListener provides in-memory dialer<->net.Listener implementation.
|
||||
//
|
||||
// It may be used either for fast in-process client<->server communications
|
||||
// It may be used either for fast in-process client<->server communcations
|
||||
// without network stack overhead or for client<->server tests.
|
||||
type InmemoryListener struct {
|
||||
lock sync.Mutex
|
||||
closed bool
|
||||
conns chan acceptConn
|
||||
}
|
||||
|
||||
type acceptConn struct {
|
||||
conn net.Conn
|
||||
accepted chan struct{}
|
||||
conns chan net.Conn
|
||||
}
|
||||
|
||||
// NewInmemoryListener returns new in-memory dialer<->net.Listener.
|
||||
func NewInmemoryListener() *InmemoryListener {
|
||||
return &InmemoryListener{
|
||||
conns: make(chan acceptConn, 1024),
|
||||
conns: make(chan net.Conn, 1024),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,10 +31,9 @@ func NewInmemoryListener() *InmemoryListener {
|
|||
func (ln *InmemoryListener) Accept() (net.Conn, error) {
|
||||
c, ok := <-ln.conns
|
||||
if !ok {
|
||||
return nil, ErrInmemoryListenerClosed
|
||||
return nil, fmt.Errorf("InmemoryListener is already closed: use of closed network connection")
|
||||
}
|
||||
close(c.accepted)
|
||||
return c.conn, nil
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Close implements net.Listener's Close.
|
||||
|
@ -54,7 +45,7 @@ func (ln *InmemoryListener) Close() error {
|
|||
close(ln.conns)
|
||||
ln.closed = true
|
||||
} else {
|
||||
err = ErrInmemoryListenerClosed
|
||||
err = fmt.Errorf("InmemoryListener is already closed")
|
||||
}
|
||||
ln.lock.Unlock()
|
||||
return err
|
||||
|
@ -68,9 +59,8 @@ func (ln *InmemoryListener) Addr() net.Addr {
|
|||
}
|
||||
}
|
||||
|
||||
// Dial creates new client<->server connection.
|
||||
// Just like a real Dial it only returns once the server
|
||||
// has accepted the connection.
|
||||
// Dial creates new client<->server connection, enqueues server side
|
||||
// of the connection to Accept and returns client side of the connection.
|
||||
//
|
||||
// It is safe calling Dial from concurrently running goroutines.
|
||||
func (ln *InmemoryListener) Dial() (net.Conn, error) {
|
||||
|
@ -78,20 +68,17 @@ func (ln *InmemoryListener) Dial() (net.Conn, error) {
|
|||
cConn := pc.Conn1()
|
||||
sConn := pc.Conn2()
|
||||
ln.lock.Lock()
|
||||
accepted := make(chan struct{})
|
||||
if !ln.closed {
|
||||
ln.conns <- acceptConn{sConn, accepted}
|
||||
// Wait until the connection has been accepted.
|
||||
<-accepted
|
||||
ln.conns <- sConn
|
||||
} else {
|
||||
sConn.Close() //nolint:errcheck
|
||||
cConn.Close() //nolint:errcheck
|
||||
sConn.Close()
|
||||
cConn.Close()
|
||||
cConn = nil
|
||||
}
|
||||
ln.lock.Unlock()
|
||||
|
||||
if cConn == nil {
|
||||
return nil, ErrInmemoryListenerClosed
|
||||
return nil, fmt.Errorf("InmemoryListener is already closed")
|
||||
}
|
||||
return cConn, nil
|
||||
}
|
|
@ -8,9 +8,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// NewPipeConns returns new bi-directional connection pipe.
|
||||
//
|
||||
// PipeConns is NOT safe for concurrent use by multiple goroutines!
|
||||
// NewPipeConns returns new bi-directonal connection pipe.
|
||||
func NewPipeConns() *PipeConns {
|
||||
ch1 := make(chan *byteBuffer, 4)
|
||||
ch2 := make(chan *byteBuffer, 4)
|
||||
|
@ -40,7 +38,6 @@ func NewPipeConns() *PipeConns {
|
|||
// calling Read in order to unblock each Write call.
|
||||
// * It supports read and write deadlines.
|
||||
//
|
||||
// PipeConns is NOT safe for concurrent use by multiple goroutines!
|
||||
type PipeConns struct {
|
||||
c1 pipeConn
|
||||
c2 pipeConn
|
||||
|
@ -90,8 +87,6 @@ type pipeConn struct {
|
|||
|
||||
readDeadlineCh <-chan time.Time
|
||||
writeDeadlineCh <-chan time.Time
|
||||
|
||||
readDeadlineChLock sync.Mutex
|
||||
}
|
||||
|
||||
func (c *pipeConn) Write(p []byte) (int, error) {
|
||||
|
@ -163,15 +158,10 @@ func (c *pipeConn) readNextByteBuffer(mayBlock bool) error {
|
|||
if !mayBlock {
|
||||
return errWouldBlock
|
||||
}
|
||||
c.readDeadlineChLock.Lock()
|
||||
readDeadlineCh := c.readDeadlineCh
|
||||
c.readDeadlineChLock.Unlock()
|
||||
select {
|
||||
case c.b = <-c.rCh:
|
||||
case <-readDeadlineCh:
|
||||
c.readDeadlineChLock.Lock()
|
||||
case <-c.readDeadlineCh:
|
||||
c.readDeadlineCh = closedDeadlineCh
|
||||
c.readDeadlineChLock.Unlock()
|
||||
// rCh may contain data when deadline is reached.
|
||||
// Read the data before returning ErrTimeout.
|
||||
select {
|
||||
|
@ -197,26 +187,9 @@ func (c *pipeConn) readNextByteBuffer(mayBlock bool) error {
|
|||
var (
|
||||
errWouldBlock = errors.New("would block")
|
||||
errConnectionClosed = errors.New("connection closed")
|
||||
)
|
||||
|
||||
type timeoutError struct {
|
||||
}
|
||||
|
||||
func (e *timeoutError) Error() string {
|
||||
return "timeout"
|
||||
}
|
||||
|
||||
// Only implement the Timeout() function of the net.Error interface.
|
||||
// This allows for checks like:
|
||||
//
|
||||
// if x, ok := err.(interface{ Timeout() bool }); ok && x.Timeout() {
|
||||
func (e *timeoutError) Timeout() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrTimeout is returned from Read() or Write() on timeout.
|
||||
ErrTimeout = &timeoutError{}
|
||||
ErrTimeout = errors.New("timeout")
|
||||
)
|
||||
|
||||
func (c *pipeConn) Close() error {
|
||||
|
@ -232,8 +205,8 @@ func (c *pipeConn) RemoteAddr() net.Addr {
|
|||
}
|
||||
|
||||
func (c *pipeConn) SetDeadline(deadline time.Time) error {
|
||||
c.SetReadDeadline(deadline) //nolint:errcheck
|
||||
c.SetWriteDeadline(deadline) //nolint:errcheck
|
||||
c.SetReadDeadline(deadline)
|
||||
c.SetWriteDeadline(deadline)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -241,10 +214,7 @@ func (c *pipeConn) SetReadDeadline(deadline time.Time) error {
|
|||
if c.readDeadlineTimer == nil {
|
||||
c.readDeadlineTimer = time.NewTimer(time.Hour)
|
||||
}
|
||||
readDeadlineCh := updateTimer(c.readDeadlineTimer, deadline)
|
||||
c.readDeadlineChLock.Lock()
|
||||
c.readDeadlineCh = readDeadlineCh
|
||||
c.readDeadlineChLock.Unlock()
|
||||
c.readDeadlineCh = updateTimer(c.readDeadlineTimer, deadline)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -17,7 +17,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/klauspost/compress/gzip"
|
||||
"github.com/valyala/bytebufferpool"
|
||||
)
|
||||
|
||||
// ServeFileBytesUncompressed returns HTTP response containing file contents
|
||||
|
@ -140,12 +139,12 @@ func NewVHostPathRewriter(slashesCount int) PathRewriteFunc {
|
|||
if len(host) == 0 {
|
||||
host = strInvalidHost
|
||||
}
|
||||
b := bytebufferpool.Get()
|
||||
b := AcquireByteBuffer()
|
||||
b.B = append(b.B, '/')
|
||||
b.B = append(b.B, host...)
|
||||
b.B = append(b.B, path...)
|
||||
ctx.URI().SetPathBytes(b.B)
|
||||
bytebufferpool.Put(b)
|
||||
ReleaseByteBuffer(b)
|
||||
|
||||
return ctx.Path()
|
||||
}
|
||||
|
@ -194,7 +193,7 @@ func NewPathPrefixStripper(prefixSize int) PathRewriteFunc {
|
|||
//
|
||||
// It is prohibited copying FS values. Create new values instead.
|
||||
type FS struct {
|
||||
noCopy noCopy //nolint:unused,structcheck
|
||||
noCopy noCopy
|
||||
|
||||
// Path to the root directory to serve files from.
|
||||
Root string
|
||||
|
@ -226,7 +225,7 @@ type FS struct {
|
|||
// It adds CompressedFileSuffix suffix to the original file name and
|
||||
// tries saving the resulting compressed file under the new file name.
|
||||
// So it is advisable to give the server write access to Root
|
||||
// and to all inner folders in order to minimize CPU usage when serving
|
||||
// and to all inner folders in order to minimze CPU usage when serving
|
||||
// compressed responses.
|
||||
//
|
||||
// Transparent compression is disabled by default.
|
||||
|
@ -242,14 +241,6 @@ type FS struct {
|
|||
// 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
|
||||
|
||||
// Expiration duration for inactive file handlers.
|
||||
//
|
||||
// FSHandlerCacheDuration is used by default.
|
||||
|
@ -352,7 +343,6 @@ func (fs *FS) initRequestHandler() {
|
|||
pathRewrite: fs.PathRewrite,
|
||||
generateIndexPages: fs.GenerateIndexPages,
|
||||
compress: fs.Compress,
|
||||
pathNotFound: fs.PathNotFound,
|
||||
acceptByteRange: fs.AcceptByteRange,
|
||||
cacheDuration: cacheDuration,
|
||||
compressedFileSuffix: compressedFileSuffix,
|
||||
|
@ -375,7 +365,6 @@ type fsHandler struct {
|
|||
root string
|
||||
indexNames []string
|
||||
pathRewrite PathRewriteFunc
|
||||
pathNotFound RequestHandler
|
||||
generateIndexPages bool
|
||||
compress bool
|
||||
acceptByteRange bool
|
||||
|
@ -737,12 +726,7 @@ func (h *fsHandler) handleRequest(ctx *RequestCtx) {
|
|||
}
|
||||
} else if err != nil {
|
||||
ctx.Logger().Printf("cannot open file %q: %s", filePath, err)
|
||||
if h.pathNotFound == nil {
|
||||
ctx.Error("Cannot open requested path", StatusNotFound)
|
||||
} else {
|
||||
ctx.SetStatusCode(StatusNotFound)
|
||||
h.pathNotFound(ctx)
|
||||
}
|
||||
ctx.Error("Cannot open requested path", StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -824,10 +808,7 @@ func (h *fsHandler) handleRequest(ctx *RequestCtx) {
|
|||
}
|
||||
}
|
||||
}
|
||||
hdr.noDefaultContentType = true
|
||||
if len(hdr.ContentType()) == 0 {
|
||||
ctx.SetContentType(ff.contentType)
|
||||
}
|
||||
ctx.SetContentType(ff.contentType)
|
||||
ctx.SetStatusCode(statusCode)
|
||||
}
|
||||
|
||||
|
@ -916,7 +897,7 @@ var (
|
|||
)
|
||||
|
||||
func (h *fsHandler) createDirIndex(base *URI, dirPath string, mustCompress bool) (*fsFile, error) {
|
||||
w := &bytebufferpool.ByteBuffer{}
|
||||
w := &ByteBuffer{}
|
||||
|
||||
basePathEscaped := html.EscapeString(string(base.Path()))
|
||||
fmt.Fprintf(w, "<html><head><title>%s</title><style>.dir { font-weight: bold }</style></head><body>", basePathEscaped)
|
||||
|
@ -943,7 +924,7 @@ func (h *fsHandler) createDirIndex(base *URI, dirPath string, mustCompress bool)
|
|||
}
|
||||
|
||||
fm := make(map[string]os.FileInfo, len(fileinfos))
|
||||
filenames := make([]string, 0, len(fileinfos))
|
||||
var filenames []string
|
||||
for _, fi := range fileinfos {
|
||||
name := fi.Name()
|
||||
if strings.HasSuffix(name, h.compressedFileSuffix) {
|
||||
|
@ -958,7 +939,7 @@ func (h *fsHandler) createDirIndex(base *URI, dirPath string, mustCompress bool)
|
|||
base.CopyTo(&u)
|
||||
u.Update(string(u.Path()) + "/")
|
||||
|
||||
sort.Strings(filenames)
|
||||
sort.Sort(sort.StringSlice(filenames))
|
||||
for _, name := range filenames {
|
||||
u.Update(name)
|
||||
pathEscaped := html.EscapeString(string(u.Path()))
|
||||
|
@ -976,7 +957,7 @@ func (h *fsHandler) createDirIndex(base *URI, dirPath string, mustCompress bool)
|
|||
fmt.Fprintf(w, "</ul></body></html>")
|
||||
|
||||
if mustCompress {
|
||||
var zbuf bytebufferpool.ByteBuffer
|
||||
var zbuf ByteBuffer
|
||||
zbuf.B = AppendGzipBytesLevel(zbuf.B, w.B, CompressDefaultCompression)
|
||||
w = &zbuf
|
||||
}
|
||||
|
@ -1193,9 +1174,7 @@ func readFileHeader(f *os.File, compressed bool) ([]byte, error) {
|
|||
N: 512,
|
||||
}
|
||||
data, err := ioutil.ReadAll(lr)
|
||||
if _, err := f.Seek(0, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Seek(0, 0)
|
||||
|
||||
if zr != nil {
|
||||
releaseGzipReader(zr)
|
9
vendor/github.com/VictoriaMetrics/fasthttp/go.mod
generated
vendored
Normal file
9
vendor/github.com/VictoriaMetrics/fasthttp/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
module github.com/VictoriaMetrics/fasthttp
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.10.5
|
||||
github.com/valyala/bytebufferpool v1.0.0
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a
|
||||
)
|
6
vendor/github.com/VictoriaMetrics/fasthttp/go.sum
generated
vendored
Normal file
6
vendor/github.com/VictoriaMetrics/fasthttp/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
github.com/klauspost/compress v1.10.5 h1:7q6vHIqubShURwQz8cQK6yIe/xC3IF0Vm7TGfqjewrc=
|
||||
github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
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/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
File diff suppressed because it is too large
Load diff
|
@ -3,15 +3,12 @@ package fasthttp
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/valyala/bytebufferpool"
|
||||
)
|
||||
|
@ -23,7 +20,7 @@ import (
|
|||
//
|
||||
// Request instance MUST NOT be used from concurrently running goroutines.
|
||||
type Request struct {
|
||||
noCopy noCopy //nolint:unused,structcheck
|
||||
noCopy noCopy
|
||||
|
||||
// Request header
|
||||
//
|
||||
|
@ -47,13 +44,6 @@ type Request struct {
|
|||
keepBodyBuffer bool
|
||||
|
||||
isTLS bool
|
||||
|
||||
// To detect scheme changes in redirects
|
||||
schemaUpdate bool
|
||||
|
||||
// Request timeout. Usually set by DoDealine or DoTimeout
|
||||
// if <= 0, means not set
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// Response represents HTTP response.
|
||||
|
@ -63,21 +53,16 @@ type Request struct {
|
|||
//
|
||||
// Response instance MUST NOT be used from concurrently running goroutines.
|
||||
type Response struct {
|
||||
noCopy noCopy //nolint:unused,structcheck
|
||||
noCopy noCopy
|
||||
|
||||
// Response header
|
||||
//
|
||||
// Copying Header by value is forbidden. Use pointer to Header instead.
|
||||
Header ResponseHeader
|
||||
|
||||
// Flush headers as soon as possible without waiting for first body bytes.
|
||||
// Relevant for bodyStream only.
|
||||
ImmediateHeaderFlush 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.
|
||||
|
@ -87,11 +72,6 @@ type Response struct {
|
|||
SkipBody bool
|
||||
|
||||
keepBodyBuffer 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.
|
||||
|
@ -298,23 +278,6 @@ func (w *requestBodyWriter) Write(p []byte) (int, error) {
|
|||
return len(p), nil
|
||||
}
|
||||
|
||||
func (resp *Response) parseNetConn(conn net.Conn) {
|
||||
resp.raddr = conn.RemoteAddr()
|
||||
resp.laddr = conn.LocalAddr()
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote network address. The Addr returned is shared
|
||||
// by all invocations of RemoteAddr, so do not modify it.
|
||||
func (resp *Response) RemoteAddr() net.Addr {
|
||||
return resp.raddr
|
||||
}
|
||||
|
||||
// LocalAddr returns the local network address. The Addr returned is shared
|
||||
// by all invocations of LocalAddr, so do not modify it.
|
||||
func (resp *Response) LocalAddr() net.Addr {
|
||||
return resp.laddr
|
||||
}
|
||||
|
||||
// Body returns response body.
|
||||
//
|
||||
// The returned body is valid until the response modification.
|
||||
|
@ -323,7 +286,7 @@ func (resp *Response) Body() []byte {
|
|||
bodyBuf := resp.bodyBuffer()
|
||||
bodyBuf.Reset()
|
||||
_, err := copyZeroAlloc(bodyBuf, resp.bodyStream)
|
||||
resp.closeBodyStream() //nolint:errcheck
|
||||
resp.closeBodyStream()
|
||||
if err != nil {
|
||||
bodyBuf.SetString(err.Error())
|
||||
}
|
||||
|
@ -332,9 +295,6 @@ func (resp *Response) Body() []byte {
|
|||
}
|
||||
|
||||
func (resp *Response) bodyBytes() []byte {
|
||||
if resp.bodyRaw != nil {
|
||||
return resp.bodyRaw
|
||||
}
|
||||
if resp.body == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -352,7 +312,6 @@ func (resp *Response) bodyBuffer() *bytebufferpool.ByteBuffer {
|
|||
if resp.body == nil {
|
||||
resp.body = responseBodyPool.Get()
|
||||
}
|
||||
resp.bodyRaw = nil
|
||||
return resp.body
|
||||
}
|
||||
|
||||
|
@ -387,7 +346,7 @@ func (resp *Response) BodyGunzip() ([]byte, error) {
|
|||
}
|
||||
|
||||
func gunzipData(p []byte) ([]byte, error) {
|
||||
var bb bytebufferpool.ByteBuffer
|
||||
var bb ByteBuffer
|
||||
_, err := WriteGunzip(&bb, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -414,7 +373,7 @@ func (resp *Response) BodyInflate() ([]byte, error) {
|
|||
}
|
||||
|
||||
func inflateData(p []byte) ([]byte, error) {
|
||||
var bb bytebufferpool.ByteBuffer
|
||||
var bb ByteBuffer
|
||||
_, err := WriteInflate(&bb, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -426,7 +385,7 @@ func inflateData(p []byte) ([]byte, error) {
|
|||
func (req *Request) BodyWriteTo(w io.Writer) error {
|
||||
if req.bodyStream != nil {
|
||||
_, err := copyZeroAlloc(w, req.bodyStream)
|
||||
req.closeBodyStream() //nolint:errcheck
|
||||
req.closeBodyStream()
|
||||
return err
|
||||
}
|
||||
if req.onlyMultipartForm() {
|
||||
|
@ -440,7 +399,7 @@ func (req *Request) BodyWriteTo(w io.Writer) error {
|
|||
func (resp *Response) BodyWriteTo(w io.Writer) error {
|
||||
if resp.bodyStream != nil {
|
||||
_, err := copyZeroAlloc(w, resp.bodyStream)
|
||||
resp.closeBodyStream() //nolint:errcheck
|
||||
resp.closeBodyStream()
|
||||
return err
|
||||
}
|
||||
_, err := w.Write(resp.bodyBytes())
|
||||
|
@ -456,8 +415,8 @@ func (resp *Response) AppendBody(p []byte) {
|
|||
|
||||
// AppendBodyString appends s to response body.
|
||||
func (resp *Response) AppendBodyString(s string) {
|
||||
resp.closeBodyStream() //nolint:errcheck
|
||||
resp.bodyBuffer().WriteString(s) //nolint:errcheck
|
||||
resp.closeBodyStream()
|
||||
resp.bodyBuffer().WriteString(s)
|
||||
}
|
||||
|
||||
// SetBody sets response body.
|
||||
|
@ -469,16 +428,15 @@ func (resp *Response) SetBody(body []byte) {
|
|||
|
||||
// SetBodyString sets response body.
|
||||
func (resp *Response) SetBodyString(body string) {
|
||||
resp.closeBodyStream() //nolint:errcheck
|
||||
resp.closeBodyStream()
|
||||
bodyBuf := resp.bodyBuffer()
|
||||
bodyBuf.Reset()
|
||||
bodyBuf.WriteString(body) //nolint:errcheck
|
||||
bodyBuf.WriteString(body)
|
||||
}
|
||||
|
||||
// ResetBody resets response body.
|
||||
func (resp *Response) ResetBody() {
|
||||
resp.bodyRaw = nil
|
||||
resp.closeBodyStream() //nolint:errcheck
|
||||
resp.closeBodyStream()
|
||||
if resp.body != nil {
|
||||
if resp.keepBodyBuffer {
|
||||
resp.body.Reset()
|
||||
|
@ -489,14 +447,6 @@ func (resp *Response) ResetBody() {
|
|||
}
|
||||
}
|
||||
|
||||
// SetBodyRaw sets response body, but without copying it.
|
||||
//
|
||||
// From this point onward the body argument must not be changed.
|
||||
func (resp *Response) SetBodyRaw(body []byte) {
|
||||
resp.ResetBody()
|
||||
resp.bodyRaw = body
|
||||
}
|
||||
|
||||
// ReleaseBody retires the response body if it is greater than "size" bytes.
|
||||
//
|
||||
// This permits GC to reclaim the large buffer. If used, must be before
|
||||
|
@ -505,9 +455,8 @@ func (resp *Response) SetBodyRaw(body []byte) {
|
|||
// Use this method only if you really understand how it works.
|
||||
// The majority of workloads don't need this method.
|
||||
func (resp *Response) ReleaseBody(size int) {
|
||||
resp.bodyRaw = nil
|
||||
if cap(resp.body.B) > size {
|
||||
resp.closeBodyStream() //nolint:errcheck
|
||||
resp.closeBodyStream()
|
||||
resp.body = nil
|
||||
}
|
||||
}
|
||||
|
@ -521,7 +470,7 @@ func (resp *Response) ReleaseBody(size int) {
|
|||
// The majority of workloads don't need this method.
|
||||
func (req *Request) ReleaseBody(size int) {
|
||||
if cap(req.body.B) > size {
|
||||
req.closeBodyStream() //nolint:errcheck
|
||||
req.closeBodyStream()
|
||||
req.body = nil
|
||||
}
|
||||
}
|
||||
|
@ -537,15 +486,13 @@ func (resp *Response) SwapBody(body []byte) []byte {
|
|||
if resp.bodyStream != nil {
|
||||
bb.Reset()
|
||||
_, err := copyZeroAlloc(bb, resp.bodyStream)
|
||||
resp.closeBodyStream() //nolint:errcheck
|
||||
resp.closeBodyStream()
|
||||
if err != nil {
|
||||
bb.Reset()
|
||||
bb.SetString(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
resp.bodyRaw = nil
|
||||
|
||||
oldBody := bb.B
|
||||
bb.B = body
|
||||
return oldBody
|
||||
|
@ -562,7 +509,7 @@ func (req *Request) SwapBody(body []byte) []byte {
|
|||
if req.bodyStream != nil {
|
||||
bb.Reset()
|
||||
_, err := copyZeroAlloc(bb, req.bodyStream)
|
||||
req.closeBodyStream() //nolint:errcheck
|
||||
req.closeBodyStream()
|
||||
if err != nil {
|
||||
bb.Reset()
|
||||
bb.SetString(err.Error())
|
||||
|
@ -582,7 +529,7 @@ func (req *Request) Body() []byte {
|
|||
bodyBuf := req.bodyBuffer()
|
||||
bodyBuf.Reset()
|
||||
_, err := copyZeroAlloc(bodyBuf, req.bodyStream)
|
||||
req.closeBodyStream() //nolint:errcheck
|
||||
req.closeBodyStream()
|
||||
if err != nil {
|
||||
bodyBuf.SetString(err.Error())
|
||||
}
|
||||
|
@ -606,8 +553,8 @@ func (req *Request) AppendBody(p []byte) {
|
|||
// AppendBodyString appends s to request body.
|
||||
func (req *Request) AppendBodyString(s string) {
|
||||
req.RemoveMultipartFormFiles()
|
||||
req.closeBodyStream() //nolint:errcheck
|
||||
req.bodyBuffer().WriteString(s) //nolint:errcheck
|
||||
req.closeBodyStream()
|
||||
req.bodyBuffer().WriteString(s)
|
||||
}
|
||||
|
||||
// SetBody sets request body.
|
||||
|
@ -620,14 +567,14 @@ func (req *Request) SetBody(body []byte) {
|
|||
// SetBodyString sets request body.
|
||||
func (req *Request) SetBodyString(body string) {
|
||||
req.RemoveMultipartFormFiles()
|
||||
req.closeBodyStream() //nolint:errcheck
|
||||
req.closeBodyStream()
|
||||
req.bodyBuffer().SetString(body)
|
||||
}
|
||||
|
||||
// ResetBody resets request body.
|
||||
func (req *Request) ResetBody() {
|
||||
req.RemoveMultipartFormFiles()
|
||||
req.closeBodyStream() //nolint:errcheck
|
||||
req.closeBodyStream()
|
||||
if req.body != nil {
|
||||
if req.keepBodyBuffer {
|
||||
req.body.Reset()
|
||||
|
@ -666,12 +613,7 @@ func (req *Request) copyToSkipBody(dst *Request) {
|
|||
// CopyTo copies resp contents to dst except of body stream.
|
||||
func (resp *Response) CopyTo(dst *Response) {
|
||||
resp.copyToSkipBody(dst)
|
||||
if resp.bodyRaw != nil {
|
||||
dst.bodyRaw = resp.bodyRaw
|
||||
if dst.body != nil {
|
||||
dst.body.Reset()
|
||||
}
|
||||
} else if resp.body != nil {
|
||||
if resp.body != nil {
|
||||
dst.bodyBuffer().Set(resp.body.B)
|
||||
} else if dst.body != nil {
|
||||
dst.body.Reset()
|
||||
|
@ -682,8 +624,6 @@ func (resp *Response) copyToSkipBody(dst *Response) {
|
|||
dst.Reset()
|
||||
resp.Header.CopyTo(&dst.Header)
|
||||
dst.SkipBody = resp.SkipBody
|
||||
dst.raddr = resp.raddr
|
||||
dst.laddr = resp.laddr
|
||||
}
|
||||
|
||||
func swapRequestBody(a, b *Request) {
|
||||
|
@ -693,7 +633,6 @@ func swapRequestBody(a, b *Request) {
|
|||
|
||||
func swapResponseBody(a, b *Response) {
|
||||
a.body, b.body = b.body, a.body
|
||||
a.bodyRaw, b.bodyRaw = b.bodyRaw, a.bodyRaw
|
||||
a.bodyStream, b.bodyStream = b.bodyStream, a.bodyStream
|
||||
}
|
||||
|
||||
|
@ -709,7 +648,7 @@ func (req *Request) parseURI() {
|
|||
}
|
||||
req.parsedURI = true
|
||||
|
||||
req.uri.parse(req.Header.Host(), req.Header.RequestURI(), req.isTLS)
|
||||
req.uri.parseQuick(req.Header.RequestURI(), &req.Header, req.isTLS)
|
||||
}
|
||||
|
||||
// PostArgs returns POST arguments.
|
||||
|
@ -772,7 +711,7 @@ func (req *Request) MultipartForm() (*multipart.Form, error) {
|
|||
}
|
||||
|
||||
func marshalMultipartForm(f *multipart.Form, boundary string) ([]byte, error) {
|
||||
var buf bytebufferpool.ByteBuffer
|
||||
var buf ByteBuffer
|
||||
if err := WriteMultipartForm(&buf, f, boundary); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -783,7 +722,7 @@ func marshalMultipartForm(f *multipart.Form, boundary string) ([]byte, error) {
|
|||
// boundary to w.
|
||||
func WriteMultipartForm(w io.Writer, f *multipart.Form, boundary string) error {
|
||||
// Do not care about memory allocations here, since multipart
|
||||
// form processing is slow.
|
||||
// form processing is slooow.
|
||||
if len(boundary) == 0 {
|
||||
panic("BUG: form boundary cannot be empty")
|
||||
}
|
||||
|
@ -805,7 +744,7 @@ func WriteMultipartForm(w io.Writer, f *multipart.Form, boundary string) error {
|
|||
// marshal files
|
||||
for k, fvv := range f.File {
|
||||
for _, fv := range fvv {
|
||||
vw, err := mw.CreatePart(fv.Header)
|
||||
vw, err := mw.CreateFormFile(k, fv.Filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create form file %q (%q): %s", k, fv.Filename, err)
|
||||
}
|
||||
|
@ -835,7 +774,7 @@ func readMultipartForm(r io.Reader, boundary string, size, maxInMemoryFileSize i
|
|||
// in multipart/form-data requests.
|
||||
|
||||
if size <= 0 {
|
||||
return nil, fmt.Errorf("form size must be greater than 0. Given %d", size)
|
||||
panic(fmt.Sprintf("BUG: form size must be greater than 0. Given %d", size))
|
||||
}
|
||||
lr := io.LimitReader(r, int64(size))
|
||||
mr := multipart.NewReader(lr, boundary)
|
||||
|
@ -850,7 +789,6 @@ func readMultipartForm(r io.Reader, boundary string, size, maxInMemoryFileSize i
|
|||
func (req *Request) Reset() {
|
||||
req.Header.Reset()
|
||||
req.resetSkipHeader()
|
||||
req.timeout = 0
|
||||
}
|
||||
|
||||
func (req *Request) resetSkipHeader() {
|
||||
|
@ -868,7 +806,7 @@ func (req *Request) RemoveMultipartFormFiles() {
|
|||
if req.multipartForm != nil {
|
||||
// Do not check for error, since these files may be deleted or moved
|
||||
// to new places by user code.
|
||||
req.multipartForm.RemoveAll() //nolint:errcheck
|
||||
req.multipartForm.RemoveAll()
|
||||
req.multipartForm = nil
|
||||
}
|
||||
req.multipartFormBoundary = ""
|
||||
|
@ -879,9 +817,6 @@ func (resp *Response) Reset() {
|
|||
resp.Header.Reset()
|
||||
resp.resetSkipHeader()
|
||||
resp.SkipBody = false
|
||||
resp.raddr = nil
|
||||
resp.laddr = nil
|
||||
resp.ImmediateHeaderFlush = false
|
||||
}
|
||||
|
||||
func (resp *Response) resetSkipHeader() {
|
||||
|
@ -909,9 +844,7 @@ func (req *Request) Read(r *bufio.Reader) error {
|
|||
|
||||
const defaultMaxInMemoryFileSize = 16 * 1024 * 1024
|
||||
|
||||
// ErrGetOnly is returned when server expects only GET requests,
|
||||
// but some other type of request came (Server.GetOnly option is true).
|
||||
var ErrGetOnly = errors.New("non-GET request received")
|
||||
var errGetOnly = errors.New("non-GET request received")
|
||||
|
||||
// ReadLimitBody reads request from the given r, limiting the body size.
|
||||
//
|
||||
|
@ -933,19 +866,23 @@ var ErrGetOnly = errors.New("non-GET request received")
|
|||
// io.EOF is returned if r is closed before reading the first header byte.
|
||||
func (req *Request) ReadLimitBody(r *bufio.Reader, maxBodySize int) error {
|
||||
req.resetSkipHeader()
|
||||
if err := req.Header.Read(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return req.readLimitBody(r, maxBodySize, false, true)
|
||||
return req.readLimitBody(r, maxBodySize, false)
|
||||
}
|
||||
|
||||
func (req *Request) readLimitBody(r *bufio.Reader, maxBodySize int, getOnly bool, preParseMultipartForm bool) error {
|
||||
func (req *Request) readLimitBody(r *bufio.Reader, maxBodySize int, getOnly bool) error {
|
||||
// Do not reset the request here - the caller must reset it before
|
||||
// calling this method.
|
||||
|
||||
err := req.Header.Read(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if getOnly && !req.Header.IsGet() {
|
||||
return ErrGetOnly
|
||||
return errGetOnly
|
||||
}
|
||||
|
||||
if req.Header.noBody() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if req.MayContinue() {
|
||||
|
@ -955,7 +892,7 @@ func (req *Request) readLimitBody(r *bufio.Reader, maxBodySize int, getOnly bool
|
|||
return nil
|
||||
}
|
||||
|
||||
return req.ContinueReadBody(r, maxBodySize, preParseMultipartForm)
|
||||
return req.ContinueReadBody(r, maxBodySize)
|
||||
}
|
||||
|
||||
// MayContinue returns true if the request contains
|
||||
|
@ -979,26 +916,24 @@ func (req *Request) MayContinue() bool {
|
|||
//
|
||||
// If maxBodySize > 0 and the body size exceeds maxBodySize,
|
||||
// then ErrBodyTooLarge is returned.
|
||||
func (req *Request) ContinueReadBody(r *bufio.Reader, maxBodySize int, preParseMultipartForm ...bool) error {
|
||||
func (req *Request) ContinueReadBody(r *bufio.Reader, maxBodySize int) error {
|
||||
var err error
|
||||
contentLength := req.Header.realContentLength()
|
||||
contentLength := req.Header.ContentLength()
|
||||
if contentLength > 0 {
|
||||
if maxBodySize > 0 && contentLength > maxBodySize {
|
||||
return ErrBodyTooLarge
|
||||
}
|
||||
|
||||
if len(preParseMultipartForm) == 0 || preParseMultipartForm[0] {
|
||||
// Pre-read multipart form data of known length.
|
||||
// This way we limit memory usage for large file uploads, since their contents
|
||||
// is streamed into temporary files if file size exceeds defaultMaxInMemoryFileSize.
|
||||
req.multipartFormBoundary = string(req.Header.MultipartFormBoundary())
|
||||
if len(req.multipartFormBoundary) > 0 && len(req.Header.peek(strContentEncoding)) == 0 {
|
||||
req.multipartForm, err = readMultipartForm(r, req.multipartFormBoundary, contentLength, defaultMaxInMemoryFileSize)
|
||||
if err != nil {
|
||||
req.Reset()
|
||||
}
|
||||
return err
|
||||
// Pre-read multipart form data of known length.
|
||||
// This way we limit memory usage for large file uploads, since their contents
|
||||
// is streamed into temporary files if file size exceeds defaultMaxInMemoryFileSize.
|
||||
req.multipartFormBoundary = string(req.Header.MultipartFormBoundary())
|
||||
if len(req.multipartFormBoundary) > 0 && len(req.Header.peek(strContentEncoding)) == 0 {
|
||||
req.multipartForm, err = readMultipartForm(r, req.multipartFormBoundary, contentLength, defaultMaxInMemoryFileSize)
|
||||
if err != nil {
|
||||
req.Reset()
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1053,6 +988,7 @@ func (resp *Response) ReadLimitBody(r *bufio.Reader, maxBodySize int) error {
|
|||
bodyBuf.Reset()
|
||||
bodyBuf.B, err = readBody(r, resp.Header.ContentLength(), maxBodySize, bodyBuf.B)
|
||||
if err != nil {
|
||||
resp.Reset()
|
||||
return err
|
||||
}
|
||||
resp.Header.SetContentLength(len(bodyBuf.B))
|
||||
|
@ -1157,25 +1093,6 @@ func (req *Request) Write(w *bufio.Writer) error {
|
|||
}
|
||||
req.Header.SetHostBytes(host)
|
||||
req.Header.SetRequestURIBytes(uri.RequestURI())
|
||||
|
||||
if len(uri.username) > 0 {
|
||||
// RequestHeader.SetBytesKV only uses RequestHeader.bufKV.key
|
||||
// So we are free to use RequestHeader.bufKV.value as a scratch pad for
|
||||
// the base64 encoding.
|
||||
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)
|
||||
}
|
||||
buf := req.Header.bufKV.value[:0]
|
||||
buf = append(buf, uri.username...)
|
||||
buf = append(buf, strColon...)
|
||||
buf = append(buf, uri.password...)
|
||||
buf = append(buf, strBasicSpace...)
|
||||
base64.StdEncoding.Encode(buf[nb:tl], buf[:nl])
|
||||
req.Header.SetBytesKV(strAuthorization, buf[nl:tl])
|
||||
}
|
||||
}
|
||||
|
||||
if req.bodyStream != nil {
|
||||
|
@ -1192,12 +1109,8 @@ func (req *Request) Write(w *bufio.Writer) error {
|
|||
req.Header.SetMultipartFormBoundary(req.multipartFormBoundary)
|
||||
}
|
||||
|
||||
hasBody := false
|
||||
if len(body) == 0 {
|
||||
body = req.postArgs.QueryString()
|
||||
}
|
||||
if len(body) != 0 || !req.Header.ignoreBody() {
|
||||
hasBody = true
|
||||
hasBody := !req.Header.noBody()
|
||||
if hasBody {
|
||||
req.Header.SetContentLength(len(body))
|
||||
}
|
||||
if err = req.Header.Write(w); err != nil {
|
||||
|
@ -1300,7 +1213,7 @@ func (resp *Response) gzipBody(level int) error {
|
|||
wf: zw,
|
||||
bw: sw,
|
||||
}
|
||||
copyZeroAlloc(fw, bs) //nolint:errcheck
|
||||
copyZeroAlloc(fw, bs)
|
||||
releaseStacklessGzipWriter(zw, level)
|
||||
if bsc, ok := bs.(io.Closer); ok {
|
||||
bsc.Close()
|
||||
|
@ -1322,7 +1235,6 @@ func (resp *Response) gzipBody(level int) error {
|
|||
responseBodyPool.Put(resp.body)
|
||||
}
|
||||
resp.body = w
|
||||
resp.bodyRaw = nil
|
||||
}
|
||||
resp.Header.SetCanonical(strContentEncoding, strGzip)
|
||||
return nil
|
||||
|
@ -1355,7 +1267,7 @@ func (resp *Response) deflateBody(level int) error {
|
|||
wf: zw,
|
||||
bw: sw,
|
||||
}
|
||||
copyZeroAlloc(fw, bs) //nolint:errcheck
|
||||
copyZeroAlloc(fw, bs)
|
||||
releaseStacklessDeflateWriter(zw, level)
|
||||
if bsc, ok := bs.(io.Closer); ok {
|
||||
bsc.Close()
|
||||
|
@ -1377,7 +1289,6 @@ func (resp *Response) deflateBody(level int) error {
|
|||
responseBodyPool.Put(resp.body)
|
||||
}
|
||||
resp.body = w
|
||||
resp.bodyRaw = nil
|
||||
}
|
||||
resp.Header.SetCanonical(strContentEncoding, strDeflate)
|
||||
return nil
|
||||
|
@ -1471,19 +1382,8 @@ func (req *Request) writeBodyStream(w *bufio.Writer) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// ErrBodyStreamWritePanic is returned when panic happens during writing body stream.
|
||||
type ErrBodyStreamWritePanic struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (resp *Response) writeBodyStream(w *bufio.Writer, sendBody bool) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = &ErrBodyStreamWritePanic{
|
||||
error: fmt.Errorf("panic while writing body stream: %+v", r),
|
||||
}
|
||||
}
|
||||
}()
|
||||
func (resp *Response) writeBodyStream(w *bufio.Writer, sendBody bool) error {
|
||||
var err error
|
||||
|
||||
contentLength := resp.Header.ContentLength()
|
||||
if contentLength < 0 {
|
||||
|
@ -1500,22 +1400,12 @@ func (resp *Response) writeBodyStream(w *bufio.Writer, sendBody bool) (err error
|
|||
}
|
||||
if contentLength >= 0 {
|
||||
if err = resp.Header.Write(w); err == nil && sendBody {
|
||||
if resp.ImmediateHeaderFlush {
|
||||
err = w.Flush()
|
||||
}
|
||||
if err == nil {
|
||||
err = writeBodyFixedSize(w, resp.bodyStream, int64(contentLength))
|
||||
}
|
||||
err = writeBodyFixedSize(w, resp.bodyStream, int64(contentLength))
|
||||
}
|
||||
} else {
|
||||
resp.Header.SetContentLength(-1)
|
||||
if err = resp.Header.Write(w); err == nil && sendBody {
|
||||
if resp.ImmediateHeaderFlush {
|
||||
err = w.Flush()
|
||||
}
|
||||
if err == nil {
|
||||
err = writeBodyChunked(w, resp.bodyStream)
|
||||
}
|
||||
err = writeBodyChunked(w, resp.bodyStream)
|
||||
}
|
||||
}
|
||||
err1 := resp.closeBodyStream()
|
||||
|
@ -1568,7 +1458,7 @@ func (resp *Response) String() string {
|
|||
}
|
||||
|
||||
func getHTTPString(hw httpWriter) string {
|
||||
w := bytebufferpool.Get()
|
||||
w := AcquireByteBuffer()
|
||||
bw := bufio.NewWriter(w)
|
||||
if err := hw.Write(bw); err != nil {
|
||||
return err.Error()
|
||||
|
@ -1577,7 +1467,7 @@ func getHTTPString(hw httpWriter) string {
|
|||
return err.Error()
|
||||
}
|
||||
s := string(w.B)
|
||||
bytebufferpool.Put(w)
|
||||
ReleaseByteBuffer(w)
|
||||
return s
|
||||
}
|
||||
|
||||
|
@ -1666,15 +1556,9 @@ var copyBufPool = sync.Pool{
|
|||
|
||||
func writeChunk(w *bufio.Writer, b []byte) error {
|
||||
n := len(b)
|
||||
if err := writeHexInt(w, n); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write(strCRLF); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write(b); err != nil {
|
||||
return err
|
||||
}
|
||||
writeHexInt(w, n)
|
||||
w.Write(strCRLF)
|
||||
w.Write(b)
|
||||
_, err := w.Write(strCRLF)
|
||||
err1 := w.Flush()
|
||||
if err == nil {
|
||||
|
@ -1766,11 +1650,6 @@ func appendBodyFixedSize(r *bufio.Reader, dst []byte, n int) ([]byte, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// ErrBrokenChunk is returned when server receives a broken chunked body (Transfer-Encoding: chunked).
|
||||
type ErrBrokenChunk struct {
|
||||
error
|
||||
}
|
||||
|
||||
func readBodyChunked(r *bufio.Reader, maxBodySize int, dst []byte) ([]byte, error) {
|
||||
if len(dst) > 0 {
|
||||
panic("BUG: expected zero-length buffer")
|
||||
|
@ -1790,9 +1669,7 @@ func readBodyChunked(r *bufio.Reader, maxBodySize int, dst []byte) ([]byte, erro
|
|||
return dst, err
|
||||
}
|
||||
if !bytes.Equal(dst[len(dst)-strCRLFLen:], strCRLF) {
|
||||
return dst, ErrBrokenChunk{
|
||||
error: fmt.Errorf("cannot find crlf at the end of chunk"),
|
||||
}
|
||||
return dst, fmt.Errorf("cannot find crlf at the end of chunk")
|
||||
}
|
||||
dst = dst[:len(dst)-strCRLFLen]
|
||||
if chunkSize == 0 {
|
||||
|
@ -1806,34 +1683,19 @@ func parseChunkSize(r *bufio.Reader) (int, error) {
|
|||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
for {
|
||||
c, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return -1, ErrBrokenChunk{
|
||||
error: fmt.Errorf("cannot read '\r' char at the end of chunk size: %s", err),
|
||||
}
|
||||
}
|
||||
// Skip any trailing whitespace after chunk size.
|
||||
if c == ' ' {
|
||||
continue
|
||||
}
|
||||
if c != '\r' {
|
||||
return -1, ErrBrokenChunk{
|
||||
error: fmt.Errorf("unexpected char %q at the end of chunk size. Expected %q", c, '\r'),
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
c, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return -1, ErrBrokenChunk{
|
||||
error: fmt.Errorf("cannot read '\n' char at the end of chunk size: %s", err),
|
||||
}
|
||||
return -1, fmt.Errorf("cannot read '\r' char at the end of chunk size: %s", err)
|
||||
}
|
||||
if c != '\r' {
|
||||
return -1, fmt.Errorf("unexpected char %q at the end of chunk size. Expected %q", c, '\r')
|
||||
}
|
||||
c, err = r.ReadByte()
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("cannot read '\n' char at the end of chunk size: %s", err)
|
||||
}
|
||||
if c != '\n' {
|
||||
return -1, ErrBrokenChunk{
|
||||
error: fmt.Errorf("unexpected char %q at the end of chunk size. Expected %q", c, '\n'),
|
||||
}
|
||||
return -1, fmt.Errorf("unexpected char %q at the end of chunk size. Expected %q", c, '\n')
|
||||
}
|
||||
return n, nil
|
||||
}
|
|
@ -17,7 +17,7 @@ type BalancingClient interface {
|
|||
//
|
||||
// It has the following features:
|
||||
//
|
||||
// - Balances load among available clients using 'least loaded' + 'least total'
|
||||
// - Balances load among available clients using 'least loaded' + 'round robin'
|
||||
// hybrid technique.
|
||||
// - Dynamically decreases load on unhealthy clients.
|
||||
//
|
||||
|
@ -25,7 +25,7 @@ type BalancingClient interface {
|
|||
//
|
||||
// It is safe calling LBClient methods from concurrently running goroutines.
|
||||
type LBClient struct {
|
||||
noCopy noCopy //nolint:unused,structcheck
|
||||
noCopy noCopy
|
||||
|
||||
// Clients must contain non-zero clients list.
|
||||
// Incoming requests are balanced among these clients.
|
||||
|
@ -49,13 +49,17 @@ type LBClient struct {
|
|||
|
||||
cs []*lbClient
|
||||
|
||||
// nextIdx is for spreading requests among equally loaded clients
|
||||
// in a round-robin fashion.
|
||||
nextIdx uint32
|
||||
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// DefaultLBClientTimeout is the default request timeout used by LBClient
|
||||
// when calling LBClient.Do.
|
||||
//
|
||||
// The timeout may be overridden via LBClient.Timeout.
|
||||
// The timeout may be overriden via LBClient.Timeout.
|
||||
const DefaultLBClientTimeout = time.Second
|
||||
|
||||
// DoDeadline calls DoDeadline on the least loaded client
|
||||
|
@ -89,23 +93,42 @@ func (cc *LBClient) init() {
|
|||
healthCheck: cc.HealthCheck,
|
||||
})
|
||||
}
|
||||
|
||||
// Randomize nextIdx in order to prevent initial servers'
|
||||
// hammering from a cluster of identical LBClients.
|
||||
cc.nextIdx = uint32(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
func (cc *LBClient) get() *lbClient {
|
||||
cc.once.Do(cc.init)
|
||||
|
||||
cs := cc.cs
|
||||
idx := atomic.AddUint32(&cc.nextIdx, 1)
|
||||
idx %= uint32(len(cs))
|
||||
|
||||
minC := cs[0]
|
||||
minC := cs[idx]
|
||||
minN := minC.PendingRequests()
|
||||
minT := atomic.LoadUint64(&minC.total)
|
||||
for _, c := range cs[1:] {
|
||||
if minN == 0 {
|
||||
return minC
|
||||
}
|
||||
for _, c := range cs[idx+1:] {
|
||||
n := c.PendingRequests()
|
||||
t := atomic.LoadUint64(&c.total)
|
||||
if n < minN || (n == minN && t < minT) {
|
||||
if n == 0 {
|
||||
return c
|
||||
}
|
||||
if n < minN {
|
||||
minC = c
|
||||
minN = n
|
||||
}
|
||||
}
|
||||
for _, c := range cs[:idx] {
|
||||
n := c.PendingRequests()
|
||||
if n == 0 {
|
||||
return c
|
||||
}
|
||||
if n < minN {
|
||||
minC = c
|
||||
minN = n
|
||||
minT = t
|
||||
}
|
||||
}
|
||||
return minC
|
||||
|
@ -115,9 +138,6 @@ type lbClient struct {
|
|||
c BalancingClient
|
||||
healthCheck func(req *Request, resp *Response, err error) bool
|
||||
penalty uint32
|
||||
|
||||
// total amount of requests handled.
|
||||
total uint64
|
||||
}
|
||||
|
||||
func (c *lbClient) DoDeadline(req *Request, resp *Response, deadline time.Time) error {
|
||||
|
@ -126,8 +146,6 @@ func (c *lbClient) DoDeadline(req *Request, resp *Response, deadline time.Time)
|
|||
// Penalize the client returning error, so the next requests
|
||||
// are routed to another clients.
|
||||
time.AfterFunc(penaltyDuration, c.decPenalty)
|
||||
} else {
|
||||
atomic.AddUint64(&c.total, 1)
|
||||
}
|
||||
return err
|
||||
}
|
|
@ -4,8 +4,6 @@ package fasthttp
|
|||
// so `go vet` gives a warning if this struct is copied.
|
||||
//
|
||||
// See https://github.com/golang/go/issues/8005#issuecomment-190753527 for details.
|
||||
// and also: https://stackoverflow.com/questions/52494458/nocopy-minimal-example
|
||||
type noCopy struct{} //nolint:unused
|
||||
type noCopy struct{}
|
||||
|
||||
func (*noCopy) Lock() {}
|
||||
func (*noCopy) Unlock() {}
|
||||
func (*noCopy) Lock() {}
|
File diff suppressed because it is too large
Load diff
|
@ -3,9 +3,8 @@ package stackless
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/valyala/bytebufferpool"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Writer is an interface stackless writer must conform to.
|
||||
|
@ -75,7 +74,7 @@ func (w *writer) Close() error {
|
|||
|
||||
func (w *writer) Reset(dstW io.Writer) {
|
||||
w.xw.Reset()
|
||||
w.do(opReset) //nolint:errcheck
|
||||
w.do(opReset)
|
||||
w.dstW = dstW
|
||||
}
|
||||
|
||||
|
@ -125,7 +124,8 @@ func (w *xWriter) Write(p []byte) (int, error) {
|
|||
if w.bb == nil {
|
||||
w.bb = bufferPool.Get()
|
||||
}
|
||||
return w.bb.Write(p)
|
||||
w.bb.Write(p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (w *xWriter) Reset() {
|
|
@ -5,7 +5,7 @@ import (
|
|||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/valyala/fasthttp/fasthttputil"
|
||||
"github.com/VictoriaMetrics/fasthttp/fasthttputil"
|
||||
)
|
||||
|
||||
// StreamWriter must write data to w.
|
73
vendor/github.com/VictoriaMetrics/fasthttp/strings.go
generated
vendored
Normal file
73
vendor/github.com/VictoriaMetrics/fasthttp/strings.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
package fasthttp
|
||||
|
||||
var (
|
||||
defaultServerName = []byte("fasthttp")
|
||||
defaultUserAgent = []byte("fasthttp")
|
||||
defaultContentType = []byte("text/plain; charset=utf-8")
|
||||
)
|
||||
|
||||
var (
|
||||
strSlash = []byte("/")
|
||||
strSlashSlash = []byte("//")
|
||||
strSlashDotDot = []byte("/..")
|
||||
strSlashDotSlash = []byte("/./")
|
||||
strSlashDotDotSlash = []byte("/../")
|
||||
strCRLF = []byte("\r\n")
|
||||
strHTTP = []byte("http")
|
||||
strHTTPS = []byte("https")
|
||||
strHTTP11 = []byte("HTTP/1.1")
|
||||
strColonSlashSlash = []byte("://")
|
||||
strColonSpace = []byte(": ")
|
||||
strGMT = []byte("GMT")
|
||||
|
||||
strResponseContinue = []byte("HTTP/1.1 100 Continue\r\n\r\n")
|
||||
|
||||
strGet = []byte("GET")
|
||||
strHead = []byte("HEAD")
|
||||
strPost = []byte("POST")
|
||||
strPut = []byte("PUT")
|
||||
strDelete = []byte("DELETE")
|
||||
|
||||
strExpect = []byte("Expect")
|
||||
strConnection = []byte("Connection")
|
||||
strContentLength = []byte("Content-Length")
|
||||
strContentType = []byte("Content-Type")
|
||||
strDate = []byte("Date")
|
||||
strHost = []byte("Host")
|
||||
strReferer = []byte("Referer")
|
||||
strServer = []byte("Server")
|
||||
strTransferEncoding = []byte("Transfer-Encoding")
|
||||
strContentEncoding = []byte("Content-Encoding")
|
||||
strAcceptEncoding = []byte("Accept-Encoding")
|
||||
strUserAgent = []byte("User-Agent")
|
||||
strCookie = []byte("Cookie")
|
||||
strSetCookie = []byte("Set-Cookie")
|
||||
strLocation = []byte("Location")
|
||||
strIfModifiedSince = []byte("If-Modified-Since")
|
||||
strLastModified = []byte("Last-Modified")
|
||||
strAcceptRanges = []byte("Accept-Ranges")
|
||||
strRange = []byte("Range")
|
||||
strContentRange = []byte("Content-Range")
|
||||
|
||||
strCookieExpires = []byte("expires")
|
||||
strCookieDomain = []byte("domain")
|
||||
strCookiePath = []byte("path")
|
||||
strCookieHTTPOnly = []byte("HttpOnly")
|
||||
strCookieSecure = []byte("secure")
|
||||
|
||||
strClose = []byte("close")
|
||||
strGzip = []byte("gzip")
|
||||
strDeflate = []byte("deflate")
|
||||
strKeepAlive = []byte("keep-alive")
|
||||
strKeepAliveCamelCase = []byte("Keep-Alive")
|
||||
strUpgrade = []byte("Upgrade")
|
||||
strChunked = []byte("chunked")
|
||||
strIdentity = []byte("identity")
|
||||
str100Continue = []byte("100-continue")
|
||||
strPostArgsContentType = []byte("application/x-www-form-urlencoded")
|
||||
strMultipartFormData = []byte("multipart/form-data")
|
||||
strBoundary = []byte("boundary")
|
||||
strBytes = []byte("bytes")
|
||||
strTextSlash = []byte("text/")
|
||||
strApplicationSlash = []byte("application/")
|
||||
)
|
369
vendor/github.com/VictoriaMetrics/fasthttp/tcpdialer.go
generated
vendored
Normal file
369
vendor/github.com/VictoriaMetrics/fasthttp/tcpdialer.go
generated
vendored
Normal file
|
@ -0,0 +1,369 @@
|
|||
package fasthttp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Dial dials the given TCP addr using tcp4.
|
||||
//
|
||||
// This function has the following additional features comparing to net.Dial:
|
||||
//
|
||||
// * It reduces load on DNS resolver by caching resolved TCP addressed
|
||||
// for DefaultDNSCacheDuration.
|
||||
// * It dials all the resolved TCP addresses in round-robin manner until
|
||||
// connection is established. This may be useful if certain addresses
|
||||
// are temporarily unreachable.
|
||||
// * It returns ErrDialTimeout if connection cannot be established during
|
||||
// DefaultDialTimeout seconds. Use DialTimeout for customizing dial timeout.
|
||||
//
|
||||
// This dialer is intended for custom code wrapping before passing
|
||||
// to Client.Dial or HostClient.Dial.
|
||||
//
|
||||
// For instance, per-host counters and/or limits may be implemented
|
||||
// by such wrappers.
|
||||
//
|
||||
// The addr passed to the function must contain port. Example addr values:
|
||||
//
|
||||
// * foobar.baz:443
|
||||
// * foo.bar:80
|
||||
// * aaa.com:8080
|
||||
func Dial(addr string) (net.Conn, error) {
|
||||
return getDialer(DefaultDialTimeout, false)(addr)
|
||||
}
|
||||
|
||||
// DialTimeout dials the given TCP addr using tcp4 using the given timeout.
|
||||
//
|
||||
// This function has the following additional features comparing to net.Dial:
|
||||
//
|
||||
// * It reduces load on DNS resolver by caching resolved TCP addressed
|
||||
// for DefaultDNSCacheDuration.
|
||||
// * It dials all the resolved TCP addresses in round-robin manner until
|
||||
// connection is established. This may be useful if certain addresses
|
||||
// are temporarily unreachable.
|
||||
//
|
||||
// This dialer is intended for custom code wrapping before passing
|
||||
// to Client.Dial or HostClient.Dial.
|
||||
//
|
||||
// For instance, per-host counters and/or limits may be implemented
|
||||
// by such wrappers.
|
||||
//
|
||||
// The addr passed to the function must contain port. Example addr values:
|
||||
//
|
||||
// * foobar.baz:443
|
||||
// * foo.bar:80
|
||||
// * aaa.com:8080
|
||||
func DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
|
||||
return getDialer(timeout, false)(addr)
|
||||
}
|
||||
|
||||
// DialDualStack dials the given TCP addr using both tcp4 and tcp6.
|
||||
//
|
||||
// This function has the following additional features comparing to net.Dial:
|
||||
//
|
||||
// * It reduces load on DNS resolver by caching resolved TCP addressed
|
||||
// for DefaultDNSCacheDuration.
|
||||
// * It dials all the resolved TCP addresses in round-robin manner until
|
||||
// connection is established. This may be useful if certain addresses
|
||||
// are temporarily unreachable.
|
||||
// * It returns ErrDialTimeout if connection cannot be established during
|
||||
// DefaultDialTimeout seconds. Use DialDualStackTimeout for custom dial
|
||||
// timeout.
|
||||
//
|
||||
// This dialer is intended for custom code wrapping before passing
|
||||
// to Client.Dial or HostClient.Dial.
|
||||
//
|
||||
// For instance, per-host counters and/or limits may be implemented
|
||||
// by such wrappers.
|
||||
//
|
||||
// The addr passed to the function must contain port. Example addr values:
|
||||
//
|
||||
// * foobar.baz:443
|
||||
// * foo.bar:80
|
||||
// * aaa.com:8080
|
||||
func DialDualStack(addr string) (net.Conn, error) {
|
||||
return getDialer(DefaultDialTimeout, true)(addr)
|
||||
}
|
||||
|
||||
// DialDualStackTimeout dials the given TCP addr using both tcp4 and tcp6
|
||||
// using the given timeout.
|
||||
//
|
||||
// This function has the following additional features comparing to net.Dial:
|
||||
//
|
||||
// * It reduces load on DNS resolver by caching resolved TCP addressed
|
||||
// for DefaultDNSCacheDuration.
|
||||
// * It dials all the resolved TCP addresses in round-robin manner until
|
||||
// connection is established. This may be useful if certain addresses
|
||||
// are temporarily unreachable.
|
||||
//
|
||||
// This dialer is intended for custom code wrapping before passing
|
||||
// to Client.Dial or HostClient.Dial.
|
||||
//
|
||||
// For instance, per-host counters and/or limits may be implemented
|
||||
// by such wrappers.
|
||||
//
|
||||
// The addr passed to the function must contain port. Example addr values:
|
||||
//
|
||||
// * foobar.baz:443
|
||||
// * foo.bar:80
|
||||
// * aaa.com:8080
|
||||
func DialDualStackTimeout(addr string, timeout time.Duration) (net.Conn, error) {
|
||||
return getDialer(timeout, true)(addr)
|
||||
}
|
||||
|
||||
func getDialer(timeout time.Duration, dualStack bool) DialFunc {
|
||||
if timeout <= 0 {
|
||||
timeout = DefaultDialTimeout
|
||||
}
|
||||
timeoutRounded := int(timeout.Seconds()*10 + 9)
|
||||
|
||||
m := dialMap
|
||||
if dualStack {
|
||||
m = dialDualStackMap
|
||||
}
|
||||
|
||||
dialMapLock.Lock()
|
||||
d := m[timeoutRounded]
|
||||
if d == nil {
|
||||
dialer := dialerStd
|
||||
if dualStack {
|
||||
dialer = dialerDualStack
|
||||
}
|
||||
d = dialer.NewDial(timeout)
|
||||
m[timeoutRounded] = d
|
||||
}
|
||||
dialMapLock.Unlock()
|
||||
return d
|
||||
}
|
||||
|
||||
var (
|
||||
dialerStd = &tcpDialer{}
|
||||
dialerDualStack = &tcpDialer{DualStack: true}
|
||||
|
||||
dialMap = make(map[int]DialFunc)
|
||||
dialDualStackMap = make(map[int]DialFunc)
|
||||
dialMapLock sync.Mutex
|
||||
)
|
||||
|
||||
type tcpDialer struct {
|
||||
DualStack bool
|
||||
|
||||
tcpAddrsLock sync.Mutex
|
||||
tcpAddrsMap map[string]*tcpAddrEntry
|
||||
|
||||
concurrencyCh chan struct{}
|
||||
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
const maxDialConcurrency = 1000
|
||||
|
||||
func (d *tcpDialer) NewDial(timeout time.Duration) DialFunc {
|
||||
d.once.Do(func() {
|
||||
d.concurrencyCh = make(chan struct{}, maxDialConcurrency)
|
||||
d.tcpAddrsMap = make(map[string]*tcpAddrEntry)
|
||||
go d.tcpAddrsClean()
|
||||
})
|
||||
|
||||
return func(addr string) (net.Conn, error) {
|
||||
addrs, idx, err := d.getTCPAddrs(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
network := "tcp4"
|
||||
if d.DualStack {
|
||||
network = "tcp"
|
||||
}
|
||||
|
||||
var conn net.Conn
|
||||
n := uint32(len(addrs))
|
||||
deadline := time.Now().Add(timeout)
|
||||
for n > 0 {
|
||||
conn, err = tryDial(network, &addrs[idx%n], deadline, d.concurrencyCh)
|
||||
if err == nil {
|
||||
return conn, nil
|
||||
}
|
||||
if err == ErrDialTimeout {
|
||||
return nil, err
|
||||
}
|
||||
idx++
|
||||
n--
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func tryDial(network string, addr *net.TCPAddr, deadline time.Time, concurrencyCh chan struct{}) (net.Conn, error) {
|
||||
timeout := -time.Since(deadline)
|
||||
if timeout <= 0 {
|
||||
return nil, ErrDialTimeout
|
||||
}
|
||||
|
||||
select {
|
||||
case concurrencyCh <- struct{}{}:
|
||||
default:
|
||||
tc := acquireTimer(timeout)
|
||||
isTimeout := false
|
||||
select {
|
||||
case concurrencyCh <- struct{}{}:
|
||||
case <-tc.C:
|
||||
isTimeout = true
|
||||
}
|
||||
releaseTimer(tc)
|
||||
if isTimeout {
|
||||
return nil, ErrDialTimeout
|
||||
}
|
||||
}
|
||||
|
||||
timeout = -time.Since(deadline)
|
||||
if timeout <= 0 {
|
||||
<-concurrencyCh
|
||||
return nil, ErrDialTimeout
|
||||
}
|
||||
|
||||
chv := dialResultChanPool.Get()
|
||||
if chv == nil {
|
||||
chv = make(chan dialResult, 1)
|
||||
}
|
||||
ch := chv.(chan dialResult)
|
||||
go func() {
|
||||
var dr dialResult
|
||||
dr.conn, dr.err = net.DialTCP(network, nil, addr)
|
||||
ch <- dr
|
||||
<-concurrencyCh
|
||||
}()
|
||||
|
||||
var (
|
||||
conn net.Conn
|
||||
err error
|
||||
)
|
||||
|
||||
tc := acquireTimer(timeout)
|
||||
select {
|
||||
case dr := <-ch:
|
||||
conn = dr.conn
|
||||
err = dr.err
|
||||
dialResultChanPool.Put(ch)
|
||||
case <-tc.C:
|
||||
err = ErrDialTimeout
|
||||
}
|
||||
releaseTimer(tc)
|
||||
|
||||
return conn, err
|
||||
}
|
||||
|
||||
var dialResultChanPool sync.Pool
|
||||
|
||||
type dialResult struct {
|
||||
conn net.Conn
|
||||
err error
|
||||
}
|
||||
|
||||
// ErrDialTimeout is returned when TCP dialing is timed out.
|
||||
var ErrDialTimeout = errors.New("dialing to the given TCP address timed out")
|
||||
|
||||
// DefaultDialTimeout is timeout used by Dial and DialDualStack
|
||||
// for establishing TCP connections.
|
||||
const DefaultDialTimeout = 3 * time.Second
|
||||
|
||||
type tcpAddrEntry struct {
|
||||
addrs []net.TCPAddr
|
||||
addrsIdx uint32
|
||||
|
||||
resolveTime time.Time
|
||||
pending bool
|
||||
}
|
||||
|
||||
// DefaultDNSCacheDuration is the duration for caching resolved TCP addresses
|
||||
// by Dial* functions.
|
||||
const DefaultDNSCacheDuration = time.Minute
|
||||
|
||||
func (d *tcpDialer) tcpAddrsClean() {
|
||||
expireDuration := 2 * DefaultDNSCacheDuration
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
t := time.Now()
|
||||
|
||||
d.tcpAddrsLock.Lock()
|
||||
for k, e := range d.tcpAddrsMap {
|
||||
if t.Sub(e.resolveTime) > expireDuration {
|
||||
delete(d.tcpAddrsMap, k)
|
||||
}
|
||||
}
|
||||
d.tcpAddrsLock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (d *tcpDialer) getTCPAddrs(addr string) ([]net.TCPAddr, uint32, error) {
|
||||
d.tcpAddrsLock.Lock()
|
||||
e := d.tcpAddrsMap[addr]
|
||||
if e != nil && !e.pending && time.Since(e.resolveTime) > DefaultDNSCacheDuration {
|
||||
e.pending = true
|
||||
e = nil
|
||||
}
|
||||
d.tcpAddrsLock.Unlock()
|
||||
|
||||
if e == nil {
|
||||
addrs, err := resolveTCPAddrs(addr, d.DualStack)
|
||||
if err != nil {
|
||||
d.tcpAddrsLock.Lock()
|
||||
e = d.tcpAddrsMap[addr]
|
||||
if e != nil && e.pending {
|
||||
e.pending = false
|
||||
}
|
||||
d.tcpAddrsLock.Unlock()
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
e = &tcpAddrEntry{
|
||||
addrs: addrs,
|
||||
resolveTime: time.Now(),
|
||||
}
|
||||
|
||||
d.tcpAddrsLock.Lock()
|
||||
d.tcpAddrsMap[addr] = e
|
||||
d.tcpAddrsLock.Unlock()
|
||||
}
|
||||
|
||||
idx := atomic.AddUint32(&e.addrsIdx, 1)
|
||||
return e.addrs, idx, nil
|
||||
}
|
||||
|
||||
func resolveTCPAddrs(addr string, dualStack bool) ([]net.TCPAddr, error) {
|
||||
host, portS, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
port, err := strconv.Atoi(portS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ips, err := net.LookupIP(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n := len(ips)
|
||||
addrs := make([]net.TCPAddr, 0, n)
|
||||
for i := 0; i < n; i++ {
|
||||
ip := ips[i]
|
||||
if !dualStack && ip.To4() == nil {
|
||||
continue
|
||||
}
|
||||
addrs = append(addrs, net.TCPAddr{
|
||||
IP: ip,
|
||||
Port: port,
|
||||
})
|
||||
}
|
||||
if len(addrs) == 0 {
|
||||
return nil, errNoDNSEntries
|
||||
}
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
var errNoDNSEntries = errors.New("couldn't find DNS entries for the given domain. Try using DialDualStack")
|
|
@ -26,12 +26,7 @@ func stopTimer(t *time.Timer) {
|
|||
}
|
||||
}
|
||||
|
||||
// AcquireTimer returns a time.Timer from the pool and updates it to
|
||||
// send the current time on its channel after at least timeout.
|
||||
//
|
||||
// The returned Timer may be returned to the pool with ReleaseTimer
|
||||
// when no longer needed. This allows reducing GC load.
|
||||
func AcquireTimer(timeout time.Duration) *time.Timer {
|
||||
func acquireTimer(timeout time.Duration) *time.Timer {
|
||||
v := timerPool.Get()
|
||||
if v == nil {
|
||||
return time.NewTimer(timeout)
|
||||
|
@ -41,12 +36,7 @@ func AcquireTimer(timeout time.Duration) *time.Timer {
|
|||
return t
|
||||
}
|
||||
|
||||
// ReleaseTimer returns the time.Timer acquired via AcquireTimer to the pool
|
||||
// and prevents the Timer from firing.
|
||||
//
|
||||
// Do not access the released time.Timer or read from it's channel otherwise
|
||||
// data races may occur.
|
||||
func ReleaseTimer(t *time.Timer) {
|
||||
func releaseTimer(t *time.Timer) {
|
||||
stopTimer(t)
|
||||
timerPool.Put(t)
|
||||
}
|
|
@ -36,7 +36,7 @@ var uriPool = &sync.Pool{
|
|||
//
|
||||
// URI instance MUST NOT be used from concurrently running goroutines.
|
||||
type URI struct {
|
||||
noCopy noCopy //nolint:unused,structcheck
|
||||
noCopy noCopy
|
||||
|
||||
pathOriginal []byte
|
||||
scheme []byte
|
||||
|
@ -48,20 +48,10 @@ type URI struct {
|
|||
queryArgs Args
|
||||
parsedQueryArgs bool
|
||||
|
||||
// Path values are sent as-is without normalization
|
||||
//
|
||||
// Disabled path normalization may be useful for proxying incoming requests
|
||||
// to servers that are expecting paths to be forwarded as-is.
|
||||
//
|
||||
// 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
|
||||
h *RequestHeader
|
||||
}
|
||||
|
||||
// CopyTo copies uri contents to dst.
|
||||
|
@ -73,15 +63,13 @@ func (u *URI) CopyTo(dst *URI) {
|
|||
dst.queryString = append(dst.queryString[:0], u.queryString...)
|
||||
dst.hash = append(dst.hash[:0], u.hash...)
|
||||
dst.host = append(dst.host[:0], u.host...)
|
||||
dst.username = append(dst.username[:0], u.username...)
|
||||
dst.password = append(dst.password[:0], u.password...)
|
||||
|
||||
u.queryArgs.CopyTo(&dst.queryArgs)
|
||||
dst.parsedQueryArgs = u.parsedQueryArgs
|
||||
dst.DisablePathNormalizing = u.DisablePathNormalizing
|
||||
|
||||
// fullURI and requestURI shouldn't be copied, since they are created
|
||||
// from scratch on each FullURI() and RequestURI() call.
|
||||
dst.h = u.h
|
||||
}
|
||||
|
||||
// Hash returns URI hash, i.e. qwe of http://aaa.com/foo/bar?baz=123#qwe .
|
||||
|
@ -101,36 +89,6 @@ func (u *URI) SetHashBytes(hash []byte) {
|
|||
u.hash = append(u.hash[:0], hash...)
|
||||
}
|
||||
|
||||
// Username returns URI username
|
||||
func (u *URI) Username() []byte {
|
||||
return u.username
|
||||
}
|
||||
|
||||
// SetUsername sets URI username.
|
||||
func (u *URI) SetUsername(username string) {
|
||||
u.username = append(u.username[:0], username...)
|
||||
}
|
||||
|
||||
// SetUsernameBytes sets URI username.
|
||||
func (u *URI) SetUsernameBytes(username []byte) {
|
||||
u.username = append(u.username[:0], username...)
|
||||
}
|
||||
|
||||
// Password returns URI password
|
||||
func (u *URI) Password() []byte {
|
||||
return u.password
|
||||
}
|
||||
|
||||
// SetPassword sets URI password.
|
||||
func (u *URI) SetPassword(password string) {
|
||||
u.password = append(u.password[:0], password...)
|
||||
}
|
||||
|
||||
// SetPasswordBytes sets URI password.
|
||||
func (u *URI) SetPasswordBytes(password []byte) {
|
||||
u.password = append(u.password[:0], password...)
|
||||
}
|
||||
|
||||
// QueryString returns URI query string,
|
||||
// i.e. baz=123 of http://aaa.com/foo/bar?baz=123#qwe .
|
||||
//
|
||||
|
@ -216,25 +174,29 @@ func (u *URI) Reset() {
|
|||
u.path = u.path[:0]
|
||||
u.queryString = u.queryString[:0]
|
||||
u.hash = u.hash[:0]
|
||||
u.username = u.username[:0]
|
||||
u.password = u.password[:0]
|
||||
|
||||
u.host = u.host[:0]
|
||||
u.queryArgs.Reset()
|
||||
u.parsedQueryArgs = false
|
||||
u.DisablePathNormalizing = false
|
||||
|
||||
// There is no need in u.fullURI = u.fullURI[:0], since full uri
|
||||
// is calculated on each call to FullURI().
|
||||
// is calucalted on each call to FullURI().
|
||||
|
||||
// There is no need in u.requestURI = u.requestURI[:0], since requestURI
|
||||
// is calculated on each call to RequestURI().
|
||||
|
||||
u.h = nil
|
||||
}
|
||||
|
||||
// Host returns host part, i.e. aaa.com of http://aaa.com/foo/bar?baz=123#qwe .
|
||||
//
|
||||
// Host is always lowercased.
|
||||
func (u *URI) Host() []byte {
|
||||
if len(u.host) == 0 && u.h != nil {
|
||||
u.host = append(u.host[:0], u.h.Host()...)
|
||||
lowercaseBytes(u.host)
|
||||
u.h = nil
|
||||
}
|
||||
return u.host
|
||||
}
|
||||
|
||||
|
@ -257,37 +219,23 @@ func (u *URI) SetHostBytes(host []byte) {
|
|||
//
|
||||
// uri may contain e.g. RequestURI without scheme and host if host is non-empty.
|
||||
func (u *URI) Parse(host, uri []byte) {
|
||||
u.parse(host, uri, false)
|
||||
u.parse(host, uri, nil)
|
||||
}
|
||||
|
||||
func (u *URI) parse(host, uri []byte, isTLS bool) {
|
||||
u.Reset()
|
||||
|
||||
if len(host) == 0 || bytes.Contains(uri, strColonSlashSlash) {
|
||||
scheme, newHost, newURI := splitHostURI(host, uri)
|
||||
u.scheme = append(u.scheme, scheme...)
|
||||
lowercaseBytes(u.scheme)
|
||||
host = newHost
|
||||
uri = newURI
|
||||
}
|
||||
|
||||
func (u *URI) parseQuick(uri []byte, h *RequestHeader, isTLS bool) {
|
||||
u.parse(nil, uri, h)
|
||||
if isTLS {
|
||||
u.scheme = append(u.scheme[:0], strHTTPS...)
|
||||
}
|
||||
}
|
||||
|
||||
if n := bytes.Index(host, strAt); n >= 0 {
|
||||
auth := host[:n]
|
||||
host = host[n+1:]
|
||||
|
||||
if n := bytes.Index(auth, strColon); n >= 0 {
|
||||
u.username = auth[:n]
|
||||
u.password = auth[n+1:]
|
||||
} else {
|
||||
u.username = auth
|
||||
u.password = auth[:0] // Make sure it's not nil
|
||||
}
|
||||
}
|
||||
func (u *URI) parse(host, uri []byte, h *RequestHeader) {
|
||||
u.Reset()
|
||||
u.h = h
|
||||
|
||||
scheme, host, uri := splitHostURI(host, uri)
|
||||
u.scheme = append(u.scheme, scheme...)
|
||||
lowercaseBytes(u.scheme)
|
||||
u.host = append(u.host, host...)
|
||||
lowercaseBytes(u.host)
|
||||
|
||||
|
@ -388,12 +336,7 @@ func normalizePath(dst, src []byte) []byte {
|
|||
|
||||
// RequestURI returns RequestURI - i.e. URI without Scheme and Host.
|
||||
func (u *URI) RequestURI() []byte {
|
||||
var dst []byte
|
||||
if u.DisablePathNormalizing {
|
||||
dst = append(u.requestURI[:0], u.PathOriginal()...)
|
||||
} else {
|
||||
dst = appendQuotedPath(u.requestURI[:0], u.Path())
|
||||
}
|
||||
dst := appendQuotedPath(u.requestURI[:0], u.Path())
|
||||
if u.queryArgs.Len() > 0 {
|
||||
dst = append(dst, '?')
|
||||
dst = u.queryArgs.AppendBytes(dst)
|
||||
|
@ -401,6 +344,10 @@ func (u *URI) RequestURI() []byte {
|
|||
dst = append(dst, '?')
|
||||
dst = append(dst, u.queryString...)
|
||||
}
|
||||
if len(u.hash) > 0 {
|
||||
dst = append(dst, '#')
|
||||
dst = append(dst, u.hash...)
|
||||
}
|
||||
u.requestURI = dst
|
||||
return u.requestURI
|
||||
}
|
||||
|
@ -515,12 +462,7 @@ func (u *URI) FullURI() []byte {
|
|||
// AppendBytes appends full uri to dst and returns the extended dst.
|
||||
func (u *URI) AppendBytes(dst []byte) []byte {
|
||||
dst = u.appendSchemeHost(dst)
|
||||
dst = append(dst, u.RequestURI()...)
|
||||
if len(u.hash) > 0 {
|
||||
dst = append(dst, '#')
|
||||
dst = append(dst, u.hash...)
|
||||
}
|
||||
return dst
|
||||
return append(dst, u.RequestURI()...)
|
||||
}
|
||||
|
||||
func (u *URI) appendSchemeHost(dst []byte) []byte {
|
|
@ -16,7 +16,7 @@ import (
|
|||
type workerPool struct {
|
||||
// Function for serving server connections.
|
||||
// It must leave c unclosed.
|
||||
WorkerFunc ServeHandler
|
||||
WorkerFunc func(c net.Conn) error
|
||||
|
||||
MaxWorkersCount int
|
||||
|
||||
|
@ -35,8 +35,6 @@ type workerPool struct {
|
|||
stopCh chan struct{}
|
||||
|
||||
workerChanPool sync.Pool
|
||||
|
||||
connState func(net.Conn, ConnState)
|
||||
}
|
||||
|
||||
type workerChan struct {
|
||||
|
@ -50,11 +48,6 @@ func (wp *workerPool) Start() {
|
|||
}
|
||||
wp.stopCh = make(chan struct{})
|
||||
stopCh := wp.stopCh
|
||||
wp.workerChanPool.New = func() interface{} {
|
||||
return &workerChan{
|
||||
ch: make(chan net.Conn, workerChanCap),
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
var scratch []*workerChan
|
||||
for {
|
||||
|
@ -81,8 +74,8 @@ func (wp *workerPool) Stop() {
|
|||
// serving the connection and noticing wp.mustStop = true.
|
||||
wp.lock.Lock()
|
||||
ready := wp.ready
|
||||
for i := range ready {
|
||||
ready[i].ch <- nil
|
||||
for i, ch := range ready {
|
||||
ch.ch <- nil
|
||||
ready[i] = nil
|
||||
}
|
||||
wp.ready = ready[:0]
|
||||
|
@ -102,34 +95,23 @@ func (wp *workerPool) clean(scratch *[]*workerChan) {
|
|||
|
||||
// Clean least recently used workers if they didn't serve connections
|
||||
// for more than maxIdleWorkerDuration.
|
||||
criticalTime := time.Now().Add(-maxIdleWorkerDuration)
|
||||
currentTime := time.Now()
|
||||
|
||||
wp.lock.Lock()
|
||||
ready := wp.ready
|
||||
n := len(ready)
|
||||
|
||||
// Use binary-search algorithm to find out the index of the least recently worker which can be cleaned up.
|
||||
l, r, mid := 0, n-1, 0
|
||||
for l <= r {
|
||||
mid = (l + r) / 2
|
||||
if criticalTime.After(wp.ready[mid].lastUseTime) {
|
||||
l = mid + 1
|
||||
} else {
|
||||
r = mid - 1
|
||||
i := 0
|
||||
for i < n && currentTime.Sub(ready[i].lastUseTime) > maxIdleWorkerDuration {
|
||||
i++
|
||||
}
|
||||
*scratch = append((*scratch)[:0], ready[:i]...)
|
||||
if i > 0 {
|
||||
m := copy(ready, ready[i:])
|
||||
for i = m; i < n; i++ {
|
||||
ready[i] = nil
|
||||
}
|
||||
wp.ready = ready[:m]
|
||||
}
|
||||
i := r
|
||||
if i == -1 {
|
||||
wp.lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
*scratch = append((*scratch)[:0], ready[:i+1]...)
|
||||
m := copy(ready, ready[i+1:])
|
||||
for i = m; i < n; i++ {
|
||||
ready[i] = nil
|
||||
}
|
||||
wp.ready = ready[:m]
|
||||
wp.lock.Unlock()
|
||||
|
||||
// Notify obsolete workers to stop.
|
||||
|
@ -137,8 +119,8 @@ func (wp *workerPool) clean(scratch *[]*workerChan) {
|
|||
// may be blocking and may consume a lot of time if many workers
|
||||
// are located on non-local CPUs.
|
||||
tmp := *scratch
|
||||
for i := range tmp {
|
||||
tmp[i].ch <- nil
|
||||
for i, ch := range tmp {
|
||||
ch.ch <- nil
|
||||
tmp[i] = nil
|
||||
}
|
||||
}
|
||||
|
@ -190,6 +172,11 @@ func (wp *workerPool) getCh() *workerChan {
|
|||
return nil
|
||||
}
|
||||
vch := wp.workerChanPool.Get()
|
||||
if vch == nil {
|
||||
vch = &workerChan{
|
||||
ch: make(chan net.Conn, workerChanCap),
|
||||
}
|
||||
}
|
||||
ch = vch.(*workerChan)
|
||||
go func() {
|
||||
wp.workerFunc(ch)
|
||||
|
@ -200,7 +187,7 @@ func (wp *workerPool) getCh() *workerChan {
|
|||
}
|
||||
|
||||
func (wp *workerPool) release(ch *workerChan) bool {
|
||||
ch.lastUseTime = time.Now()
|
||||
ch.lastUseTime = CoarseTimeNow()
|
||||
wp.lock.Lock()
|
||||
if wp.mustStop {
|
||||
wp.lock.Unlock()
|
||||
|
@ -224,17 +211,12 @@ func (wp *workerPool) workerFunc(ch *workerChan) {
|
|||
errStr := err.Error()
|
||||
if wp.LogAllErrors || !(strings.Contains(errStr, "broken pipe") ||
|
||||
strings.Contains(errStr, "reset by peer") ||
|
||||
strings.Contains(errStr, "request headers: small read buffer") ||
|
||||
strings.Contains(errStr, "unexpected EOF") ||
|
||||
strings.Contains(errStr, "i/o timeout")) {
|
||||
wp.Logger.Printf("error when serving connection %q<->%q: %s", c.LocalAddr(), c.RemoteAddr(), err)
|
||||
}
|
||||
}
|
||||
if err == errHijacked {
|
||||
wp.connState(c, StateHijacked)
|
||||
} else {
|
||||
_ = c.Close()
|
||||
wp.connState(c, StateClosed)
|
||||
if err != errHijacked {
|
||||
c.Close()
|
||||
}
|
||||
c = nil
|
||||
|
56
vendor/github.com/valyala/fasthttp/.travis.yml
generated
vendored
56
vendor/github.com/valyala/fasthttp/.travis.yml
generated
vendored
|
@ -1,56 +0,0 @@
|
|||
language: go
|
||||
|
||||
# Docker is required for fuzzit regression tests
|
||||
services:
|
||||
- docker
|
||||
|
||||
dist: bionic
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
go:
|
||||
- tip
|
||||
- 1.14.x
|
||||
- 1.13.x
|
||||
- 1.12.x
|
||||
- 1.11.x
|
||||
- 1.10.x
|
||||
- 1.9.x
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- tip
|
||||
fast_finish: true
|
||||
|
||||
env:
|
||||
global:
|
||||
secure: "v/F0oI9zE9mcpEp4AVdHzSSHbe5ZFtH6B0i/BiUXKdQRQ10+JMPDOFRJQti7yxjMwltyd/QSFmR50Fl108sQYpo4xdlEXMHp2Y6OAN6crrp6PuHbLYgDWu3df/cH7/BqDyIq1uX8KZEeQssnygYN8hN4tpJCUg+NIb40Lm57Zsodt8DVjjyDWQQFDL7soNyAwGwQIqEyJsn+NUieXWEB1Qnt0xUtPIReuLlrwXR8wC1nLEjG9yz4ftDHHQdhVbO2b+xGWyaJ7QB5ixztaQP8Jnny6kSW9j6zEhJVuzdZ6d3xz23ibCbzSXBHdIUEI9u6ifQj8BYXr8fFS0FB3++IxgAYSs3ybZ+qEwuAxSBBm6YNW+3FrfDknVwTQscjKqnXPisjUqaRC9b31hke0tXzBq1488hE+wxMXeDM4LwWT5IMEO2gz0WGQXxmdVit72DIjCZxJkf1TvZZ0YH7Y//6wJTYYP9xulsy4gqu8CuFdWiF3fiGc3p5DTIS75nJ/Yy76Sa1pRPASKCujfLxtHE6Mt0XKvSolIXklYIzBkjN6vn80N6JIrqtqlimBGPW/Ec6+dwbmRe2AcOKRl4y7pZsGYhJhqdue1mucUYO/e2QeBZJGkqqG+zF5AW0v8x29BHvMwViAonc8o9eelkJ8khYzc/Qeq05pZnR/N/Pqfc+68k="
|
||||
|
||||
before_install:
|
||||
- go get -t -v ./...
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: test
|
||||
script:
|
||||
# build test for supported platforms
|
||||
- GOOS=linux go build
|
||||
- GOOS=darwin go build
|
||||
- GOOS=freebsd go build
|
||||
- GOOS=windows go build
|
||||
- GOARCH=386 go build
|
||||
|
||||
# run tests on a standard platform
|
||||
- go test -v ./...
|
||||
|
||||
# run tests with the race detector as well
|
||||
- go test -race -v ./...
|
||||
- stage: fuzzit.dev
|
||||
os:
|
||||
- linux
|
||||
go:
|
||||
- 1.14
|
||||
script:
|
||||
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then ./fuzzit.sh fuzzing; fi
|
||||
- if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then ./fuzzit.sh local-regression; fi
|
9
vendor/github.com/valyala/fasthttp/LICENSE
generated
vendored
9
vendor/github.com/valyala/fasthttp/LICENSE
generated
vendored
|
@ -1,9 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-present Aliaksandr Valialkin, VertaMedia, Kirill Danshin, Erik Dubbelboer, FastHTTP Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
590
vendor/github.com/valyala/fasthttp/README.md
generated
vendored
590
vendor/github.com/valyala/fasthttp/README.md
generated
vendored
|
@ -1,590 +0,0 @@
|
|||
# fasthttp [](https://travis-ci.org/valyala/fasthttp?branch=master) [](http://godoc.org/github.com/valyala/fasthttp) [](https://fuzzit.dev) [](https://goreportcard.com/report/github.com/valyala/fasthttp) [](https://sourcegraph.com/github.com/valyala/fasthttp?badge)
|
||||
|
||||

|
||||
|
||||
Fast HTTP implementation for Go.
|
||||
|
||||
Currently fasthttp is successfully used by [VertaMedia](https://vertamedia.com/)
|
||||
in a production serving up to 200K rps from more than 1.5M concurrent keep-alive
|
||||
connections per physical server.
|
||||
|
||||
[TechEmpower Benchmark round 18 results](https://www.techempower.com/benchmarks/#section=data-r18&hw=ph&test=plaintext)
|
||||
|
||||
[Server Benchmarks](#http-server-performance-comparison-with-nethttp)
|
||||
|
||||
[Client Benchmarks](#http-client-comparison-with-nethttp)
|
||||
|
||||
[Install](#install)
|
||||
|
||||
[Documentation](https://godoc.org/github.com/valyala/fasthttp)
|
||||
|
||||
[Examples from docs](https://godoc.org/github.com/valyala/fasthttp#pkg-examples)
|
||||
|
||||
[Code examples](examples)
|
||||
|
||||
[Awesome fasthttp tools](https://github.com/fasthttp)
|
||||
|
||||
[Switching from net/http to fasthttp](#switching-from-nethttp-to-fasthttp)
|
||||
|
||||
[Fasthttp best practices](#fasthttp-best-practices)
|
||||
|
||||
[Tricks with byte buffers](#tricks-with-byte-buffers)
|
||||
|
||||
[Related projects](#related-projects)
|
||||
|
||||
[FAQ](#faq)
|
||||
|
||||
# HTTP server performance comparison with [net/http](https://golang.org/pkg/net/http/)
|
||||
|
||||
In short, fasthttp server is up to 10 times faster than net/http.
|
||||
Below are benchmark results.
|
||||
|
||||
*GOMAXPROCS=1*
|
||||
|
||||
net/http server:
|
||||
```
|
||||
$ GOMAXPROCS=1 go test -bench=NetHTTPServerGet -benchmem -benchtime=10s
|
||||
BenchmarkNetHTTPServerGet1ReqPerConn 1000000 12052 ns/op 2297 B/op 29 allocs/op
|
||||
BenchmarkNetHTTPServerGet2ReqPerConn 1000000 12278 ns/op 2327 B/op 24 allocs/op
|
||||
BenchmarkNetHTTPServerGet10ReqPerConn 2000000 8903 ns/op 2112 B/op 19 allocs/op
|
||||
BenchmarkNetHTTPServerGet10KReqPerConn 2000000 8451 ns/op 2058 B/op 18 allocs/op
|
||||
BenchmarkNetHTTPServerGet1ReqPerConn10KClients 500000 26733 ns/op 3229 B/op 29 allocs/op
|
||||
BenchmarkNetHTTPServerGet2ReqPerConn10KClients 1000000 23351 ns/op 3211 B/op 24 allocs/op
|
||||
BenchmarkNetHTTPServerGet10ReqPerConn10KClients 1000000 13390 ns/op 2483 B/op 19 allocs/op
|
||||
BenchmarkNetHTTPServerGet100ReqPerConn10KClients 1000000 13484 ns/op 2171 B/op 18 allocs/op
|
||||
```
|
||||
|
||||
fasthttp server:
|
||||
```
|
||||
$ GOMAXPROCS=1 go test -bench=kServerGet -benchmem -benchtime=10s
|
||||
BenchmarkServerGet1ReqPerConn 10000000 1559 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkServerGet2ReqPerConn 10000000 1248 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkServerGet10ReqPerConn 20000000 797 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkServerGet10KReqPerConn 20000000 716 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkServerGet1ReqPerConn10KClients 10000000 1974 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkServerGet2ReqPerConn10KClients 10000000 1352 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkServerGet10ReqPerConn10KClients 20000000 789 ns/op 2 B/op 0 allocs/op
|
||||
BenchmarkServerGet100ReqPerConn10KClients 20000000 604 ns/op 0 B/op 0 allocs/op
|
||||
```
|
||||
|
||||
*GOMAXPROCS=4*
|
||||
|
||||
net/http server:
|
||||
```
|
||||
$ GOMAXPROCS=4 go test -bench=NetHTTPServerGet -benchmem -benchtime=10s
|
||||
BenchmarkNetHTTPServerGet1ReqPerConn-4 3000000 4529 ns/op 2389 B/op 29 allocs/op
|
||||
BenchmarkNetHTTPServerGet2ReqPerConn-4 5000000 3896 ns/op 2418 B/op 24 allocs/op
|
||||
BenchmarkNetHTTPServerGet10ReqPerConn-4 5000000 3145 ns/op 2160 B/op 19 allocs/op
|
||||
BenchmarkNetHTTPServerGet10KReqPerConn-4 5000000 3054 ns/op 2065 B/op 18 allocs/op
|
||||
BenchmarkNetHTTPServerGet1ReqPerConn10KClients-4 1000000 10321 ns/op 3710 B/op 30 allocs/op
|
||||
BenchmarkNetHTTPServerGet2ReqPerConn10KClients-4 2000000 7556 ns/op 3296 B/op 24 allocs/op
|
||||
BenchmarkNetHTTPServerGet10ReqPerConn10KClients-4 5000000 3905 ns/op 2349 B/op 19 allocs/op
|
||||
BenchmarkNetHTTPServerGet100ReqPerConn10KClients-4 5000000 3435 ns/op 2130 B/op 18 allocs/op
|
||||
```
|
||||
|
||||
fasthttp server:
|
||||
```
|
||||
$ GOMAXPROCS=4 go test -bench=kServerGet -benchmem -benchtime=10s
|
||||
BenchmarkServerGet1ReqPerConn-4 10000000 1141 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkServerGet2ReqPerConn-4 20000000 707 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkServerGet10ReqPerConn-4 30000000 341 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkServerGet10KReqPerConn-4 50000000 310 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkServerGet1ReqPerConn10KClients-4 10000000 1119 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkServerGet2ReqPerConn10KClients-4 20000000 644 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkServerGet10ReqPerConn10KClients-4 30000000 346 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkServerGet100ReqPerConn10KClients-4 50000000 282 ns/op 0 B/op 0 allocs/op
|
||||
```
|
||||
|
||||
# HTTP client comparison with net/http
|
||||
|
||||
In short, fasthttp client is up to 10 times faster than net/http.
|
||||
Below are benchmark results.
|
||||
|
||||
*GOMAXPROCS=1*
|
||||
|
||||
net/http client:
|
||||
```
|
||||
$ GOMAXPROCS=1 go test -bench='HTTPClient(Do|GetEndToEnd)' -benchmem -benchtime=10s
|
||||
BenchmarkNetHTTPClientDoFastServer 1000000 12567 ns/op 2616 B/op 35 allocs/op
|
||||
BenchmarkNetHTTPClientGetEndToEnd1TCP 200000 67030 ns/op 5028 B/op 56 allocs/op
|
||||
BenchmarkNetHTTPClientGetEndToEnd10TCP 300000 51098 ns/op 5031 B/op 56 allocs/op
|
||||
BenchmarkNetHTTPClientGetEndToEnd100TCP 300000 45096 ns/op 5026 B/op 55 allocs/op
|
||||
BenchmarkNetHTTPClientGetEndToEnd1Inmemory 500000 24779 ns/op 5035 B/op 57 allocs/op
|
||||
BenchmarkNetHTTPClientGetEndToEnd10Inmemory 1000000 26425 ns/op 5035 B/op 57 allocs/op
|
||||
BenchmarkNetHTTPClientGetEndToEnd100Inmemory 500000 28515 ns/op 5045 B/op 57 allocs/op
|
||||
BenchmarkNetHTTPClientGetEndToEnd1000Inmemory 500000 39511 ns/op 5096 B/op 56 allocs/op
|
||||
```
|
||||
|
||||
fasthttp client:
|
||||
```
|
||||
$ GOMAXPROCS=1 go test -bench='kClient(Do|GetEndToEnd)' -benchmem -benchtime=10s
|
||||
BenchmarkClientDoFastServer 20000000 865 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkClientGetEndToEnd1TCP 1000000 18711 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkClientGetEndToEnd10TCP 1000000 14664 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkClientGetEndToEnd100TCP 1000000 14043 ns/op 1 B/op 0 allocs/op
|
||||
BenchmarkClientGetEndToEnd1Inmemory 5000000 3965 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkClientGetEndToEnd10Inmemory 3000000 4060 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkClientGetEndToEnd100Inmemory 5000000 3396 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkClientGetEndToEnd1000Inmemory 5000000 3306 ns/op 2 B/op 0 allocs/op
|
||||
```
|
||||
|
||||
*GOMAXPROCS=4*
|
||||
|
||||
net/http client:
|
||||
```
|
||||
$ GOMAXPROCS=4 go test -bench='HTTPClient(Do|GetEndToEnd)' -benchmem -benchtime=10s
|
||||
BenchmarkNetHTTPClientDoFastServer-4 2000000 8774 ns/op 2619 B/op 35 allocs/op
|
||||
BenchmarkNetHTTPClientGetEndToEnd1TCP-4 500000 22951 ns/op 5047 B/op 56 allocs/op
|
||||
BenchmarkNetHTTPClientGetEndToEnd10TCP-4 1000000 19182 ns/op 5037 B/op 55 allocs/op
|
||||
BenchmarkNetHTTPClientGetEndToEnd100TCP-4 1000000 16535 ns/op 5031 B/op 55 allocs/op
|
||||
BenchmarkNetHTTPClientGetEndToEnd1Inmemory-4 1000000 14495 ns/op 5038 B/op 56 allocs/op
|
||||
BenchmarkNetHTTPClientGetEndToEnd10Inmemory-4 1000000 10237 ns/op 5034 B/op 56 allocs/op
|
||||
BenchmarkNetHTTPClientGetEndToEnd100Inmemory-4 1000000 10125 ns/op 5045 B/op 56 allocs/op
|
||||
BenchmarkNetHTTPClientGetEndToEnd1000Inmemory-4 1000000 11132 ns/op 5136 B/op 56 allocs/op
|
||||
```
|
||||
|
||||
fasthttp client:
|
||||
```
|
||||
$ GOMAXPROCS=4 go test -bench='kClient(Do|GetEndToEnd)' -benchmem -benchtime=10s
|
||||
BenchmarkClientDoFastServer-4 50000000 397 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkClientGetEndToEnd1TCP-4 2000000 7388 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkClientGetEndToEnd10TCP-4 2000000 6689 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkClientGetEndToEnd100TCP-4 3000000 4927 ns/op 1 B/op 0 allocs/op
|
||||
BenchmarkClientGetEndToEnd1Inmemory-4 10000000 1604 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkClientGetEndToEnd10Inmemory-4 10000000 1458 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkClientGetEndToEnd100Inmemory-4 10000000 1329 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkClientGetEndToEnd1000Inmemory-4 10000000 1316 ns/op 5 B/op 0 allocs/op
|
||||
```
|
||||
|
||||
|
||||
# Install
|
||||
|
||||
```
|
||||
go get -u github.com/valyala/fasthttp
|
||||
```
|
||||
|
||||
|
||||
# Switching from net/http to fasthttp
|
||||
|
||||
Unfortunately, fasthttp doesn't provide API identical to net/http.
|
||||
See the [FAQ](#faq) for details.
|
||||
There is [net/http -> fasthttp handler converter](https://godoc.org/github.com/valyala/fasthttp/fasthttpadaptor),
|
||||
but it is better to write fasthttp request handlers by hand in order to use
|
||||
all of the fasthttp advantages (especially high performance :) ).
|
||||
|
||||
Important points:
|
||||
|
||||
* Fasthttp works with [RequestHandler functions](https://godoc.org/github.com/valyala/fasthttp#RequestHandler)
|
||||
instead of objects implementing [Handler interface](https://golang.org/pkg/net/http/#Handler).
|
||||
Fortunately, it is easy to pass bound struct methods to fasthttp:
|
||||
|
||||
```go
|
||||
type MyHandler struct {
|
||||
foobar string
|
||||
}
|
||||
|
||||
// request handler in net/http style, i.e. method bound to MyHandler struct.
|
||||
func (h *MyHandler) HandleFastHTTP(ctx *fasthttp.RequestCtx) {
|
||||
// notice that we may access MyHandler properties here - see h.foobar.
|
||||
fmt.Fprintf(ctx, "Hello, world! Requested path is %q. Foobar is %q",
|
||||
ctx.Path(), h.foobar)
|
||||
}
|
||||
|
||||
// request handler in fasthttp style, i.e. just plain function.
|
||||
func fastHTTPHandler(ctx *fasthttp.RequestCtx) {
|
||||
fmt.Fprintf(ctx, "Hi there! RequestURI is %q", ctx.RequestURI())
|
||||
}
|
||||
|
||||
// pass bound struct method to fasthttp
|
||||
myHandler := &MyHandler{
|
||||
foobar: "foobar",
|
||||
}
|
||||
fasthttp.ListenAndServe(":8080", myHandler.HandleFastHTTP)
|
||||
|
||||
// pass plain function to fasthttp
|
||||
fasthttp.ListenAndServe(":8081", fastHTTPHandler)
|
||||
```
|
||||
|
||||
* The [RequestHandler](https://godoc.org/github.com/valyala/fasthttp#RequestHandler)
|
||||
accepts only one argument - [RequestCtx](https://godoc.org/github.com/valyala/fasthttp#RequestCtx).
|
||||
It contains all the functionality required for http request processing
|
||||
and response writing. Below is an example of a simple request handler conversion
|
||||
from net/http to fasthttp.
|
||||
|
||||
```go
|
||||
// net/http request handler
|
||||
requestHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/foo":
|
||||
fooHandler(w, r)
|
||||
case "/bar":
|
||||
barHandler(w, r)
|
||||
default:
|
||||
http.Error(w, "Unsupported path", http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// the corresponding fasthttp request handler
|
||||
requestHandler := func(ctx *fasthttp.RequestCtx) {
|
||||
switch string(ctx.Path()) {
|
||||
case "/foo":
|
||||
fooHandler(ctx)
|
||||
case "/bar":
|
||||
barHandler(ctx)
|
||||
default:
|
||||
ctx.Error("Unsupported path", fasthttp.StatusNotFound)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* Fasthttp allows setting response headers and writing response body
|
||||
in an arbitrary order. There is no 'headers first, then body' restriction
|
||||
like in net/http. The following code is valid for fasthttp:
|
||||
|
||||
```go
|
||||
requestHandler := func(ctx *fasthttp.RequestCtx) {
|
||||
// set some headers and status code first
|
||||
ctx.SetContentType("foo/bar")
|
||||
ctx.SetStatusCode(fasthttp.StatusOK)
|
||||
|
||||
// then write the first part of body
|
||||
fmt.Fprintf(ctx, "this is the first part of body\n")
|
||||
|
||||
// then set more headers
|
||||
ctx.Response.Header.Set("Foo-Bar", "baz")
|
||||
|
||||
// then write more body
|
||||
fmt.Fprintf(ctx, "this is the second part of body\n")
|
||||
|
||||
// then override already written body
|
||||
ctx.SetBody([]byte("this is completely new body contents"))
|
||||
|
||||
// then update status code
|
||||
ctx.SetStatusCode(fasthttp.StatusNotFound)
|
||||
|
||||
// basically, anything may be updated many times before
|
||||
// returning from RequestHandler.
|
||||
//
|
||||
// Unlike net/http fasthttp doesn't put response to the wire until
|
||||
// returning from RequestHandler.
|
||||
}
|
||||
```
|
||||
|
||||
* Fasthttp doesn't provide [ServeMux](https://golang.org/pkg/net/http/#ServeMux),
|
||||
but there are more powerful third-party routers and web frameworks
|
||||
with fasthttp support:
|
||||
|
||||
* [fasthttp-routing](https://github.com/qiangxue/fasthttp-routing)
|
||||
* [fasthttprouter](https://github.com/buaazp/fasthttprouter)
|
||||
* [lu](https://github.com/vincentLiuxiang/lu)
|
||||
* [atreugo](https://github.com/savsgio/atreugo)
|
||||
* [Fiber](https://github.com/gofiber/fiber)
|
||||
|
||||
Net/http code with simple ServeMux is trivially converted to fasthttp code:
|
||||
|
||||
```go
|
||||
// net/http code
|
||||
|
||||
m := &http.ServeMux{}
|
||||
m.HandleFunc("/foo", fooHandlerFunc)
|
||||
m.HandleFunc("/bar", barHandlerFunc)
|
||||
m.Handle("/baz", bazHandler)
|
||||
|
||||
http.ListenAndServe(":80", m)
|
||||
```
|
||||
|
||||
```go
|
||||
// the corresponding fasthttp code
|
||||
m := func(ctx *fasthttp.RequestCtx) {
|
||||
switch string(ctx.Path()) {
|
||||
case "/foo":
|
||||
fooHandlerFunc(ctx)
|
||||
case "/bar":
|
||||
barHandlerFunc(ctx)
|
||||
case "/baz":
|
||||
bazHandler.HandlerFunc(ctx)
|
||||
default:
|
||||
ctx.Error("not found", fasthttp.StatusNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
fasthttp.ListenAndServe(":80", m)
|
||||
```
|
||||
|
||||
* net/http -> fasthttp conversion table:
|
||||
|
||||
* All the pseudocode below assumes w, r and ctx have these types:
|
||||
```go
|
||||
var (
|
||||
w http.ResponseWriter
|
||||
r *http.Request
|
||||
ctx *fasthttp.RequestCtx
|
||||
)
|
||||
```
|
||||
* r.Body -> [ctx.PostBody()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.PostBody)
|
||||
* r.URL.Path -> [ctx.Path()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.Path)
|
||||
* r.URL -> [ctx.URI()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.URI)
|
||||
* r.Method -> [ctx.Method()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.Method)
|
||||
* r.Header -> [ctx.Request.Header](https://godoc.org/github.com/valyala/fasthttp#RequestHeader)
|
||||
* r.Header.Get() -> [ctx.Request.Header.Peek()](https://godoc.org/github.com/valyala/fasthttp#RequestHeader.Peek)
|
||||
* r.Host -> [ctx.Host()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.Host)
|
||||
* r.Form -> [ctx.QueryArgs()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.QueryArgs) +
|
||||
[ctx.PostArgs()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.PostArgs)
|
||||
* r.PostForm -> [ctx.PostArgs()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.PostArgs)
|
||||
* r.FormValue() -> [ctx.FormValue()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.FormValue)
|
||||
* r.FormFile() -> [ctx.FormFile()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.FormFile)
|
||||
* r.MultipartForm -> [ctx.MultipartForm()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.MultipartForm)
|
||||
* r.RemoteAddr -> [ctx.RemoteAddr()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.RemoteAddr)
|
||||
* r.RequestURI -> [ctx.RequestURI()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.RequestURI)
|
||||
* r.TLS -> [ctx.IsTLS()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.IsTLS)
|
||||
* r.Cookie() -> [ctx.Request.Header.Cookie()](https://godoc.org/github.com/valyala/fasthttp#RequestHeader.Cookie)
|
||||
* r.Referer() -> [ctx.Referer()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.Referer)
|
||||
* r.UserAgent() -> [ctx.UserAgent()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.UserAgent)
|
||||
* w.Header() -> [ctx.Response.Header](https://godoc.org/github.com/valyala/fasthttp#ResponseHeader)
|
||||
* w.Header().Set() -> [ctx.Response.Header.Set()](https://godoc.org/github.com/valyala/fasthttp#ResponseHeader.Set)
|
||||
* w.Header().Set("Content-Type") -> [ctx.SetContentType()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.SetContentType)
|
||||
* w.Header().Set("Set-Cookie") -> [ctx.Response.Header.SetCookie()](https://godoc.org/github.com/valyala/fasthttp#ResponseHeader.SetCookie)
|
||||
* w.Write() -> [ctx.Write()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.Write),
|
||||
[ctx.SetBody()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.SetBody),
|
||||
[ctx.SetBodyStream()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.SetBodyStream),
|
||||
[ctx.SetBodyStreamWriter()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.SetBodyStreamWriter)
|
||||
* w.WriteHeader() -> [ctx.SetStatusCode()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.SetStatusCode)
|
||||
* w.(http.Hijacker).Hijack() -> [ctx.Hijack()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.Hijack)
|
||||
* http.Error() -> [ctx.Error()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.Error)
|
||||
* http.FileServer() -> [fasthttp.FSHandler()](https://godoc.org/github.com/valyala/fasthttp#FSHandler),
|
||||
[fasthttp.FS](https://godoc.org/github.com/valyala/fasthttp#FS)
|
||||
* http.ServeFile() -> [fasthttp.ServeFile()](https://godoc.org/github.com/valyala/fasthttp#ServeFile)
|
||||
* http.Redirect() -> [ctx.Redirect()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.Redirect)
|
||||
* http.NotFound() -> [ctx.NotFound()](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.NotFound)
|
||||
* http.StripPrefix() -> [fasthttp.PathRewriteFunc](https://godoc.org/github.com/valyala/fasthttp#PathRewriteFunc)
|
||||
|
||||
* *VERY IMPORTANT!* Fasthttp disallows holding references
|
||||
to [RequestCtx](https://godoc.org/github.com/valyala/fasthttp#RequestCtx) or to its'
|
||||
members after returning from [RequestHandler](https://godoc.org/github.com/valyala/fasthttp#RequestHandler).
|
||||
Otherwise [data races](http://blog.golang.org/race-detector) are inevitable.
|
||||
Carefully inspect all the net/http request handlers converted to fasthttp whether
|
||||
they retain references to RequestCtx or to its' members after returning.
|
||||
RequestCtx provides the following _band aids_ for this case:
|
||||
|
||||
* Wrap RequestHandler into [TimeoutHandler](https://godoc.org/github.com/valyala/fasthttp#TimeoutHandler).
|
||||
* Call [TimeoutError](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.TimeoutError)
|
||||
before returning from RequestHandler if there are references to RequestCtx or to its' members.
|
||||
See [the example](https://godoc.org/github.com/valyala/fasthttp#example-RequestCtx-TimeoutError)
|
||||
for more details.
|
||||
|
||||
Use this brilliant tool - [race detector](http://blog.golang.org/race-detector) -
|
||||
for detecting and eliminating data races in your program. If you detected
|
||||
data race related to fasthttp in your program, then there is high probability
|
||||
you forgot calling [TimeoutError](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.TimeoutError)
|
||||
before returning from [RequestHandler](https://godoc.org/github.com/valyala/fasthttp#RequestHandler).
|
||||
|
||||
* Blind switching from net/http to fasthttp won't give you performance boost.
|
||||
While fasthttp is optimized for speed, its' performance may be easily saturated
|
||||
by slow [RequestHandler](https://godoc.org/github.com/valyala/fasthttp#RequestHandler).
|
||||
So [profile](http://blog.golang.org/profiling-go-programs) and optimize your
|
||||
code after switching to fasthttp. For instance, use [quicktemplate](https://github.com/valyala/quicktemplate)
|
||||
instead of [html/template](https://golang.org/pkg/html/template/).
|
||||
|
||||
* See also [fasthttputil](https://godoc.org/github.com/valyala/fasthttp/fasthttputil),
|
||||
[fasthttpadaptor](https://godoc.org/github.com/valyala/fasthttp/fasthttpadaptor) and
|
||||
[expvarhandler](https://godoc.org/github.com/valyala/fasthttp/expvarhandler).
|
||||
|
||||
|
||||
# Performance optimization tips for multi-core systems
|
||||
|
||||
* Use [reuseport](https://godoc.org/github.com/valyala/fasthttp/reuseport) listener.
|
||||
* Run a separate server instance per CPU core with GOMAXPROCS=1.
|
||||
* Pin each server instance to a separate CPU core using [taskset](http://linux.die.net/man/1/taskset).
|
||||
* Ensure the interrupts of multiqueue network card are evenly distributed between CPU cores.
|
||||
See [this article](https://blog.cloudflare.com/how-to-achieve-low-latency/) for details.
|
||||
* Use Go 1.13 as it provides some considerable performance improvements.
|
||||
|
||||
|
||||
# Fasthttp best practices
|
||||
|
||||
* Do not allocate objects and `[]byte` buffers - just reuse them as much
|
||||
as possible. Fasthttp API design encourages this.
|
||||
* [sync.Pool](https://golang.org/pkg/sync/#Pool) is your best friend.
|
||||
* [Profile your program](http://blog.golang.org/profiling-go-programs)
|
||||
in production.
|
||||
`go tool pprof --alloc_objects your-program mem.pprof` usually gives better
|
||||
insights for optimization opportunities than `go tool pprof your-program cpu.pprof`.
|
||||
* Write [tests and benchmarks](https://golang.org/pkg/testing/) for hot paths.
|
||||
* Avoid conversion between `[]byte` and `string`, since this may result in memory
|
||||
allocation+copy. Fasthttp API provides functions for both `[]byte` and `string` -
|
||||
use these functions instead of converting manually between `[]byte` and `string`.
|
||||
There are some exceptions - see [this wiki page](https://github.com/golang/go/wiki/CompilerOptimizations#string-and-byte)
|
||||
for more details.
|
||||
* Verify your tests and production code under
|
||||
[race detector](https://golang.org/doc/articles/race_detector.html) on a regular basis.
|
||||
* Prefer [quicktemplate](https://github.com/valyala/quicktemplate) instead of
|
||||
[html/template](https://golang.org/pkg/html/template/) in your webserver.
|
||||
|
||||
|
||||
# Tricks with `[]byte` buffers
|
||||
|
||||
The following tricks are used by fasthttp. Use them in your code too.
|
||||
|
||||
* Standard Go functions accept nil buffers
|
||||
```go
|
||||
var (
|
||||
// both buffers are uninitialized
|
||||
dst []byte
|
||||
src []byte
|
||||
)
|
||||
dst = append(dst, src...) // is legal if dst is nil and/or src is nil
|
||||
copy(dst, src) // is legal if dst is nil and/or src is nil
|
||||
(string(src) == "") // is true if src is nil
|
||||
(len(src) == 0) // is true if src is nil
|
||||
src = src[:0] // works like a charm with nil src
|
||||
|
||||
// this for loop doesn't panic if src is nil
|
||||
for i, ch := range src {
|
||||
doSomething(i, ch)
|
||||
}
|
||||
```
|
||||
|
||||
So throw away nil checks for `[]byte` buffers from you code. For example,
|
||||
```go
|
||||
srcLen := 0
|
||||
if src != nil {
|
||||
srcLen = len(src)
|
||||
}
|
||||
```
|
||||
|
||||
becomes
|
||||
|
||||
```go
|
||||
srcLen := len(src)
|
||||
```
|
||||
|
||||
* String may be appended to `[]byte` buffer with `append`
|
||||
```go
|
||||
dst = append(dst, "foobar"...)
|
||||
```
|
||||
|
||||
* `[]byte` buffer may be extended to its' capacity.
|
||||
```go
|
||||
buf := make([]byte, 100)
|
||||
a := buf[:10] // len(a) == 10, cap(a) == 100.
|
||||
b := a[:100] // is valid, since cap(a) == 100.
|
||||
```
|
||||
|
||||
* All fasthttp functions accept nil `[]byte` buffer
|
||||
```go
|
||||
statusCode, body, err := fasthttp.Get(nil, "http://google.com/")
|
||||
uintBuf := fasthttp.AppendUint(nil, 1234)
|
||||
```
|
||||
|
||||
# Related projects
|
||||
|
||||
* [fasthttp](https://github.com/fasthttp) - various useful
|
||||
helpers for projects based on fasthttp.
|
||||
* [fasthttp-routing](https://github.com/qiangxue/fasthttp-routing) - fast and
|
||||
powerful routing package for fasthttp servers.
|
||||
* [fasthttprouter](https://github.com/buaazp/fasthttprouter) - a high
|
||||
performance fasthttp request router that scales well.
|
||||
* [fastws](https://github.com/fasthttp/fastws) - Bloatless WebSocket package made for fasthttp
|
||||
to handle Read/Write operations concurrently.
|
||||
* [gramework](https://github.com/gramework/gramework) - a web framework made by one of fasthttp maintainers
|
||||
* [lu](https://github.com/vincentLiuxiang/lu) - a high performance
|
||||
go middleware web framework which is based on fasthttp.
|
||||
* [websocket](https://github.com/fasthttp/websocket) - Gorilla-based
|
||||
websocket implementation for fasthttp.
|
||||
* [fasthttpsession](https://github.com/phachon/fasthttpsession) - a fast and powerful session package for fasthttp servers.
|
||||
* [atreugo](https://github.com/savsgio/atreugo) - High performance and extensible micro web framework with zero memory allocations in hot paths.
|
||||
* [kratgo](https://github.com/savsgio/kratgo) - Simple, lightweight and ultra-fast HTTP Cache to speed up your websites.
|
||||
* [kit-plugins](https://github.com/wencan/kit-plugins/tree/master/transport/fasthttp) - go-kit transport implementation for fasthttp.
|
||||
* [Fiber](https://github.com/gofiber/fiber) - An Expressjs inspired web framework running on Fasthttp
|
||||
|
||||
|
||||
# FAQ
|
||||
|
||||
* *Why creating yet another http package instead of optimizing net/http?*
|
||||
|
||||
Because net/http API limits many optimization opportunities.
|
||||
For example:
|
||||
* net/http Request object lifetime isn't limited by request handler execution
|
||||
time. So the server must create a new request object per each request instead
|
||||
of reusing existing objects like fasthttp does.
|
||||
* net/http headers are stored in a `map[string][]string`. So the server
|
||||
must parse all the headers, convert them from `[]byte` to `string` and put
|
||||
them into the map before calling user-provided request handler.
|
||||
This all requires unnecessary memory allocations avoided by fasthttp.
|
||||
* net/http client API requires creating a new response object per each request.
|
||||
|
||||
* *Why fasthttp API is incompatible with net/http?*
|
||||
|
||||
Because net/http API limits many optimization opportunities. See the answer
|
||||
above for more details. Also certain net/http API parts are suboptimal
|
||||
for use:
|
||||
* Compare [net/http connection hijacking](https://golang.org/pkg/net/http/#Hijacker)
|
||||
to [fasthttp connection hijacking](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.Hijack).
|
||||
* Compare [net/http Request.Body reading](https://golang.org/pkg/net/http/#Request)
|
||||
to [fasthttp request body reading](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.PostBody).
|
||||
|
||||
* *Why fasthttp doesn't support HTTP/2.0 and WebSockets?*
|
||||
|
||||
[HTTP/2.0 support](https://github.com/fasthttp/http2) is in progress. [WebSockets](https://github.com/fasthttp/websockets) has been done already.
|
||||
Third parties also may use [RequestCtx.Hijack](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.Hijack)
|
||||
for implementing these goodies.
|
||||
|
||||
* *Are there known net/http advantages comparing to fasthttp?*
|
||||
|
||||
Yes:
|
||||
* net/http supports [HTTP/2.0 starting from go1.6](https://http2.golang.org/).
|
||||
* net/http API is stable, while fasthttp API constantly evolves.
|
||||
* net/http handles more HTTP corner cases.
|
||||
* net/http should contain less bugs, since it is used and tested by much
|
||||
wider audience.
|
||||
* net/http works on Go older than 1.5.
|
||||
|
||||
* *Why fasthttp API prefers returning `[]byte` instead of `string`?*
|
||||
|
||||
Because `[]byte` to `string` conversion isn't free - it requires memory
|
||||
allocation and copy. Feel free wrapping returned `[]byte` result into
|
||||
`string()` if you prefer working with strings instead of byte slices.
|
||||
But be aware that this has non-zero overhead.
|
||||
|
||||
* *Which GO versions are supported by fasthttp?*
|
||||
|
||||
Go1.5+. Older versions won't be supported, since their standard package
|
||||
[miss useful functions](https://github.com/valyala/fasthttp/issues/5).
|
||||
|
||||
**NOTE**: Go 1.9.7 is the oldest tested version. We recommend you to update as soon as you can. As of 1.11.3 we will drop 1.9.x support.
|
||||
|
||||
* *Please provide real benchmark data and server information*
|
||||
|
||||
See [this issue](https://github.com/valyala/fasthttp/issues/4).
|
||||
|
||||
* *Are there plans to add request routing to fasthttp?*
|
||||
|
||||
There are no plans to add request routing into fasthttp.
|
||||
Use third-party routers and web frameworks with fasthttp support:
|
||||
|
||||
* [fasthttp-routing](https://github.com/qiangxue/fasthttp-routing)
|
||||
* [fasthttprouter](https://github.com/buaazp/fasthttprouter)
|
||||
* [gramework](https://github.com/gramework/gramework)
|
||||
* [lu](https://github.com/vincentLiuxiang/lu)
|
||||
* [atreugo](https://github.com/savsgio/atreugo)
|
||||
* [Fiber](https://github.com/gofiber/fiber)
|
||||
|
||||
See also [this issue](https://github.com/valyala/fasthttp/issues/9) for more info.
|
||||
|
||||
* *I detected data race in fasthttp!*
|
||||
|
||||
Cool! [File a bug](https://github.com/valyala/fasthttp/issues/new). But before
|
||||
doing this check the following in your code:
|
||||
|
||||
* Make sure there are no references to [RequestCtx](https://godoc.org/github.com/valyala/fasthttp#RequestCtx)
|
||||
or to its' members after returning from [RequestHandler](https://godoc.org/github.com/valyala/fasthttp#RequestHandler).
|
||||
* Make sure you call [TimeoutError](https://godoc.org/github.com/valyala/fasthttp#RequestCtx.TimeoutError)
|
||||
before returning from [RequestHandler](https://godoc.org/github.com/valyala/fasthttp#RequestHandler)
|
||||
if there are references to [RequestCtx](https://godoc.org/github.com/valyala/fasthttp#RequestCtx)
|
||||
or to its' members, which may be accessed by other goroutines.
|
||||
|
||||
* *I didn't find an answer for my question here*
|
||||
|
||||
Try exploring [these questions](https://github.com/valyala/fasthttp/issues?q=label%3Aquestion).
|
115
vendor/github.com/valyala/fasthttp/SECURITY.md
generated
vendored
115
vendor/github.com/valyala/fasthttp/SECURITY.md
generated
vendored
|
@ -1,115 +0,0 @@
|
|||
### TL;DR
|
||||
|
||||
We use a simplified version of [Golang Security Policy](https://golang.org/security).
|
||||
For example, for now we skip CVE assignment.
|
||||
|
||||
### Reporting a Security Bug
|
||||
|
||||
Please report to us any issues you find. This document explains how to do that and what to expect in return.
|
||||
|
||||
All security bugs in our releases should be reported by email to oss-security@highload.solutions.
|
||||
This mail is delivered to a small security team.
|
||||
Your email will be acknowledged within 24 hours, and you'll receive a more detailed response
|
||||
to your email within 72 hours indicating the next steps in handling your report.
|
||||
For critical problems, you can encrypt your report using our PGP key (listed below).
|
||||
|
||||
Please use a descriptive subject line for your report email.
|
||||
After the initial reply to your report, the security team will
|
||||
endeavor to keep you informed of the progress being made towards a fix and full announcement.
|
||||
These updates will be sent at least every five days.
|
||||
In reality, this is more likely to be every 24-48 hours.
|
||||
|
||||
If you have not received a reply to your email within 48 hours or you have not heard from the security
|
||||
team for the past five days please contact us by email to developers@highload.solutions or by Telegram message
|
||||
to [our support](https://t.me/highload_support).
|
||||
Please note that developers@highload.solutions list includes all developers, who may be outside our opensource security team.
|
||||
When escalating on this list, please do not disclose the details of the issue.
|
||||
Simply state that you're trying to reach a member of the security team.
|
||||
|
||||
### Flagging Existing Issues as Security-related
|
||||
|
||||
If you believe that an existing issue is security-related, we ask that you send an email to oss-security@highload.solutions.
|
||||
The email should include the issue ID and a short description of why it should be handled according to this security policy.
|
||||
|
||||
### Disclosure Process
|
||||
|
||||
Our project uses the following disclosure process:
|
||||
|
||||
- Once the security report is received it is assigned a primary handler. This person coordinates the fix and release process.
|
||||
- The issue is confirmed and a list of affected software is determined.
|
||||
- Code is audited to find any potential similar problems.
|
||||
- Fixes are prepared for the two most recent major releases and the head/master revision. These fixes are not yet committed to the public repository.
|
||||
- To notify users, a new issue without security details is submitted to our GitHub repository.
|
||||
- Three working days following this notification, the fixes are applied to the public repository and a new release is issued.
|
||||
- On the date that the fixes are applied, announcement is published in the issue.
|
||||
|
||||
This process can take some time, especially when coordination is required with maintainers of other projects.
|
||||
Every effort will be made to handle the bug in as timely a manner as possible, however it's important that we follow
|
||||
the process described above to ensure that disclosures are handled consistently.
|
||||
|
||||
### Receiving Security Updates
|
||||
The best way to receive security announcements is to subscribe ("Watch") to our repository.
|
||||
Any GitHub issues pertaining to a security issue will be prefixed with [security].
|
||||
|
||||
### Comments on This Policy
|
||||
If you have any suggestions to improve this policy, please send an email to oss-security@highload.solutions for discussion.
|
||||
|
||||
### PGP Key for oss-security@highload.ltd
|
||||
|
||||
We accept PGP-encrypted email, but the majority of the security team are not regular PGP users
|
||||
so it's somewhat inconvenient. Please only use PGP for critical security reports.
|
||||
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBFzdjYUBEACa3YN+QVSlnXofUjxr+YrmIaF+da0IUq+TRM4aqUXALsemEdGh
|
||||
yIl7Z6qOOy1d2kPe6t//H9l/92lJ1X7i6aEBK4n/pnPZkwbpy9gGpebgvTZFvcbe
|
||||
mFhF6k1FM35D8TxneJSjizPyGhJPqcr5qccqf8R64TlQx5Ud1JqT2l8P1C5N7gNS
|
||||
lEYXq1h4zBCvTWk1wdeLRRPx7Bn6xrgmyu/k61dLoJDvpvWNATVFDA67oTrPgzTW
|
||||
xtLbbk/xm0mK4a8zMzIpNyz1WkaJW9+4HFXaL+yKlsx7iHe2O7VlGoqS0kdeQup4
|
||||
1HIw/P7yc0jBlNMLUzpuA6ElYUwESWsnCI71YY1x4rKgI+GqH1mWwgn7tteuXQtb
|
||||
Zj0vEdjK3IKIOSbzbzAvSbDt8F1+o7EMtdy1eUysjKSQgFkDlT6JRmYvEup5/IoG
|
||||
iknh/InQq9RmGFKii6pXWWoltC0ebfCwYOXvymyDdr/hYDqJeHS9Tenpy86Doaaf
|
||||
HGf5nIFAMB2G5ctNpBwzNXR2MAWkeHQgdr5a1xmog0hS125usjnUTet3QeCyo4kd
|
||||
gVouoOroMcqFFUXdYaMH4c3KWz0afhTmIaAsFFOv/eMdadVA4QyExTJf3TAoQ+kH
|
||||
lKDlbOAIxEZWRPDFxMRixaVPQC+VxhBcaQ+yNoaUkM0V2m8u8sDBpzi1OQARAQAB
|
||||
tDxPU1MgU2VjdXJpdHksIEhpZ2hsb2FkIExURCA8b3NzLXNlY3VyaXR5QGhpZ2hs
|
||||
b2FkLnNvbHV0aW9ucz6JAlQEEwEIAD4WIQRljYp380uKq2g8TeqsQcvu+Qp2TAUC
|
||||
XN2NhQIbAwUJB4YfgAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRCsQcvu+Qp2
|
||||
TKmED/96YoQoOjD28blFFrigvAsiNcNNZoX9I0dX1lNpD83fBJf+/9i+x4jqUnI5
|
||||
5XK/DFTDbhpw8kQBpxS9eEuIYnuo0RdLLp1ctNWTlpwfyHn92mGddl/uBdYHUuUk
|
||||
cjhIQcFaCcWRY+EpamDlv1wmZ83IwBr8Hu5FS+/Msyw1TBvtTRVKW1KoGYMYoXLk
|
||||
BzIglRPwn821B6s4BvK/RJnZkrmHMBZBfYMf+iSMSYd2yPmfT8wbcAjgjLfQa28U
|
||||
gbt4u9xslgKjuM83IqwFfEXBnm7su3OouGWqc+62mQTsbnK65zRFnx6GXRXC1BAi
|
||||
6m9Tm1PU0IiINz66ainquspkXYeHjd9hTwfR3BdFnzBTRRM01cKMFabWbLj8j0p8
|
||||
fF4g9cxEdiLrzEF7Yz4WY0mI4Cpw4eJZfsHMc07Jn7QxfJhIoq+rqBOtEmTjnxMh
|
||||
aWeykoXMHlZN4K0ZrAytozVH1D4bugWA9Zuzi9U3F9hrVVABm11yyhd2iSqI6/FR
|
||||
GcCFOCBW1kEJbzoEguub+BV8LDi8ldljHalvur5k/VFhoDBxniYNsKmiCLVCmDWs
|
||||
/nF84hCReAOJt0vDGwqHe3E2BFFPbKwdJLRNkjxBY0c/pvaV+JxbWQmaxDZNeIFV
|
||||
hFcVGp48HNY3qLWZdsQIfT9m1masJFLVuq8Wx7bYs8Et5eFnH7kCDQRc3Y2FARAA
|
||||
2DJWAxABydyIdCxgFNdqnYyWS46vh2DmLmRMqgasNlD0ozG4S9bszBsgnUI2Xs06
|
||||
J76kFRh8MMHcu9I4lUKCQzfrA4uHkiOK5wvNCaWP+C6JUYNHsqPwk/ILO3gtQ/Ws
|
||||
LLf/PW3rJZVOZB+WY8iaYc20l5vukTaVw4qbEi9dtLkJvVpNHt//+jayXU6s3ew1
|
||||
2X5xdwyAZxaxlnzFaY/Xo/qR+bZhVFC0T9pAECnHv9TVhFGp0JE9ipPGnro5xTIS
|
||||
LttdAkzv4AuSVTIgWgTkh8nN8t7STJqfPEv0I12nmmYHMUyTYOurkfskF3jY2x6x
|
||||
8l02NQ4d5KdC3ReV1j51swrGcZCwsWNp51jnEXKwo+B0NM5OmoRrNJgF2iDgLehs
|
||||
hP00ljU7cB8/1/7kdHZStYaUHICFOFqHzg415FlYm+jpY0nJp/b9BAO0d0/WYnEe
|
||||
Xjihw8EVBAqzEt4kay1BQonZAypeYnGBJr7vNvdiP+mnRwly5qZSGiInxGvtZZFt
|
||||
zL1E3osiF+muQxFcM63BeGdJeYXy+MoczkWa4WNggfcHlGAZkMYiv28zpr4PfrK9
|
||||
mvj4Nu8s71PE9pPpBoZcNDf9v1sHuu96jDSITsPx5YMvvKZWhzJXFKzk6YgAsNH/
|
||||
MF0G+/qmKJZpCdvtHKpYM1uHX85H81CwWJFfBPthyD8AEQEAAYkCPAQYAQgAJhYh
|
||||
BGWNinfzS4qraDxN6qxBy+75CnZMBQJc3Y2FAhsMBQkHhh+AAAoJEKxBy+75CnZM
|
||||
Rn8P/RyL1bhU4Q4WpvmlkepCAwNA0G3QvnKcSZNHEPE5h7H3IyrA/qy16A9eOsgm
|
||||
sthsHYlo5A5lRIy4wPHkFCClMrMHdKuoS72//qgw+oOrBcwb7Te+Nas+ewhaJ7N9
|
||||
vAX06vDH9bLl52CPbtats5+eBpePgP3HDPxd7CWHxq9bzJTbzqsTkN7JvoovR2dP
|
||||
itPJDij7QYLYVEM1t7QxUVpVwAjDi/kCtC9ts5L+V0snF2n3bHZvu04EXdpvxOQI
|
||||
pG/7Q+/WoI8NU6Bb/FA3tJGYIhSwI3SY+5XV/TAZttZaYSh2SD8vhc+eo+gW9sAN
|
||||
xa+VESBQCht9+tKIwEwHs1efoRgFdbwwJ2c+33+XydQ6yjdXoX1mn2uyCr82jorZ
|
||||
xTzbkY04zr7oZ+0fLpouOFg/mrSL4w2bWEhdHuyoVthLBjnRme0wXCaS3g3mYdLG
|
||||
nSUkogOGOOvvvBtoq/vfx0Eu79piUtw5D8yQSrxLDuz8GxCrVRZ0tYIHb26aTE9G
|
||||
cDsW/Lg5PjcY/LgVNEWOxDQDFVurlImnlVJFb3q+NrWvPbgeIEWwJDCay/z25SEH
|
||||
k3bSOXLp8YGRnlkWUmoeL4g/CCK52iAAlfscZNoKMILhBnbCoD657jpa5GQKJj/U
|
||||
Q8kjgr7kwV/RSosNV9HCPj30mVyiCQ1xg+ZLzMKXVCuBWd+G
|
||||
=lnt2
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
10
vendor/github.com/valyala/fasthttp/bytesconv_table.go
generated
vendored
10
vendor/github.com/valyala/fasthttp/bytesconv_table.go
generated
vendored
|
@ -1,10 +0,0 @@
|
|||
package fasthttp
|
||||
|
||||
// Code generated by go run bytesconv_table_gen.go; DO NOT EDIT.
|
||||
// See bytesconv_table_gen.go for more information about these tables.
|
||||
|
||||
const hex2intTable = "\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x00\x01\x02\x03\x04\x05\x06\a\b\t\x10\x10\x10\x10\x10\x10\x10\n\v\f\r\x0e\x0f\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\n\v\f\r\x0e\x0f\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10"
|
||||
const toLowerTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
|
||||
const toUpperTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
|
||||
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"
|
13
vendor/github.com/valyala/fasthttp/coarseTime.go
generated
vendored
13
vendor/github.com/valyala/fasthttp/coarseTime.go
generated
vendored
|
@ -1,13 +0,0 @@
|
|||
package fasthttp
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// CoarseTimeNow returns the current time truncated to the nearest second.
|
||||
//
|
||||
// Deprecated: This is slower than calling time.Now() directly.
|
||||
// This is now time.Now().Truncate(time.Second) shortcut.
|
||||
func CoarseTimeNow() time.Time {
|
||||
return time.Now().Truncate(time.Second)
|
||||
}
|
22
vendor/github.com/valyala/fasthttp/fuzzit.sh
generated
vendored
22
vendor/github.com/valyala/fasthttp/fuzzit.sh
generated
vendored
|
@ -1,22 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -xe
|
||||
|
||||
## go-fuzz doesn't support modules for now, so ensure we do everything
|
||||
## in the old style GOPATH way
|
||||
export GO111MODULE="off"
|
||||
|
||||
# We need to download these dependencies again after we set GO111MODULE="off"
|
||||
go get -t -v ./...
|
||||
|
||||
go get github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build
|
||||
|
||||
wget -q -O fuzzitbin https://github.com/fuzzitdev/fuzzit/releases/download/v2.4.52/fuzzit_Linux_x86_64
|
||||
chmod a+x fuzzitbin
|
||||
|
||||
for w in request response cookie url; do
|
||||
go-fuzz-build -libfuzzer -o fasthttp_$w.a ./fuzzit/$w/
|
||||
clang -fsanitize=fuzzer fasthttp_$w.a -o fasthttp_$w
|
||||
|
||||
./fuzzitbin create job --type $1 fasthttp/$w fasthttp_$w
|
||||
done
|
10
vendor/github.com/valyala/fasthttp/go.mod
generated
vendored
10
vendor/github.com/valyala/fasthttp/go.mod
generated
vendored
|
@ -1,10 +0,0 @@
|
|||
module github.com/valyala/fasthttp
|
||||
|
||||
go 1.11
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.10.4
|
||||
github.com/valyala/bytebufferpool v1.0.0
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e
|
||||
)
|
12
vendor/github.com/valyala/fasthttp/go.sum
generated
vendored
12
vendor/github.com/valyala/fasthttp/go.sum
generated
vendored
|
@ -1,12 +0,0 @@
|
|||
github.com/klauspost/compress v1.10.4 h1:jFzIFaf586tquEB5EhzQG0HwGNSlgAJpG53G6Ss11wc=
|
||||
github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
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/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
164
vendor/github.com/valyala/fasthttp/headers.go
generated
vendored
164
vendor/github.com/valyala/fasthttp/headers.go
generated
vendored
|
@ -1,164 +0,0 @@
|
|||
package fasthttp
|
||||
|
||||
// Headers
|
||||
const (
|
||||
// Authentication
|
||||
HeaderAuthorization = "Authorization"
|
||||
HeaderProxyAuthenticate = "Proxy-Authenticate"
|
||||
HeaderProxyAuthorization = "Proxy-Authorization"
|
||||
HeaderWWWAuthenticate = "WWW-Authenticate"
|
||||
|
||||
// Caching
|
||||
HeaderAge = "Age"
|
||||
HeaderCacheControl = "Cache-Control"
|
||||
HeaderClearSiteData = "Clear-Site-Data"
|
||||
HeaderExpires = "Expires"
|
||||
HeaderPragma = "Pragma"
|
||||
HeaderWarning = "Warning"
|
||||
|
||||
// Client hints
|
||||
HeaderAcceptCH = "Accept-CH"
|
||||
HeaderAcceptCHLifetime = "Accept-CH-Lifetime"
|
||||
HeaderContentDPR = "Content-DPR"
|
||||
HeaderDPR = "DPR"
|
||||
HeaderEarlyData = "Early-Data"
|
||||
HeaderSaveData = "Save-Data"
|
||||
HeaderViewportWidth = "Viewport-Width"
|
||||
HeaderWidth = "Width"
|
||||
|
||||
// Conditionals
|
||||
HeaderETag = "ETag"
|
||||
HeaderIfMatch = "If-Match"
|
||||
HeaderIfModifiedSince = "If-Modified-Since"
|
||||
HeaderIfNoneMatch = "If-None-Match"
|
||||
HeaderIfUnmodifiedSince = "If-Unmodified-Since"
|
||||
HeaderLastModified = "Last-Modified"
|
||||
HeaderVary = "Vary"
|
||||
|
||||
// Connection management
|
||||
HeaderConnection = "Connection"
|
||||
HeaderKeepAlive = "Keep-Alive"
|
||||
|
||||
// Content negotiation
|
||||
HeaderAccept = "Accept"
|
||||
HeaderAcceptCharset = "Accept-Charset"
|
||||
HeaderAcceptEncoding = "Accept-Encoding"
|
||||
HeaderAcceptLanguage = "Accept-Language"
|
||||
|
||||
// Controls
|
||||
HeaderCookie = "Cookie"
|
||||
HeaderExpect = "Expect"
|
||||
HeaderMaxForwards = "Max-Forwards"
|
||||
HeaderSetCookie = "Set-Cookie"
|
||||
|
||||
// CORS
|
||||
HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"
|
||||
HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers"
|
||||
HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods"
|
||||
HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin"
|
||||
HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers"
|
||||
HeaderAccessControlMaxAge = "Access-Control-Max-Age"
|
||||
HeaderAccessControlRequestHeaders = "Access-Control-Request-Headers"
|
||||
HeaderAccessControlRequestMethod = "Access-Control-Request-Method"
|
||||
HeaderOrigin = "Origin"
|
||||
HeaderTimingAllowOrigin = "Timing-Allow-Origin"
|
||||
HeaderXPermittedCrossDomainPolicies = "X-Permitted-Cross-Domain-Policies"
|
||||
|
||||
// Do Not Track
|
||||
HeaderDNT = "DNT"
|
||||
HeaderTk = "Tk"
|
||||
|
||||
// Downloads
|
||||
HeaderContentDisposition = "Content-Disposition"
|
||||
|
||||
// Message body information
|
||||
HeaderContentEncoding = "Content-Encoding"
|
||||
HeaderContentLanguage = "Content-Language"
|
||||
HeaderContentLength = "Content-Length"
|
||||
HeaderContentLocation = "Content-Location"
|
||||
HeaderContentType = "Content-Type"
|
||||
|
||||
// Proxies
|
||||
HeaderForwarded = "Forwarded"
|
||||
HeaderVia = "Via"
|
||||
HeaderXForwardedFor = "X-Forwarded-For"
|
||||
HeaderXForwardedHost = "X-Forwarded-Host"
|
||||
HeaderXForwardedProto = "X-Forwarded-Proto"
|
||||
|
||||
// Redirects
|
||||
HeaderLocation = "Location"
|
||||
|
||||
// Request context
|
||||
HeaderFrom = "From"
|
||||
HeaderHost = "Host"
|
||||
HeaderReferer = "Referer"
|
||||
HeaderReferrerPolicy = "Referrer-Policy"
|
||||
HeaderUserAgent = "User-Agent"
|
||||
|
||||
// Response context
|
||||
HeaderAllow = "Allow"
|
||||
HeaderServer = "Server"
|
||||
|
||||
// Range requests
|
||||
HeaderAcceptRanges = "Accept-Ranges"
|
||||
HeaderContentRange = "Content-Range"
|
||||
HeaderIfRange = "If-Range"
|
||||
HeaderRange = "Range"
|
||||
|
||||
// Security
|
||||
HeaderContentSecurityPolicy = "Content-Security-Policy"
|
||||
HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only"
|
||||
HeaderCrossOriginResourcePolicy = "Cross-Origin-Resource-Policy"
|
||||
HeaderExpectCT = "Expect-CT"
|
||||
HeaderFeaturePolicy = "Feature-Policy"
|
||||
HeaderPublicKeyPins = "Public-Key-Pins"
|
||||
HeaderPublicKeyPinsReportOnly = "Public-Key-Pins-Report-Only"
|
||||
HeaderStrictTransportSecurity = "Strict-Transport-Security"
|
||||
HeaderUpgradeInsecureRequests = "Upgrade-Insecure-Requests"
|
||||
HeaderXContentTypeOptions = "X-Content-Type-Options"
|
||||
HeaderXDownloadOptions = "X-Download-Options"
|
||||
HeaderXFrameOptions = "X-Frame-Options"
|
||||
HeaderXPoweredBy = "X-Powered-By"
|
||||
HeaderXXSSProtection = "X-XSS-Protection"
|
||||
|
||||
// Server-sent event
|
||||
HeaderLastEventID = "Last-Event-ID"
|
||||
HeaderNEL = "NEL"
|
||||
HeaderPingFrom = "Ping-From"
|
||||
HeaderPingTo = "Ping-To"
|
||||
HeaderReportTo = "Report-To"
|
||||
|
||||
// Transfer coding
|
||||
HeaderTE = "TE"
|
||||
HeaderTrailer = "Trailer"
|
||||
HeaderTransferEncoding = "Transfer-Encoding"
|
||||
|
||||
// WebSockets
|
||||
HeaderSecWebSocketAccept = "Sec-WebSocket-Accept"
|
||||
HeaderSecWebSocketExtensions = "Sec-WebSocket-Extensions"
|
||||
HeaderSecWebSocketKey = "Sec-WebSocket-Key"
|
||||
HeaderSecWebSocketProtocol = "Sec-WebSocket-Protocol"
|
||||
HeaderSecWebSocketVersion = "Sec-WebSocket-Version"
|
||||
|
||||
// Other
|
||||
HeaderAcceptPatch = "Accept-Patch"
|
||||
HeaderAcceptPushPolicy = "Accept-Push-Policy"
|
||||
HeaderAcceptSignature = "Accept-Signature"
|
||||
HeaderAltSvc = "Alt-Svc"
|
||||
HeaderDate = "Date"
|
||||
HeaderIndex = "Index"
|
||||
HeaderLargeAllocation = "Large-Allocation"
|
||||
HeaderLink = "Link"
|
||||
HeaderPushPolicy = "Push-Policy"
|
||||
HeaderRetryAfter = "Retry-After"
|
||||
HeaderServerTiming = "Server-Timing"
|
||||
HeaderSignature = "Signature"
|
||||
HeaderSignedHeaders = "Signed-Headers"
|
||||
HeaderSourceMap = "SourceMap"
|
||||
HeaderUpgrade = "Upgrade"
|
||||
HeaderXDNSPrefetchControl = "X-DNS-Prefetch-Control"
|
||||
HeaderXPingback = "X-Pingback"
|
||||
HeaderXRequestedWith = "X-Requested-With"
|
||||
HeaderXRobotsTag = "X-Robots-Tag"
|
||||
HeaderXUACompatible = "X-UA-Compatible"
|
||||
)
|
14
vendor/github.com/valyala/fasthttp/methods.go
generated
vendored
14
vendor/github.com/valyala/fasthttp/methods.go
generated
vendored
|
@ -1,14 +0,0 @@
|
|||
package fasthttp
|
||||
|
||||
// HTTP methods were copied from net/http.
|
||||
const (
|
||||
MethodGet = "GET" // RFC 7231, 4.3.1
|
||||
MethodHead = "HEAD" // RFC 7231, 4.3.2
|
||||
MethodPost = "POST" // RFC 7231, 4.3.3
|
||||
MethodPut = "PUT" // RFC 7231, 4.3.4
|
||||
MethodPatch = "PATCH" // RFC 5789
|
||||
MethodDelete = "DELETE" // RFC 7231, 4.3.5
|
||||
MethodConnect = "CONNECT" // RFC 7231, 4.3.6
|
||||
MethodOptions = "OPTIONS" // RFC 7231, 4.3.7
|
||||
MethodTrace = "TRACE" // RFC 7231, 4.3.8
|
||||
)
|
85
vendor/github.com/valyala/fasthttp/strings.go
generated
vendored
85
vendor/github.com/valyala/fasthttp/strings.go
generated
vendored
|
@ -1,85 +0,0 @@
|
|||
package fasthttp
|
||||
|
||||
var (
|
||||
defaultServerName = []byte("fasthttp")
|
||||
defaultUserAgent = []byte("fasthttp")
|
||||
defaultContentType = []byte("text/plain; charset=utf-8")
|
||||
)
|
||||
|
||||
var (
|
||||
strSlash = []byte("/")
|
||||
strSlashSlash = []byte("//")
|
||||
strSlashDotDot = []byte("/..")
|
||||
strSlashDotSlash = []byte("/./")
|
||||
strSlashDotDotSlash = []byte("/../")
|
||||
strCRLF = []byte("\r\n")
|
||||
strHTTP = []byte("http")
|
||||
strHTTPS = []byte("https")
|
||||
strHTTP11 = []byte("HTTP/1.1")
|
||||
strColon = []byte(":")
|
||||
strColonSlashSlash = []byte("://")
|
||||
strColonSpace = []byte(": ")
|
||||
strGMT = []byte("GMT")
|
||||
strAt = []byte("@")
|
||||
|
||||
strResponseContinue = []byte("HTTP/1.1 100 Continue\r\n\r\n")
|
||||
|
||||
strGet = []byte(MethodGet)
|
||||
strHead = []byte(MethodHead)
|
||||
strPost = []byte(MethodPost)
|
||||
strPut = []byte(MethodPut)
|
||||
strDelete = []byte(MethodDelete)
|
||||
strConnect = []byte(MethodConnect)
|
||||
strOptions = []byte(MethodOptions)
|
||||
strTrace = []byte(MethodTrace)
|
||||
strPatch = []byte(MethodPatch)
|
||||
|
||||
strExpect = []byte(HeaderExpect)
|
||||
strConnection = []byte(HeaderConnection)
|
||||
strContentLength = []byte(HeaderContentLength)
|
||||
strContentType = []byte(HeaderContentType)
|
||||
strDate = []byte(HeaderDate)
|
||||
strHost = []byte(HeaderHost)
|
||||
strReferer = []byte(HeaderReferer)
|
||||
strServer = []byte(HeaderServer)
|
||||
strTransferEncoding = []byte(HeaderTransferEncoding)
|
||||
strContentEncoding = []byte(HeaderContentEncoding)
|
||||
strAcceptEncoding = []byte(HeaderAcceptEncoding)
|
||||
strUserAgent = []byte(HeaderUserAgent)
|
||||
strCookie = []byte(HeaderCookie)
|
||||
strSetCookie = []byte(HeaderSetCookie)
|
||||
strLocation = []byte(HeaderLocation)
|
||||
strIfModifiedSince = []byte(HeaderIfModifiedSince)
|
||||
strLastModified = []byte(HeaderLastModified)
|
||||
strAcceptRanges = []byte(HeaderAcceptRanges)
|
||||
strRange = []byte(HeaderRange)
|
||||
strContentRange = []byte(HeaderContentRange)
|
||||
strAuthorization = []byte(HeaderAuthorization)
|
||||
|
||||
strCookieExpires = []byte("expires")
|
||||
strCookieDomain = []byte("domain")
|
||||
strCookiePath = []byte("path")
|
||||
strCookieHTTPOnly = []byte("HttpOnly")
|
||||
strCookieSecure = []byte("secure")
|
||||
strCookieMaxAge = []byte("max-age")
|
||||
strCookieSameSite = []byte("SameSite")
|
||||
strCookieSameSiteLax = []byte("Lax")
|
||||
strCookieSameSiteStrict = []byte("Strict")
|
||||
strCookieSameSiteNone = []byte("None")
|
||||
|
||||
strClose = []byte("close")
|
||||
strGzip = []byte("gzip")
|
||||
strDeflate = []byte("deflate")
|
||||
strKeepAlive = []byte("keep-alive")
|
||||
strUpgrade = []byte("Upgrade")
|
||||
strChunked = []byte("chunked")
|
||||
strIdentity = []byte("identity")
|
||||
str100Continue = []byte("100-continue")
|
||||
strPostArgsContentType = []byte("application/x-www-form-urlencoded")
|
||||
strMultipartFormData = []byte("multipart/form-data")
|
||||
strBoundary = []byte("boundary")
|
||||
strBytes = []byte("bytes")
|
||||
strTextSlash = []byte("text/")
|
||||
strApplicationSlash = []byte("application/")
|
||||
strBasicSpace = []byte("Basic ")
|
||||
)
|
478
vendor/github.com/valyala/fasthttp/tcpdialer.go
generated
vendored
478
vendor/github.com/valyala/fasthttp/tcpdialer.go
generated
vendored
|
@ -1,478 +0,0 @@
|
|||
package fasthttp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Dial dials the given TCP addr using tcp4.
|
||||
//
|
||||
// This function has the following additional features comparing to net.Dial:
|
||||
//
|
||||
// * It reduces load on DNS resolver by caching resolved TCP addressed
|
||||
// for DefaultDNSCacheDuration.
|
||||
// * It dials all the resolved TCP addresses in round-robin manner until
|
||||
// connection is established. This may be useful if certain addresses
|
||||
// are temporarily unreachable.
|
||||
// * It returns ErrDialTimeout if connection cannot be established during
|
||||
// DefaultDialTimeout seconds. Use DialTimeout for customizing dial timeout.
|
||||
//
|
||||
// This dialer is intended for custom code wrapping before passing
|
||||
// to Client.Dial or HostClient.Dial.
|
||||
//
|
||||
// For instance, per-host counters and/or limits may be implemented
|
||||
// by such wrappers.
|
||||
//
|
||||
// The addr passed to the function must contain port. Example addr values:
|
||||
//
|
||||
// * foobar.baz:443
|
||||
// * foo.bar:80
|
||||
// * aaa.com:8080
|
||||
func Dial(addr string) (net.Conn, error) {
|
||||
return defaultDialer.Dial(addr)
|
||||
}
|
||||
|
||||
// DialTimeout dials the given TCP addr using tcp4 using the given timeout.
|
||||
//
|
||||
// This function has the following additional features comparing to net.Dial:
|
||||
//
|
||||
// * It reduces load on DNS resolver by caching resolved TCP addressed
|
||||
// for DefaultDNSCacheDuration.
|
||||
// * It dials all the resolved TCP addresses in round-robin manner until
|
||||
// connection is established. This may be useful if certain addresses
|
||||
// are temporarily unreachable.
|
||||
//
|
||||
// This dialer is intended for custom code wrapping before passing
|
||||
// to Client.Dial or HostClient.Dial.
|
||||
//
|
||||
// For instance, per-host counters and/or limits may be implemented
|
||||
// by such wrappers.
|
||||
//
|
||||
// The addr passed to the function must contain port. Example addr values:
|
||||
//
|
||||
// * foobar.baz:443
|
||||
// * foo.bar:80
|
||||
// * aaa.com:8080
|
||||
func DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
|
||||
return defaultDialer.DialTimeout(addr, timeout)
|
||||
}
|
||||
|
||||
// DialDualStack dials the given TCP addr using both tcp4 and tcp6.
|
||||
//
|
||||
// This function has the following additional features comparing to net.Dial:
|
||||
//
|
||||
// * It reduces load on DNS resolver by caching resolved TCP addressed
|
||||
// for DefaultDNSCacheDuration.
|
||||
// * It dials all the resolved TCP addresses in round-robin manner until
|
||||
// connection is established. This may be useful if certain addresses
|
||||
// are temporarily unreachable.
|
||||
// * It returns ErrDialTimeout if connection cannot be established during
|
||||
// DefaultDialTimeout seconds. Use DialDualStackTimeout for custom dial
|
||||
// timeout.
|
||||
//
|
||||
// This dialer is intended for custom code wrapping before passing
|
||||
// to Client.Dial or HostClient.Dial.
|
||||
//
|
||||
// For instance, per-host counters and/or limits may be implemented
|
||||
// by such wrappers.
|
||||
//
|
||||
// The addr passed to the function must contain port. Example addr values:
|
||||
//
|
||||
// * foobar.baz:443
|
||||
// * foo.bar:80
|
||||
// * aaa.com:8080
|
||||
func DialDualStack(addr string) (net.Conn, error) {
|
||||
return defaultDialer.DialDualStack(addr)
|
||||
}
|
||||
|
||||
// DialDualStackTimeout dials the given TCP addr using both tcp4 and tcp6
|
||||
// using the given timeout.
|
||||
//
|
||||
// This function has the following additional features comparing to net.Dial:
|
||||
//
|
||||
// * It reduces load on DNS resolver by caching resolved TCP addressed
|
||||
// for DefaultDNSCacheDuration.
|
||||
// * It dials all the resolved TCP addresses in round-robin manner until
|
||||
// connection is established. This may be useful if certain addresses
|
||||
// are temporarily unreachable.
|
||||
//
|
||||
// This dialer is intended for custom code wrapping before passing
|
||||
// to Client.Dial or HostClient.Dial.
|
||||
//
|
||||
// For instance, per-host counters and/or limits may be implemented
|
||||
// by such wrappers.
|
||||
//
|
||||
// The addr passed to the function must contain port. Example addr values:
|
||||
//
|
||||
// * foobar.baz:443
|
||||
// * foo.bar:80
|
||||
// * aaa.com:8080
|
||||
func DialDualStackTimeout(addr string, timeout time.Duration) (net.Conn, error) {
|
||||
return defaultDialer.DialDualStackTimeout(addr, timeout)
|
||||
}
|
||||
|
||||
var (
|
||||
defaultDialer = &TCPDialer{Concurrency: 1000}
|
||||
)
|
||||
|
||||
// Resolver represents interface of the tcp resolver.
|
||||
type Resolver interface {
|
||||
LookupIPAddr(context.Context, string) (names []net.IPAddr, err error)
|
||||
}
|
||||
|
||||
// TCPDialer contains options to control a group of Dial calls.
|
||||
type TCPDialer struct {
|
||||
// Concurrency controls the maximum number of concurrent Dails
|
||||
// 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{
|
||||
// PreferGo: true,
|
||||
// StrictErrors: false,
|
||||
// Dial: func (ctx context.Context, network, address string) (net.Conn, error) {
|
||||
// d := net.Dialer{}
|
||||
// return d.DialContext(ctx, "udp", "8.8.8.8:53")
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
Resolver Resolver
|
||||
|
||||
tcpAddrsLock sync.Mutex
|
||||
tcpAddrsMap map[string]*tcpAddrEntry
|
||||
|
||||
concurrencyCh chan struct{}
|
||||
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// Dial dials the given TCP addr using tcp4.
|
||||
//
|
||||
// This function has the following additional features comparing to net.Dial:
|
||||
//
|
||||
// * It reduces load on DNS resolver by caching resolved TCP addressed
|
||||
// for DefaultDNSCacheDuration.
|
||||
// * It dials all the resolved TCP addresses in round-robin manner until
|
||||
// connection is established. This may be useful if certain addresses
|
||||
// are temporarily unreachable.
|
||||
// * It returns ErrDialTimeout if connection cannot be established during
|
||||
// DefaultDialTimeout seconds. Use DialTimeout for customizing dial timeout.
|
||||
//
|
||||
// This dialer is intended for custom code wrapping before passing
|
||||
// to Client.Dial or HostClient.Dial.
|
||||
//
|
||||
// For instance, per-host counters and/or limits may be implemented
|
||||
// by such wrappers.
|
||||
//
|
||||
// The addr passed to the function must contain port. Example addr values:
|
||||
//
|
||||
// * foobar.baz:443
|
||||
// * foo.bar:80
|
||||
// * aaa.com:8080
|
||||
func (d *TCPDialer) Dial(addr string) (net.Conn, error) {
|
||||
return d.dial(addr, false, DefaultDialTimeout)
|
||||
}
|
||||
|
||||
// DialTimeout dials the given TCP addr using tcp4 using the given timeout.
|
||||
//
|
||||
// This function has the following additional features comparing to net.Dial:
|
||||
//
|
||||
// * It reduces load on DNS resolver by caching resolved TCP addressed
|
||||
// for DefaultDNSCacheDuration.
|
||||
// * It dials all the resolved TCP addresses in round-robin manner until
|
||||
// connection is established. This may be useful if certain addresses
|
||||
// are temporarily unreachable.
|
||||
//
|
||||
// This dialer is intended for custom code wrapping before passing
|
||||
// to Client.Dial or HostClient.Dial.
|
||||
//
|
||||
// For instance, per-host counters and/or limits may be implemented
|
||||
// by such wrappers.
|
||||
//
|
||||
// The addr passed to the function must contain port. Example addr values:
|
||||
//
|
||||
// * foobar.baz:443
|
||||
// * foo.bar:80
|
||||
// * aaa.com:8080
|
||||
func (d *TCPDialer) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
|
||||
return d.dial(addr, false, timeout)
|
||||
}
|
||||
|
||||
// DialDualStack dials the given TCP addr using both tcp4 and tcp6.
|
||||
//
|
||||
// This function has the following additional features comparing to net.Dial:
|
||||
//
|
||||
// * It reduces load on DNS resolver by caching resolved TCP addressed
|
||||
// for DefaultDNSCacheDuration.
|
||||
// * It dials all the resolved TCP addresses in round-robin manner until
|
||||
// connection is established. This may be useful if certain addresses
|
||||
// are temporarily unreachable.
|
||||
// * It returns ErrDialTimeout if connection cannot be established during
|
||||
// DefaultDialTimeout seconds. Use DialDualStackTimeout for custom dial
|
||||
// timeout.
|
||||
//
|
||||
// This dialer is intended for custom code wrapping before passing
|
||||
// to Client.Dial or HostClient.Dial.
|
||||
//
|
||||
// For instance, per-host counters and/or limits may be implemented
|
||||
// by such wrappers.
|
||||
//
|
||||
// The addr passed to the function must contain port. Example addr values:
|
||||
//
|
||||
// * foobar.baz:443
|
||||
// * foo.bar:80
|
||||
// * aaa.com:8080
|
||||
func (d *TCPDialer) DialDualStack(addr string) (net.Conn, error) {
|
||||
return d.dial(addr, true, DefaultDialTimeout)
|
||||
}
|
||||
|
||||
// DialDualStackTimeout dials the given TCP addr using both tcp4 and tcp6
|
||||
// using the given timeout.
|
||||
//
|
||||
// This function has the following additional features comparing to net.Dial:
|
||||
//
|
||||
// * It reduces load on DNS resolver by caching resolved TCP addressed
|
||||
// for DefaultDNSCacheDuration.
|
||||
// * It dials all the resolved TCP addresses in round-robin manner until
|
||||
// connection is established. This may be useful if certain addresses
|
||||
// are temporarily unreachable.
|
||||
//
|
||||
// This dialer is intended for custom code wrapping before passing
|
||||
// to Client.Dial or HostClient.Dial.
|
||||
//
|
||||
// For instance, per-host counters and/or limits may be implemented
|
||||
// by such wrappers.
|
||||
//
|
||||
// The addr passed to the function must contain port. Example addr values:
|
||||
//
|
||||
// * foobar.baz:443
|
||||
// * foo.bar:80
|
||||
// * aaa.com:8080
|
||||
func (d *TCPDialer) DialDualStackTimeout(addr string, timeout time.Duration) (net.Conn, error) {
|
||||
return d.dial(addr, true, timeout)
|
||||
}
|
||||
|
||||
func (d *TCPDialer) dial(addr string, dualStack bool, timeout time.Duration) (net.Conn, error) {
|
||||
d.once.Do(func() {
|
||||
if d.Concurrency > 0 {
|
||||
d.concurrencyCh = make(chan struct{}, d.Concurrency)
|
||||
}
|
||||
d.tcpAddrsMap = make(map[string]*tcpAddrEntry)
|
||||
go d.tcpAddrsClean()
|
||||
})
|
||||
|
||||
addrs, idx, err := d.getTCPAddrs(addr, dualStack)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
network := "tcp4"
|
||||
if dualStack {
|
||||
network = "tcp"
|
||||
}
|
||||
|
||||
var conn net.Conn
|
||||
n := uint32(len(addrs))
|
||||
deadline := time.Now().Add(timeout)
|
||||
for n > 0 {
|
||||
conn, err = d.tryDial(network, &addrs[idx%n], deadline, d.concurrencyCh)
|
||||
if err == nil {
|
||||
return conn, nil
|
||||
}
|
||||
if err == ErrDialTimeout {
|
||||
return nil, err
|
||||
}
|
||||
idx++
|
||||
n--
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (d *TCPDialer) tryDial(network string, addr *net.TCPAddr, deadline time.Time, concurrencyCh chan struct{}) (net.Conn, error) {
|
||||
timeout := -time.Since(deadline)
|
||||
if timeout <= 0 {
|
||||
return nil, ErrDialTimeout
|
||||
}
|
||||
|
||||
if concurrencyCh != nil {
|
||||
select {
|
||||
case concurrencyCh <- struct{}{}:
|
||||
default:
|
||||
tc := AcquireTimer(timeout)
|
||||
isTimeout := false
|
||||
select {
|
||||
case concurrencyCh <- struct{}{}:
|
||||
case <-tc.C:
|
||||
isTimeout = true
|
||||
}
|
||||
ReleaseTimer(tc)
|
||||
if isTimeout {
|
||||
return nil, ErrDialTimeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chv := dialResultChanPool.Get()
|
||||
if chv == nil {
|
||||
chv = make(chan dialResult, 1)
|
||||
}
|
||||
ch := chv.(chan dialResult)
|
||||
go func() {
|
||||
var dr dialResult
|
||||
dr.conn, dr.err = net.DialTCP(network, d.LocalAddr, addr)
|
||||
ch <- dr
|
||||
if concurrencyCh != nil {
|
||||
<-concurrencyCh
|
||||
}
|
||||
}()
|
||||
|
||||
var (
|
||||
conn net.Conn
|
||||
err error
|
||||
)
|
||||
|
||||
tc := AcquireTimer(timeout)
|
||||
select {
|
||||
case dr := <-ch:
|
||||
conn = dr.conn
|
||||
err = dr.err
|
||||
dialResultChanPool.Put(ch)
|
||||
case <-tc.C:
|
||||
err = ErrDialTimeout
|
||||
}
|
||||
ReleaseTimer(tc)
|
||||
|
||||
return conn, err
|
||||
}
|
||||
|
||||
var dialResultChanPool sync.Pool
|
||||
|
||||
type dialResult struct {
|
||||
conn net.Conn
|
||||
err error
|
||||
}
|
||||
|
||||
// ErrDialTimeout is returned when TCP dialing is timed out.
|
||||
var ErrDialTimeout = errors.New("dialing to the given TCP address timed out")
|
||||
|
||||
// DefaultDialTimeout is timeout used by Dial and DialDualStack
|
||||
// for establishing TCP connections.
|
||||
const DefaultDialTimeout = 3 * time.Second
|
||||
|
||||
type tcpAddrEntry struct {
|
||||
addrs []net.TCPAddr
|
||||
addrsIdx uint32
|
||||
|
||||
resolveTime time.Time
|
||||
pending bool
|
||||
}
|
||||
|
||||
// DefaultDNSCacheDuration is the duration for caching resolved TCP addresses
|
||||
// by Dial* functions.
|
||||
const DefaultDNSCacheDuration = time.Minute
|
||||
|
||||
func (d *TCPDialer) tcpAddrsClean() {
|
||||
expireDuration := 2 * DefaultDNSCacheDuration
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
t := time.Now()
|
||||
|
||||
d.tcpAddrsLock.Lock()
|
||||
for k, e := range d.tcpAddrsMap {
|
||||
if t.Sub(e.resolveTime) > expireDuration {
|
||||
delete(d.tcpAddrsMap, k)
|
||||
}
|
||||
}
|
||||
d.tcpAddrsLock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (d *TCPDialer) getTCPAddrs(addr string, dualStack bool) ([]net.TCPAddr, uint32, error) {
|
||||
d.tcpAddrsLock.Lock()
|
||||
e := d.tcpAddrsMap[addr]
|
||||
if e != nil && !e.pending && time.Since(e.resolveTime) > DefaultDNSCacheDuration {
|
||||
e.pending = true
|
||||
e = nil
|
||||
}
|
||||
d.tcpAddrsLock.Unlock()
|
||||
|
||||
if e == nil {
|
||||
addrs, err := resolveTCPAddrs(addr, dualStack, d.Resolver)
|
||||
if err != nil {
|
||||
d.tcpAddrsLock.Lock()
|
||||
e = d.tcpAddrsMap[addr]
|
||||
if e != nil && e.pending {
|
||||
e.pending = false
|
||||
}
|
||||
d.tcpAddrsLock.Unlock()
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
e = &tcpAddrEntry{
|
||||
addrs: addrs,
|
||||
resolveTime: time.Now(),
|
||||
}
|
||||
|
||||
d.tcpAddrsLock.Lock()
|
||||
d.tcpAddrsMap[addr] = e
|
||||
d.tcpAddrsLock.Unlock()
|
||||
}
|
||||
|
||||
idx := atomic.AddUint32(&e.addrsIdx, 1)
|
||||
return e.addrs, idx, nil
|
||||
}
|
||||
|
||||
func resolveTCPAddrs(addr string, dualStack bool, resolver Resolver) ([]net.TCPAddr, error) {
|
||||
host, portS, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
port, err := strconv.Atoi(portS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resolver == nil {
|
||||
resolver = net.DefaultResolver
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ipaddrs, err := resolver.LookupIPAddr(ctx, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n := len(ipaddrs)
|
||||
addrs := make([]net.TCPAddr, 0, n)
|
||||
for i := 0; i < n; i++ {
|
||||
ip := ipaddrs[i]
|
||||
if !dualStack && ip.IP.To4() == nil {
|
||||
continue
|
||||
}
|
||||
addrs = append(addrs, net.TCPAddr{
|
||||
IP: ip.IP,
|
||||
Port: port,
|
||||
Zone: ip.Zone,
|
||||
})
|
||||
}
|
||||
if len(addrs) == 0 {
|
||||
return nil, errNoDNSEntries
|
||||
}
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
var errNoDNSEntries = errors.New("couldn't find DNS entries for the given domain. Try using DialDualStack")
|
8
vendor/modules.txt
vendored
8
vendor/modules.txt
vendored
|
@ -12,6 +12,10 @@ cloud.google.com/go/storage
|
|||
github.com/BurntSushi/toml
|
||||
# github.com/VictoriaMetrics/fastcache v1.5.7
|
||||
github.com/VictoriaMetrics/fastcache
|
||||
# github.com/VictoriaMetrics/fasthttp v1.0.1
|
||||
github.com/VictoriaMetrics/fasthttp
|
||||
github.com/VictoriaMetrics/fasthttp/fasthttputil
|
||||
github.com/VictoriaMetrics/fasthttp/stackless
|
||||
# github.com/VictoriaMetrics/metrics v1.11.2
|
||||
github.com/VictoriaMetrics/metrics
|
||||
# github.com/VictoriaMetrics/metricsql v0.1.0
|
||||
|
@ -98,10 +102,6 @@ github.com/klauspost/compress/zstd/internal/xxhash
|
|||
github.com/lithammer/go-jump-consistent-hash
|
||||
# github.com/valyala/bytebufferpool v1.0.0
|
||||
github.com/valyala/bytebufferpool
|
||||
# github.com/valyala/fasthttp v1.12.0
|
||||
github.com/valyala/fasthttp
|
||||
github.com/valyala/fasthttp/fasthttputil
|
||||
github.com/valyala/fasthttp/stackless
|
||||
# github.com/valyala/fastjson v1.5.1
|
||||
github.com/valyala/fastjson
|
||||
github.com/valyala/fastjson/fastfloat
|
||||
|
|
Loading…
Reference in a new issue