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:
Aliaksandr Valialkin 2020-04-29 16:20:23 +03:00
parent 473188f4fd
commit b6d88bac04
67 changed files with 1619 additions and 4579 deletions

View file

@ -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 (

View file

@ -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
View file

@ -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
View file

@ -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=

View file

@ -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,

View file

@ -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

View file

@ -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) {

View file

@ -1,4 +1,3 @@
tags
*.pprof
*.fasthttp.gz
.idea

16
vendor/github.com/VictoriaMetrics/fasthttp/.travis.yml generated vendored Normal file
View 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
View 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
View 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!

View file

@ -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 {

View 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

View file

@ -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...)
}

View file

@ -3,5 +3,6 @@
package fasthttp
const (
maxIntChars = 9
maxHexIntChars = 7
)

View file

@ -3,5 +3,6 @@
package fasthttp
const (
maxIntChars = 18
maxHexIntChars = 15
)

View 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

View file

@ -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
}

View file

@ -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
}

View file

@ -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.

View file

@ -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
}

View file

@ -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
}

View file

@ -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
View 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
View 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=

View file

@ -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
}

View file

@ -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
}

View file

@ -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() {}

View file

@ -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() {

View file

@ -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
View 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
View 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")

View file

@ -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)
}

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -1,590 +0,0 @@
# fasthttp [![Build Status](https://travis-ci.org/valyala/fasthttp.svg?branch=master)](https://travis-ci.org/valyala/fasthttp?branch=master) [![GoDoc](https://godoc.org/github.com/valyala/fasthttp?status.svg)](http://godoc.org/github.com/valyala/fasthttp) [![fuzzit](https://app.fuzzit.dev/badge?org_id=fasthttp&branch=master)](https://fuzzit.dev) [![Go Report](https://goreportcard.com/badge/github.com/valyala/fasthttp)](https://goreportcard.com/report/github.com/valyala/fasthttp) [![Sourcegraph](https://sourcegraph.com/github.com/valyala/fasthttp/-/badge.svg)](https://sourcegraph.com/github.com/valyala/fasthttp?badge)
![FastHTTP  Fastest and reliable HTTP implementation in Go](https://github.com/fasthttp/docs-assets/raw/master/banner@0.5.png)
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).

View file

@ -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-----
```

View file

@ -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"

View file

@ -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)
}

View file

@ -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

View file

@ -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
)

View file

@ -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=

View file

@ -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"
)

View file

@ -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
)

View file

@ -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 ")
)

View file

@ -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
View file

@ -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