mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
vendor: use github.com/VictoriaMetrics/fasthttp instead of github.com/fasthttp/fasthttp
The upstream fasthttp may contain issues like 996610f021
,
plus a code that isn't used by VictoriaMetrics. So let's use a private copy under our control instead.
This commit is contained in:
parent
cc1878607a
commit
43c39dc36c
59 changed files with 738 additions and 2483 deletions
|
@ -14,8 +14,8 @@ import (
|
|||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/netutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/persistentqueue"
|
||||
"github.com/VictoriaMetrics/fasthttp"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"net"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/VictoriaMetrics/fasthttp"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
func statDial(addr string) (net.Conn, error) {
|
||||
|
|
8
go.mod
8
go.mod
|
@ -4,6 +4,10 @@ require (
|
|||
cloud.google.com/go v0.56.0 // indirect
|
||||
cloud.google.com/go/storage v1.6.0
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7
|
||||
|
||||
// Do not use the original github.com/valyala/fasthttp because of issues
|
||||
// like https://github.com/valyala/fasthttp/commit/996610f021ff45fdc98c2ce7884d5fa4e7f9199b
|
||||
github.com/VictoriaMetrics/fasthttp v1.0.1
|
||||
github.com/VictoriaMetrics/metrics v1.11.2
|
||||
github.com/VictoriaMetrics/metricsql v0.1.0
|
||||
github.com/aws/aws-sdk-go v1.30.13
|
||||
|
@ -11,10 +15,6 @@ require (
|
|||
github.com/golang/protobuf v1.4.0 // indirect
|
||||
github.com/golang/snappy v0.0.1
|
||||
github.com/klauspost/compress v1.10.5
|
||||
|
||||
// do not update fasthttp releases because of issues like https://github.com/valyala/fasthttp/commit/996610f021ff45fdc98c2ce7884d5fa4e7f9199b
|
||||
// in the new code.
|
||||
github.com/valyala/fasthttp v1.2.0
|
||||
github.com/valyala/fastjson v1.5.1
|
||||
github.com/valyala/fastrand v1.0.0
|
||||
github.com/valyala/gozstd v1.7.0
|
||||
|
|
37
go.sum
37
go.sum
|
@ -7,26 +7,21 @@ cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTj
|
|||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
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/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/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=
|
||||
|
@ -36,6 +31,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=
|
||||
|
@ -63,10 +60,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=
|
||||
|
@ -77,11 +72,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=
|
||||
|
@ -95,7 +87,6 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
|
|||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
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=
|
||||
|
@ -137,13 +128,11 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
|
|||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
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=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.2.0 h1:dzZJf2IuMiclVjdw0kkT+f9u4YdrapbNyGAN47E/qnk=
|
||||
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
|
||||
github.com/valyala/fastjson v1.5.1 h1:SXaQZVSwLjZOVhDEhjiCcDtnX0Feu7Z7A1+C5atpoHM=
|
||||
github.com/valyala/fastjson v1.5.1/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
|
@ -160,7 +149,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=
|
||||
|
@ -176,9 +164,7 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE
|
|||
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=
|
||||
|
@ -189,9 +175,7 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
|
|||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
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=
|
||||
|
@ -199,7 +183,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=
|
||||
|
@ -218,12 +201,9 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
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=
|
||||
|
@ -238,7 +218,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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=
|
||||
|
@ -257,9 +236,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=
|
||||
|
@ -315,12 +292,9 @@ google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEn
|
|||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
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=
|
||||
|
@ -328,7 +302,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=
|
||||
|
@ -347,7 +320,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=
|
||||
|
@ -358,12 +330,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=
|
||||
|
@ -377,7 +346,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=
|
||||
|
@ -385,7 +353,6 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
|
|||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/netutil"
|
||||
"github.com/VictoriaMetrics/fasthttp"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -46,17 +46,17 @@ func newClient(sw *ScrapeWork) *client {
|
|||
}
|
||||
}
|
||||
hc := &fasthttp.HostClient{
|
||||
Addr: host,
|
||||
Name: "vm_promscrape",
|
||||
Dial: statDial,
|
||||
DialDualStack: netutil.TCP6Enabled(),
|
||||
IsTLS: isTLS,
|
||||
TLSConfig: tlsCfg,
|
||||
MaxIdleConnDuration: 2 * sw.ScrapeInterval,
|
||||
ReadTimeout: sw.ScrapeTimeout,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
MaxResponseBodySize: *maxScrapeSize,
|
||||
MaxIdemponentCallAttempts: 1,
|
||||
Addr: host,
|
||||
Name: "vm_promscrape",
|
||||
Dial: statDial,
|
||||
DialDualStack: netutil.TCP6Enabled(),
|
||||
IsTLS: isTLS,
|
||||
TLSConfig: tlsCfg,
|
||||
MaxIdleConnDuration: 2 * sw.ScrapeInterval,
|
||||
ReadTimeout: sw.ScrapeTimeout,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
MaxResponseBodySize: *maxScrapeSize,
|
||||
MaxIdempotentRequestAttempts: 1,
|
||||
}
|
||||
return &client{
|
||||
hc: hc,
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/netutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promauth"
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/VictoriaMetrics/fasthttp"
|
||||
)
|
||||
|
||||
// apiConfig contains config for API server
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"net"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/VictoriaMetrics/fasthttp"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
func statDial(addr string) (net.Conn, error) {
|
||||
|
|
16
vendor/github.com/VictoriaMetrics/fasthttp/.travis.yml
generated
vendored
Normal file
16
vendor/github.com/VictoriaMetrics/fasthttp/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.9.x
|
||||
- 1.8.x
|
||||
|
||||
script:
|
||||
# build test for supported platforms
|
||||
- GOOS=linux go build
|
||||
- GOOS=darwin go build
|
||||
- GOOS=freebsd go build
|
||||
- GOOS=windows go build
|
||||
- GOARCH=386 go build
|
||||
|
||||
# run tests on a standard platform
|
||||
- go test -v ./...
|
|
@ -1,9 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-present Aliaksandr Valialkin, VertaMedia
|
||||
Copyright (c) 2018-present Kirill Danshin
|
||||
Copyright (c) 2018-present Erik Dubbelboer
|
||||
Copyright (c) 2018-present FastHTTP Authors
|
||||
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
|
5
vendor/github.com/VictoriaMetrics/fasthttp/README.md
generated
vendored
Normal file
5
vendor/github.com/VictoriaMetrics/fasthttp/README.md
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
Private copy of [fasthttp](https://github.com/valyala/fasthttp) for VictoriaMetrics usage.
|
||||
|
||||
It contains only the functionality required for [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics).
|
||||
|
||||
Do not use it in your own projects!
|
|
@ -4,15 +4,7 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/valyala/bytebufferpool"
|
||||
)
|
||||
|
||||
const (
|
||||
argsNoValue = true
|
||||
argsHasValue = false
|
||||
)
|
||||
|
||||
// AcquireArgs returns an empty Args object from the pool.
|
||||
|
@ -23,7 +15,7 @@ func AcquireArgs() *Args {
|
|||
return argsPool.Get().(*Args)
|
||||
}
|
||||
|
||||
// ReleaseArgs returns the object acquired via AcquireArgs to the pool.
|
||||
// ReleaseArgs returns the object acquired via AquireArgs to the pool.
|
||||
//
|
||||
// Do not access the released Args object, otherwise data races may occur.
|
||||
func ReleaseArgs(a *Args) {
|
||||
|
@ -51,9 +43,8 @@ type Args struct {
|
|||
}
|
||||
|
||||
type argsKV struct {
|
||||
key []byte
|
||||
value []byte
|
||||
noValue bool
|
||||
key []byte
|
||||
value []byte
|
||||
}
|
||||
|
||||
// Reset clears query args.
|
||||
|
@ -116,29 +107,14 @@ func (a *Args) QueryString() []byte {
|
|||
return a.buf
|
||||
}
|
||||
|
||||
// Sort sorts Args by key and then value using 'f' as comparison function.
|
||||
//
|
||||
// For example args.Sort(bytes.Compare)
|
||||
func (a *Args) Sort(f func(x, y []byte) int) {
|
||||
sort.SliceStable(a.args, func(i, j int) bool {
|
||||
n := f(a.args[i].key, a.args[j].key)
|
||||
if n == 0 {
|
||||
return f(a.args[i].value, a.args[j].value) == -1
|
||||
}
|
||||
return n == -1
|
||||
})
|
||||
}
|
||||
|
||||
// AppendBytes appends query string to dst and returns the extended dst.
|
||||
func (a *Args) AppendBytes(dst []byte) []byte {
|
||||
for i, n := 0, len(a.args); i < n; i++ {
|
||||
kv := &a.args[i]
|
||||
dst = AppendQuotedArg(dst, kv.key)
|
||||
if !kv.noValue {
|
||||
if len(kv.value) > 0 {
|
||||
dst = append(dst, '=')
|
||||
if len(kv.value) > 0 {
|
||||
dst = AppendQuotedArg(dst, kv.value)
|
||||
}
|
||||
dst = AppendQuotedArg(dst, kv.value)
|
||||
}
|
||||
if i+1 < n {
|
||||
dst = append(dst, '&')
|
||||
|
@ -169,74 +145,48 @@ func (a *Args) DelBytes(key []byte) {
|
|||
//
|
||||
// Multiple values for the same key may be added.
|
||||
func (a *Args) Add(key, value string) {
|
||||
a.args = appendArg(a.args, key, value, argsHasValue)
|
||||
a.args = appendArg(a.args, key, value)
|
||||
}
|
||||
|
||||
// AddBytesK adds 'key=value' argument.
|
||||
//
|
||||
// Multiple values for the same key may be added.
|
||||
func (a *Args) AddBytesK(key []byte, value string) {
|
||||
a.args = appendArg(a.args, b2s(key), value, argsHasValue)
|
||||
a.args = appendArg(a.args, b2s(key), value)
|
||||
}
|
||||
|
||||
// AddBytesV adds 'key=value' argument.
|
||||
//
|
||||
// Multiple values for the same key may be added.
|
||||
func (a *Args) AddBytesV(key string, value []byte) {
|
||||
a.args = appendArg(a.args, key, b2s(value), argsHasValue)
|
||||
a.args = appendArg(a.args, key, b2s(value))
|
||||
}
|
||||
|
||||
// AddBytesKV adds 'key=value' argument.
|
||||
//
|
||||
// Multiple values for the same key may be added.
|
||||
func (a *Args) AddBytesKV(key, value []byte) {
|
||||
a.args = appendArg(a.args, b2s(key), b2s(value), argsHasValue)
|
||||
}
|
||||
|
||||
// AddNoValue adds only 'key' as argument without the '='.
|
||||
//
|
||||
// Multiple values for the same key may be added.
|
||||
func (a *Args) AddNoValue(key string) {
|
||||
a.args = appendArg(a.args, key, "", argsNoValue)
|
||||
}
|
||||
|
||||
// AddBytesKNoValue adds only 'key' as argument without the '='.
|
||||
//
|
||||
// Multiple values for the same key may be added.
|
||||
func (a *Args) AddBytesKNoValue(key []byte) {
|
||||
a.args = appendArg(a.args, b2s(key), "", argsNoValue)
|
||||
a.args = appendArg(a.args, b2s(key), b2s(value))
|
||||
}
|
||||
|
||||
// Set sets 'key=value' argument.
|
||||
func (a *Args) Set(key, value string) {
|
||||
a.args = setArg(a.args, key, value, argsHasValue)
|
||||
a.args = setArg(a.args, key, value)
|
||||
}
|
||||
|
||||
// SetBytesK sets 'key=value' argument.
|
||||
func (a *Args) SetBytesK(key []byte, value string) {
|
||||
a.args = setArg(a.args, b2s(key), value, argsHasValue)
|
||||
a.args = setArg(a.args, b2s(key), value)
|
||||
}
|
||||
|
||||
// SetBytesV sets 'key=value' argument.
|
||||
func (a *Args) SetBytesV(key string, value []byte) {
|
||||
a.args = setArg(a.args, key, b2s(value), argsHasValue)
|
||||
a.args = setArg(a.args, key, b2s(value))
|
||||
}
|
||||
|
||||
// SetBytesKV sets 'key=value' argument.
|
||||
func (a *Args) SetBytesKV(key, value []byte) {
|
||||
a.args = setArgBytes(a.args, key, value, argsHasValue)
|
||||
}
|
||||
|
||||
// SetNoValue sets only 'key' as argument without the '='.
|
||||
//
|
||||
// Only key in argumemt, like key1&key2
|
||||
func (a *Args) SetNoValue(key string) {
|
||||
a.args = setArg(a.args, key, "", argsNoValue)
|
||||
}
|
||||
|
||||
// SetBytesKNoValue sets 'key' argument.
|
||||
func (a *Args) SetBytesKNoValue(key []byte) {
|
||||
a.args = setArg(a.args, b2s(key), "", argsNoValue)
|
||||
a.args = setArgBytes(a.args, key, value)
|
||||
}
|
||||
|
||||
// Peek returns query arg value for the given key.
|
||||
|
@ -293,10 +243,10 @@ func (a *Args) GetUint(key string) (int, error) {
|
|||
|
||||
// SetUint sets uint value for the given key.
|
||||
func (a *Args) SetUint(key string, value int) {
|
||||
bb := bytebufferpool.Get()
|
||||
bb := AcquireByteBuffer()
|
||||
bb.B = AppendUint(bb.B[:0], value)
|
||||
a.SetBytesV(key, bb.B)
|
||||
bytebufferpool.Put(bb)
|
||||
ReleaseByteBuffer(bb)
|
||||
}
|
||||
|
||||
// SetUintBytes sets uint value for the given key.
|
||||
|
@ -337,14 +287,11 @@ func (a *Args) GetUfloatOrZero(key string) float64 {
|
|||
|
||||
// GetBool returns boolean value for the given key.
|
||||
//
|
||||
// true is returned for "1", "t", "T", "true", "TRUE", "True", "y", "yes", "Y", "YES", "Yes",
|
||||
// true is returned for '1', 'y' and 'yes' values,
|
||||
// otherwise false is returned.
|
||||
func (a *Args) GetBool(key string) bool {
|
||||
switch b2s(a.Peek(key)) {
|
||||
// Support the same true cases as strconv.ParseBool
|
||||
// See: https://github.com/golang/go/blob/4e1b11e2c9bdb0ddea1141eed487be1a626ff5be/src/strconv/atob.go#L12
|
||||
// and Y and Yes versions.
|
||||
case "1", "t", "T", "true", "TRUE", "True", "y", "yes", "Y", "YES", "Yes":
|
||||
switch string(a.Peek(key)) {
|
||||
case "1", "y", "yes":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
@ -370,12 +317,7 @@ func copyArgs(dst, src []argsKV) []argsKV {
|
|||
dstKV := &dst[i]
|
||||
srcKV := &src[i]
|
||||
dstKV.key = append(dstKV.key[:0], srcKV.key...)
|
||||
if srcKV.noValue {
|
||||
dstKV.value = dstKV.value[:0]
|
||||
} else {
|
||||
dstKV.value = append(dstKV.value[:0], srcKV.value...)
|
||||
}
|
||||
dstKV.noValue = srcKV.noValue
|
||||
dstKV.value = append(dstKV.value[:0], srcKV.value...)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
@ -398,41 +340,31 @@ func delAllArgs(args []argsKV, key string) []argsKV {
|
|||
return args
|
||||
}
|
||||
|
||||
func setArgBytes(h []argsKV, key, value []byte, noValue bool) []argsKV {
|
||||
return setArg(h, b2s(key), b2s(value), noValue)
|
||||
func setArgBytes(h []argsKV, key, value []byte) []argsKV {
|
||||
return setArg(h, b2s(key), b2s(value))
|
||||
}
|
||||
|
||||
func setArg(h []argsKV, key, value string, noValue bool) []argsKV {
|
||||
func setArg(h []argsKV, key, value string) []argsKV {
|
||||
n := len(h)
|
||||
for i := 0; i < n; i++ {
|
||||
kv := &h[i]
|
||||
if key == string(kv.key) {
|
||||
if noValue {
|
||||
kv.value = kv.value[:0]
|
||||
} else {
|
||||
kv.value = append(kv.value[:0], value...)
|
||||
}
|
||||
kv.noValue = noValue
|
||||
kv.value = append(kv.value[:0], value...)
|
||||
return h
|
||||
}
|
||||
}
|
||||
return appendArg(h, key, value, noValue)
|
||||
return appendArg(h, key, value)
|
||||
}
|
||||
|
||||
func appendArgBytes(h []argsKV, key, value []byte, noValue bool) []argsKV {
|
||||
return appendArg(h, b2s(key), b2s(value), noValue)
|
||||
func appendArgBytes(h []argsKV, key, value []byte) []argsKV {
|
||||
return appendArg(h, b2s(key), b2s(value))
|
||||
}
|
||||
|
||||
func appendArg(args []argsKV, key, value string, noValue bool) []argsKV {
|
||||
func appendArg(args []argsKV, key, value string) []argsKV {
|
||||
var kv *argsKV
|
||||
args, kv = allocArg(args)
|
||||
kv.key = append(kv.key[:0], key...)
|
||||
if noValue {
|
||||
kv.value = kv.value[:0]
|
||||
} else {
|
||||
kv.value = append(kv.value[:0], value...)
|
||||
}
|
||||
kv.noValue = noValue
|
||||
kv.value = append(kv.value[:0], value...)
|
||||
return args
|
||||
}
|
||||
|
||||
|
@ -488,7 +420,6 @@ func (s *argsScanner) next(kv *argsKV) bool {
|
|||
if len(s.b) == 0 {
|
||||
return false
|
||||
}
|
||||
kv.noValue = argsHasValue
|
||||
|
||||
isKey := true
|
||||
k := 0
|
||||
|
@ -504,7 +435,6 @@ func (s *argsScanner) next(kv *argsKV) bool {
|
|||
if isKey {
|
||||
kv.key = decodeArgAppend(kv.key[:0], s.b[:i])
|
||||
kv.value = kv.value[:0]
|
||||
kv.noValue = argsNoValue
|
||||
} else {
|
||||
kv.value = decodeArgAppend(kv.value[:0], s.b[k:i])
|
||||
}
|
||||
|
@ -516,7 +446,6 @@ func (s *argsScanner) next(kv *argsKV) bool {
|
|||
if isKey {
|
||||
kv.key = decodeArgAppend(kv.key[:0], s.b)
|
||||
kv.value = kv.value[:0]
|
||||
kv.noValue = argsNoValue
|
||||
} else {
|
||||
kv.value = decodeArgAppend(kv.value[:0], s.b[k:])
|
||||
}
|
||||
|
@ -557,7 +486,7 @@ func decodeArgAppend(dst, src []byte) []byte {
|
|||
// decodeArgAppendNoPlus is almost identical to decodeArgAppend, but it doesn't
|
||||
// substitute '+' with ' '.
|
||||
//
|
||||
// The function is copy-pasted from decodeArgAppend due to the performance
|
||||
// The function is copy-pasted from decodeArgAppend due to the preformance
|
||||
// reasons only.
|
||||
func decodeArgAppendNoPlus(dst, src []byte) []byte {
|
||||
if bytes.IndexByte(src, '%') < 0 {
|
64
vendor/github.com/VictoriaMetrics/fasthttp/bytebuffer.go
generated
vendored
Normal file
64
vendor/github.com/VictoriaMetrics/fasthttp/bytebuffer.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
package fasthttp
|
||||
|
||||
import (
|
||||
"github.com/valyala/bytebufferpool"
|
||||
)
|
||||
|
||||
// ByteBuffer provides byte buffer, which can be used with fasthttp API
|
||||
// in order to minimize memory allocations.
|
||||
//
|
||||
// ByteBuffer may be used with functions appending data to the given []byte
|
||||
// slice. See example code for details.
|
||||
//
|
||||
// Use AcquireByteBuffer for obtaining an empty byte buffer.
|
||||
//
|
||||
// ByteBuffer is deprecated. Use github.com/valyala/bytebufferpool instead.
|
||||
type ByteBuffer bytebufferpool.ByteBuffer
|
||||
|
||||
// Write implements io.Writer - it appends p to ByteBuffer.B
|
||||
func (b *ByteBuffer) Write(p []byte) (int, error) {
|
||||
return bb(b).Write(p)
|
||||
}
|
||||
|
||||
// WriteString appends s to ByteBuffer.B
|
||||
func (b *ByteBuffer) WriteString(s string) (int, error) {
|
||||
return bb(b).WriteString(s)
|
||||
}
|
||||
|
||||
// Set sets ByteBuffer.B to p
|
||||
func (b *ByteBuffer) Set(p []byte) {
|
||||
bb(b).Set(p)
|
||||
}
|
||||
|
||||
// SetString sets ByteBuffer.B to s
|
||||
func (b *ByteBuffer) SetString(s string) {
|
||||
bb(b).SetString(s)
|
||||
}
|
||||
|
||||
// Reset makes ByteBuffer.B empty.
|
||||
func (b *ByteBuffer) Reset() {
|
||||
bb(b).Reset()
|
||||
}
|
||||
|
||||
// AcquireByteBuffer returns an empty byte buffer from the pool.
|
||||
//
|
||||
// Acquired byte buffer may be returned to the pool via ReleaseByteBuffer call.
|
||||
// This reduces the number of memory allocations required for byte buffer
|
||||
// management.
|
||||
func AcquireByteBuffer() *ByteBuffer {
|
||||
return (*ByteBuffer)(defaultByteBufferPool.Get())
|
||||
}
|
||||
|
||||
// ReleaseByteBuffer returns byte buffer to the pool.
|
||||
//
|
||||
// ByteBuffer.B mustn't be touched after returning it to the pool.
|
||||
// Otherwise data races occur.
|
||||
func ReleaseByteBuffer(b *ByteBuffer) {
|
||||
defaultByteBufferPool.Put(bb(b))
|
||||
}
|
||||
|
||||
func bb(b *ByteBuffer) *bytebufferpool.ByteBuffer {
|
||||
return (*bytebufferpool.ByteBuffer)(b)
|
||||
}
|
||||
|
||||
var defaultByteBufferPool bytebufferpool.Pool
|
|
@ -164,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")
|
||||
)
|
||||
|
||||
|
@ -183,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)
|
||||
|
@ -417,17 +416,8 @@ func AppendQuotedArg(dst, src []byte) []byte {
|
|||
|
||||
func appendQuotedPath(dst, src []byte) []byte {
|
||||
for _, c := range src {
|
||||
// From the spec: http://tools.ietf.org/html/rfc3986#section-3.3
|
||||
// an path can contain zero or more of pchar that is defined as follows:
|
||||
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
// pct-encoded = "%" HEXDIG HEXDIG
|
||||
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||
// / "*" / "+" / "," / ";" / "="
|
||||
if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' ||
|
||||
c == '-' || c == '.' || c == '_' || c == '~' || c == '!' || c == '$' ||
|
||||
c == '&' || c == '\'' || c == '(' || c == ')' || c == '*' || c == '+' ||
|
||||
c == ',' || c == ';' || c == '=' || c == ':' || c == '@' || c == '/' {
|
||||
c == '/' || c == '.' || c == ',' || c == '=' || c == ':' || c == '&' || c == '~' || c == '-' || c == '_' {
|
||||
dst = append(dst, c)
|
||||
} else {
|
||||
dst = append(dst, '%', hexCharUpper(c>>4), hexCharUpper(c&15))
|
||||
|
@ -435,3 +425,23 @@ func appendQuotedPath(dst, src []byte) []byte {
|
|||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// EqualBytesStr returns true if string(b) == s.
|
||||
//
|
||||
// This function has no performance benefits comparing to string(b) == s.
|
||||
// It is left here for backwards compatibility only.
|
||||
//
|
||||
// This function is deperecated and may be deleted soon.
|
||||
func EqualBytesStr(b []byte, s string) bool {
|
||||
return string(b) == s
|
||||
}
|
||||
|
||||
// AppendBytesStr appends src to dst and returns the extended dst.
|
||||
//
|
||||
// This function has no performance benefits comparing to append(dst, src...).
|
||||
// It is left here for backwards compatibility only.
|
||||
//
|
||||
// This function is deprecated and may be deleted soon.
|
||||
func AppendBytesStr(dst []byte, src string) []byte {
|
||||
return append(dst, src...)
|
||||
}
|
|
@ -3,5 +3,6 @@
|
|||
package fasthttp
|
||||
|
||||
const (
|
||||
maxIntChars = 9
|
||||
maxHexIntChars = 7
|
||||
)
|
|
@ -3,5 +3,6 @@
|
|||
package fasthttp
|
||||
|
||||
const (
|
||||
maxIntChars = 18
|
||||
maxHexIntChars = 15
|
||||
)
|
|
@ -60,11 +60,6 @@ func Do(req *Request, resp *Response) error {
|
|||
//
|
||||
// It is recommended obtaining req and resp via AcquireRequest
|
||||
// and AcquireResponse in performance-critical code.
|
||||
//
|
||||
// Warning: DoTimeout does not terminate the request itself. The request will
|
||||
// continue in the background and the response will be discarded.
|
||||
// If requests take too long and the connection pool gets filled up please
|
||||
// try using a Client and setting a ReadTimeout.
|
||||
func DoTimeout(req *Request, resp *Response, timeout time.Duration) error {
|
||||
return defaultClient.DoTimeout(req, resp, timeout)
|
||||
}
|
||||
|
@ -96,36 +91,33 @@ func DoDeadline(req *Request, resp *Response, deadline time.Time) error {
|
|||
return defaultClient.DoDeadline(req, resp, deadline)
|
||||
}
|
||||
|
||||
// Get returns the status code and body of url.
|
||||
//
|
||||
// The contents of dst will be replaced by the body and returned, if the dst
|
||||
// is too small a new slice will be allocated.
|
||||
// Get appends url contents to dst and returns it as body.
|
||||
//
|
||||
// The function follows redirects. Use Do* for manually handling redirects.
|
||||
//
|
||||
// New body buffer is allocated if dst is nil.
|
||||
func Get(dst []byte, url string) (statusCode int, body []byte, err error) {
|
||||
return defaultClient.Get(dst, url)
|
||||
}
|
||||
|
||||
// GetTimeout returns the status code and body of url.
|
||||
//
|
||||
// The contents of dst will be replaced by the body and returned, if the dst
|
||||
// is too small a new slice will be allocated.
|
||||
// GetTimeout appends url contents to dst and returns it as body.
|
||||
//
|
||||
// The function follows redirects. Use Do* for manually handling redirects.
|
||||
//
|
||||
// New body buffer is allocated if dst is nil.
|
||||
//
|
||||
// ErrTimeout error is returned if url contents couldn't be fetched
|
||||
// during the given timeout.
|
||||
func GetTimeout(dst []byte, url string, timeout time.Duration) (statusCode int, body []byte, err error) {
|
||||
return defaultClient.GetTimeout(dst, url, timeout)
|
||||
}
|
||||
|
||||
// GetDeadline returns the status code and body of url.
|
||||
//
|
||||
// The contents of dst will be replaced by the body and returned, if the dst
|
||||
// is too small a new slice will be allocated.
|
||||
// GetDeadline appends url contents to dst and returns it as body.
|
||||
//
|
||||
// The function follows redirects. Use Do* for manually handling redirects.
|
||||
//
|
||||
// New body buffer is allocated if dst is nil.
|
||||
//
|
||||
// ErrTimeout error is returned if url contents couldn't be fetched
|
||||
// until the given deadline.
|
||||
func GetDeadline(dst []byte, url string, deadline time.Time) (statusCode int, body []byte, err error) {
|
||||
|
@ -134,11 +126,12 @@ func GetDeadline(dst []byte, url string, deadline time.Time) (statusCode int, bo
|
|||
|
||||
// Post sends POST request to the given url with the given POST arguments.
|
||||
//
|
||||
// The contents of dst will be replaced by the body and returned, if the dst
|
||||
// is too small a new slice will be allocated.
|
||||
// Response body is appended to dst, which is returned as body.
|
||||
//
|
||||
// The function follows redirects. Use Do* for manually handling redirects.
|
||||
//
|
||||
// New body buffer is allocated if dst is nil.
|
||||
//
|
||||
// Empty POST body is sent if postArgs is nil.
|
||||
func Post(dst []byte, url string, postArgs *Args) (statusCode int, body []byte, err error) {
|
||||
return defaultClient.Post(dst, url, postArgs)
|
||||
|
@ -159,10 +152,6 @@ type Client struct {
|
|||
// Default client name is used if not set.
|
||||
Name string
|
||||
|
||||
// NoDefaultUserAgentHeader when set to true, causes the default
|
||||
// User-Agent header to be excluded from the Request.
|
||||
NoDefaultUserAgentHeader bool
|
||||
|
||||
// Callback for establishing new connections to hosts.
|
||||
//
|
||||
// Default Dial is used if not set.
|
||||
|
@ -193,11 +182,6 @@ type Client struct {
|
|||
// after DefaultMaxIdleConnDuration.
|
||||
MaxIdleConnDuration time.Duration
|
||||
|
||||
// Maximum number of attempts for idempotent calls
|
||||
//
|
||||
// DefaultMaxIdemponentCallAttempts is used if not set.
|
||||
MaxIdemponentCallAttempts int
|
||||
|
||||
// Per-connection buffer size for responses' reading.
|
||||
// This also limits the maximum header size.
|
||||
//
|
||||
|
@ -227,59 +211,41 @@ type Client struct {
|
|||
// By default response body size is unlimited.
|
||||
MaxResponseBodySize int
|
||||
|
||||
// Header names are passed as-is without normalization
|
||||
// if this option is set.
|
||||
//
|
||||
// Disabled header names' normalization may be useful only for proxying
|
||||
// responses to other clients expecting case-sensitive
|
||||
// header names. See https://github.com/valyala/fasthttp/issues/57
|
||||
// for details.
|
||||
//
|
||||
// By default request and response header names are normalized, i.e.
|
||||
// The first letter and the first letters following dashes
|
||||
// are uppercased, while all the other letters are lowercased.
|
||||
// Examples:
|
||||
//
|
||||
// * HOST -> Host
|
||||
// * content-type -> Content-Type
|
||||
// * cONTENT-lenGTH -> Content-Length
|
||||
DisableHeaderNamesNormalizing bool
|
||||
// The maximum number of idempotent requests the client can make.
|
||||
MaxIdempotentRequestAttempts int
|
||||
|
||||
mLock sync.Mutex
|
||||
m map[string]*HostClient
|
||||
ms map[string]*HostClient
|
||||
}
|
||||
|
||||
// Get returns the status code and body of url.
|
||||
//
|
||||
// The contents of dst will be replaced by the body and returned, if the dst
|
||||
// is too small a new slice will be allocated.
|
||||
// Get appends url contents to dst and returns it as body.
|
||||
//
|
||||
// The function follows redirects. Use Do* for manually handling redirects.
|
||||
//
|
||||
// New body buffer is allocated if dst is nil.
|
||||
func (c *Client) Get(dst []byte, url string) (statusCode int, body []byte, err error) {
|
||||
return clientGetURL(dst, url, c)
|
||||
}
|
||||
|
||||
// GetTimeout returns the status code and body of url.
|
||||
//
|
||||
// The contents of dst will be replaced by the body and returned, if the dst
|
||||
// is too small a new slice will be allocated.
|
||||
// GetTimeout appends url contents to dst and returns it as body.
|
||||
//
|
||||
// The function follows redirects. Use Do* for manually handling redirects.
|
||||
//
|
||||
// New body buffer is allocated if dst is nil.
|
||||
//
|
||||
// ErrTimeout error is returned if url contents couldn't be fetched
|
||||
// during the given timeout.
|
||||
func (c *Client) GetTimeout(dst []byte, url string, timeout time.Duration) (statusCode int, body []byte, err error) {
|
||||
return clientGetURLTimeout(dst, url, timeout, c)
|
||||
}
|
||||
|
||||
// GetDeadline returns the status code and body of url.
|
||||
//
|
||||
// The contents of dst will be replaced by the body and returned, if the dst
|
||||
// is too small a new slice will be allocated.
|
||||
// GetDeadline appends url contents to dst and returns it as body.
|
||||
//
|
||||
// The function follows redirects. Use Do* for manually handling redirects.
|
||||
//
|
||||
// New body buffer is allocated if dst is nil.
|
||||
//
|
||||
// ErrTimeout error is returned if url contents couldn't be fetched
|
||||
// until the given deadline.
|
||||
func (c *Client) GetDeadline(dst []byte, url string, deadline time.Time) (statusCode int, body []byte, err error) {
|
||||
|
@ -288,11 +254,12 @@ func (c *Client) GetDeadline(dst []byte, url string, deadline time.Time) (status
|
|||
|
||||
// Post sends POST request to the given url with the given POST arguments.
|
||||
//
|
||||
// The contents of dst will be replaced by the body and returned, if the dst
|
||||
// is too small a new slice will be allocated.
|
||||
// Response body is appended to dst, which is returned as body.
|
||||
//
|
||||
// The function follows redirects. Use Do* for manually handling redirects.
|
||||
//
|
||||
// New body buffer is allocated if dst is nil.
|
||||
//
|
||||
// Empty POST body is sent if postArgs is nil.
|
||||
func (c *Client) Post(dst []byte, url string, postArgs *Args) (statusCode int, body []byte, err error) {
|
||||
return clientPostURL(dst, url, postArgs, c)
|
||||
|
@ -321,11 +288,6 @@ func (c *Client) Post(dst []byte, url string, postArgs *Args) (statusCode int, b
|
|||
//
|
||||
// It is recommended obtaining req and resp via AcquireRequest
|
||||
// and AcquireResponse in performance-critical code.
|
||||
//
|
||||
// Warning: DoTimeout does not terminate the request itself. The request will
|
||||
// continue in the background and the response will be discarded.
|
||||
// If requests take too long and the connection pool gets filled up please
|
||||
// try setting a ReadTimeout.
|
||||
func (c *Client) DoTimeout(req *Request, resp *Response, timeout time.Duration) error {
|
||||
return clientDoTimeout(req, resp, timeout, c)
|
||||
}
|
||||
|
@ -406,22 +368,20 @@ func (c *Client) Do(req *Request, resp *Response) error {
|
|||
hc := m[string(host)]
|
||||
if hc == nil {
|
||||
hc = &HostClient{
|
||||
Addr: addMissingPort(string(host), isTLS),
|
||||
Name: c.Name,
|
||||
NoDefaultUserAgentHeader: c.NoDefaultUserAgentHeader,
|
||||
Dial: c.Dial,
|
||||
DialDualStack: c.DialDualStack,
|
||||
IsTLS: isTLS,
|
||||
TLSConfig: c.TLSConfig,
|
||||
MaxConns: c.MaxConnsPerHost,
|
||||
MaxIdleConnDuration: c.MaxIdleConnDuration,
|
||||
MaxIdemponentCallAttempts: c.MaxIdemponentCallAttempts,
|
||||
ReadBufferSize: c.ReadBufferSize,
|
||||
WriteBufferSize: c.WriteBufferSize,
|
||||
ReadTimeout: c.ReadTimeout,
|
||||
WriteTimeout: c.WriteTimeout,
|
||||
MaxResponseBodySize: c.MaxResponseBodySize,
|
||||
DisableHeaderNamesNormalizing: c.DisableHeaderNamesNormalizing,
|
||||
Addr: addMissingPort(string(host), isTLS),
|
||||
Name: c.Name,
|
||||
Dial: c.Dial,
|
||||
DialDualStack: c.DialDualStack,
|
||||
IsTLS: isTLS,
|
||||
TLSConfig: c.TLSConfig,
|
||||
MaxConns: c.MaxConnsPerHost,
|
||||
MaxIdleConnDuration: c.MaxIdleConnDuration,
|
||||
ReadBufferSize: c.ReadBufferSize,
|
||||
WriteBufferSize: c.WriteBufferSize,
|
||||
ReadTimeout: c.ReadTimeout,
|
||||
WriteTimeout: c.WriteTimeout,
|
||||
MaxResponseBodySize: c.MaxResponseBodySize,
|
||||
MaxIdempotentRequestAttempts: c.MaxIdempotentRequestAttempts,
|
||||
}
|
||||
m[string(host)] = hc
|
||||
if len(m) == 1 {
|
||||
|
@ -439,15 +399,11 @@ func (c *Client) Do(req *Request, resp *Response) error {
|
|||
|
||||
func (c *Client) mCleaner(m map[string]*HostClient) {
|
||||
mustStop := false
|
||||
|
||||
for {
|
||||
t := time.Now()
|
||||
c.mLock.Lock()
|
||||
for k, v := range m {
|
||||
v.connsLock.Lock()
|
||||
shouldRemove := v.connsCount == 0
|
||||
v.connsLock.Unlock()
|
||||
|
||||
if shouldRemove {
|
||||
if t.Sub(v.LastUseTime()) > time.Minute {
|
||||
delete(m, k)
|
||||
}
|
||||
}
|
||||
|
@ -472,9 +428,6 @@ const DefaultMaxConnsPerHost = 512
|
|||
// connection is closed.
|
||||
const DefaultMaxIdleConnDuration = 10 * time.Second
|
||||
|
||||
// DefaultMaxIdemponentCallAttempts is the default idempotent calls attempts count.
|
||||
const DefaultMaxIdemponentCallAttempts = 5
|
||||
|
||||
// DialFunc must establish connection to addr.
|
||||
//
|
||||
// There is no need in establishing TLS (SSL) connection for https.
|
||||
|
@ -516,10 +469,6 @@ type HostClient struct {
|
|||
// Client name. Used in User-Agent request header.
|
||||
Name string
|
||||
|
||||
// NoDefaultUserAgentHeader when set to true, causes the default
|
||||
// User-Agent header to be excluded from the Request.
|
||||
NoDefaultUserAgentHeader bool
|
||||
|
||||
// Callback for establishing new connection to the host.
|
||||
//
|
||||
// Default Dial is used if not set.
|
||||
|
@ -544,9 +493,6 @@ type HostClient struct {
|
|||
// Maximum number of connections which may be established to all hosts
|
||||
// listed in Addr.
|
||||
//
|
||||
// You can change this value while the HostClient is being used
|
||||
// using HostClient.SetMaxConns(value)
|
||||
//
|
||||
// DefaultMaxConnsPerHost is used if not set.
|
||||
MaxConns int
|
||||
|
||||
|
@ -561,11 +507,6 @@ type HostClient struct {
|
|||
// after DefaultMaxIdleConnDuration.
|
||||
MaxIdleConnDuration time.Duration
|
||||
|
||||
// Maximum number of attempts for idempotent calls
|
||||
//
|
||||
// DefaultMaxIdemponentCallAttempts is used if not set.
|
||||
MaxIdemponentCallAttempts int
|
||||
|
||||
// Per-connection buffer size for responses' reading.
|
||||
// This also limits the maximum header size.
|
||||
//
|
||||
|
@ -595,23 +536,8 @@ type HostClient struct {
|
|||
// By default response body size is unlimited.
|
||||
MaxResponseBodySize int
|
||||
|
||||
// Header names are passed as-is without normalization
|
||||
// if this option is set.
|
||||
//
|
||||
// Disabled header names' normalization may be useful only for proxying
|
||||
// responses to other clients expecting case-sensitive
|
||||
// header names. See https://github.com/valyala/fasthttp/issues/57
|
||||
// for details.
|
||||
//
|
||||
// By default request and response header names are normalized, i.e.
|
||||
// The first letter and the first letters following dashes
|
||||
// are uppercased, while all the other letters are lowercased.
|
||||
// Examples:
|
||||
//
|
||||
// * HOST -> Host
|
||||
// * content-type -> Content-Type
|
||||
// * cONTENT-lenGTH -> Content-Length
|
||||
DisableHeaderNamesNormalizing bool
|
||||
// The maximum number of idempotent requests the client can make.
|
||||
MaxIdempotentRequestAttempts int
|
||||
|
||||
clientName atomic.Value
|
||||
lastUseTime uint32
|
||||
|
@ -630,7 +556,7 @@ type HostClient struct {
|
|||
readerPool sync.Pool
|
||||
writerPool sync.Pool
|
||||
|
||||
pendingRequests int32
|
||||
pendingRequests uint64
|
||||
|
||||
connsCleanerRun bool
|
||||
}
|
||||
|
@ -640,6 +566,9 @@ type clientConn struct {
|
|||
|
||||
createdTime time.Time
|
||||
lastUseTime time.Time
|
||||
|
||||
lastReadDeadlineTime time.Time
|
||||
lastWriteDeadlineTime time.Time
|
||||
}
|
||||
|
||||
var startTimeUnix = time.Now().Unix()
|
||||
|
@ -650,36 +579,33 @@ func (c *HostClient) LastUseTime() time.Time {
|
|||
return time.Unix(startTimeUnix+int64(n), 0)
|
||||
}
|
||||
|
||||
// Get returns the status code and body of url.
|
||||
//
|
||||
// The contents of dst will be replaced by the body and returned, if the dst
|
||||
// is too small a new slice will be allocated.
|
||||
// Get appends url contents to dst and returns it as body.
|
||||
//
|
||||
// The function follows redirects. Use Do* for manually handling redirects.
|
||||
//
|
||||
// New body buffer is allocated if dst is nil.
|
||||
func (c *HostClient) Get(dst []byte, url string) (statusCode int, body []byte, err error) {
|
||||
return clientGetURL(dst, url, c)
|
||||
}
|
||||
|
||||
// GetTimeout returns the status code and body of url.
|
||||
//
|
||||
// The contents of dst will be replaced by the body and returned, if the dst
|
||||
// is too small a new slice will be allocated.
|
||||
// GetTimeout appends url contents to dst and returns it as body.
|
||||
//
|
||||
// The function follows redirects. Use Do* for manually handling redirects.
|
||||
//
|
||||
// New body buffer is allocated if dst is nil.
|
||||
//
|
||||
// ErrTimeout error is returned if url contents couldn't be fetched
|
||||
// during the given timeout.
|
||||
func (c *HostClient) GetTimeout(dst []byte, url string, timeout time.Duration) (statusCode int, body []byte, err error) {
|
||||
return clientGetURLTimeout(dst, url, timeout, c)
|
||||
}
|
||||
|
||||
// GetDeadline returns the status code and body of url.
|
||||
//
|
||||
// The contents of dst will be replaced by the body and returned, if the dst
|
||||
// is too small a new slice will be allocated.
|
||||
// GetDeadline appends url contents to dst and returns it as body.
|
||||
//
|
||||
// The function follows redirects. Use Do* for manually handling redirects.
|
||||
//
|
||||
// New body buffer is allocated if dst is nil.
|
||||
//
|
||||
// ErrTimeout error is returned if url contents couldn't be fetched
|
||||
// until the given deadline.
|
||||
func (c *HostClient) GetDeadline(dst []byte, url string, deadline time.Time) (statusCode int, body []byte, err error) {
|
||||
|
@ -688,11 +614,12 @@ func (c *HostClient) GetDeadline(dst []byte, url string, deadline time.Time) (st
|
|||
|
||||
// Post sends POST request to the given url with the given POST arguments.
|
||||
//
|
||||
// The contents of dst will be replaced by the body and returned, if the dst
|
||||
// is too small a new slice will be allocated.
|
||||
// Response body is appended to dst, which is returned as body.
|
||||
//
|
||||
// The function follows redirects. Use Do* for manually handling redirects.
|
||||
//
|
||||
// New body buffer is allocated if dst is nil.
|
||||
//
|
||||
// Empty POST body is sent if postArgs is nil.
|
||||
func (c *HostClient) Post(dst []byte, url string, postArgs *Args) (statusCode int, body []byte, err error) {
|
||||
return clientPostURL(dst, url, postArgs, c)
|
||||
|
@ -753,7 +680,7 @@ func clientGetURLDeadline(dst []byte, url string, deadline time.Time, c clientDo
|
|||
}
|
||||
}()
|
||||
|
||||
tc := AcquireTimer(timeout)
|
||||
tc := acquireTimer(timeout)
|
||||
select {
|
||||
case resp := <-ch:
|
||||
ReleaseRequest(req)
|
||||
|
@ -765,7 +692,7 @@ func clientGetURLDeadline(dst []byte, url string, deadline time.Time, c clientDo
|
|||
body = dst
|
||||
err = ErrTimeout
|
||||
}
|
||||
ReleaseTimer(tc)
|
||||
releaseTimer(tc)
|
||||
|
||||
return statusCode, body, err
|
||||
}
|
||||
|
@ -799,24 +726,9 @@ func doRequestFollowRedirects(req *Request, dst []byte, url string, c clientDoer
|
|||
resp.keepBodyBuffer = true
|
||||
oldBody := bodyBuf.B
|
||||
bodyBuf.B = dst
|
||||
scheme := req.uri.Scheme()
|
||||
req.schemaUpdate = false
|
||||
|
||||
redirectsCount := 0
|
||||
for {
|
||||
// In case redirect to different scheme
|
||||
if redirectsCount > 0 && !bytes.Equal(scheme, req.uri.Scheme()) {
|
||||
if strings.HasPrefix(url, string(strHTTPS)) {
|
||||
req.isTLS = true
|
||||
req.uri.SetSchemeBytes(strHTTPS)
|
||||
} else {
|
||||
req.isTLS = false
|
||||
req.uri.SetSchemeBytes(strHTTP)
|
||||
}
|
||||
scheme = req.uri.Scheme()
|
||||
req.schemaUpdate = true
|
||||
}
|
||||
|
||||
req.parsedURI = false
|
||||
req.Header.host = req.Header.host[:0]
|
||||
req.SetRequestURI(url)
|
||||
|
@ -825,11 +737,7 @@ func doRequestFollowRedirects(req *Request, dst []byte, url string, c clientDoer
|
|||
break
|
||||
}
|
||||
statusCode = resp.Header.StatusCode()
|
||||
if statusCode != StatusMovedPermanently &&
|
||||
statusCode != StatusFound &&
|
||||
statusCode != StatusSeeOther &&
|
||||
statusCode != StatusTemporaryRedirect &&
|
||||
statusCode != StatusPermanentRedirect {
|
||||
if statusCode != StatusMovedPermanently && statusCode != StatusFound && statusCode != StatusSeeOther {
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -930,11 +838,6 @@ func ReleaseResponse(resp *Response) {
|
|||
//
|
||||
// It is recommended obtaining req and resp via AcquireRequest
|
||||
// and AcquireResponse in performance-critical code.
|
||||
//
|
||||
// Warning: DoTimeout does not terminate the request itself. The request will
|
||||
// continue in the background and the response will be discarded.
|
||||
// If requests take too long and the connection pool gets filled up please
|
||||
// try setting a ReadTimeout.
|
||||
func (c *HostClient) DoTimeout(req *Request, resp *Response, timeout time.Duration) error {
|
||||
return clientDoTimeout(req, resp, timeout, c)
|
||||
}
|
||||
|
@ -985,9 +888,6 @@ func clientDoDeadline(req *Request, resp *Response, deadline time.Time, c client
|
|||
req.copyToSkipBody(reqCopy)
|
||||
swapRequestBody(req, reqCopy)
|
||||
respCopy := AcquireResponse()
|
||||
// Not calling resp.copyToSkipBody(respCopy) here to avoid
|
||||
// unexpected messing with headers
|
||||
respCopy.SkipBody = resp.SkipBody
|
||||
|
||||
// Note that the request continues execution on ErrTimeout until
|
||||
// client-specific ReadTimeout exceeds. This helps limiting load
|
||||
|
@ -996,20 +896,11 @@ func clientDoDeadline(req *Request, resp *Response, deadline time.Time, c client
|
|||
// Without this 'hack' the load on slow host could exceed MaxConns*
|
||||
// concurrent requests, since timed out requests on client side
|
||||
// usually continue execution on the host.
|
||||
|
||||
var cleanup int32
|
||||
go func() {
|
||||
errDo := c.Do(reqCopy, respCopy)
|
||||
if atomic.LoadInt32(&cleanup) == 1 {
|
||||
ReleaseResponse(respCopy)
|
||||
ReleaseRequest(reqCopy)
|
||||
errorChPool.Put(chv)
|
||||
} else {
|
||||
ch <- errDo
|
||||
}
|
||||
ch <- c.Do(reqCopy, respCopy)
|
||||
}()
|
||||
|
||||
tc := AcquireTimer(timeout)
|
||||
tc := acquireTimer(timeout)
|
||||
var err error
|
||||
select {
|
||||
case err = <-ch:
|
||||
|
@ -1022,10 +913,9 @@ func clientDoDeadline(req *Request, resp *Response, deadline time.Time, c client
|
|||
ReleaseRequest(reqCopy)
|
||||
errorChPool.Put(chv)
|
||||
case <-tc.C:
|
||||
atomic.StoreInt32(&cleanup, 1)
|
||||
err = ErrTimeout
|
||||
}
|
||||
ReleaseTimer(tc)
|
||||
releaseTimer(tc)
|
||||
|
||||
return err
|
||||
}
|
||||
|
@ -1049,13 +939,13 @@ var errorChPool sync.Pool
|
|||
func (c *HostClient) Do(req *Request, resp *Response) error {
|
||||
var err error
|
||||
var retry bool
|
||||
maxAttempts := c.MaxIdemponentCallAttempts
|
||||
maxAttempts := c.MaxIdempotentRequestAttempts
|
||||
if maxAttempts <= 0 {
|
||||
maxAttempts = DefaultMaxIdemponentCallAttempts
|
||||
maxAttempts = 5
|
||||
}
|
||||
attempts := 0
|
||||
|
||||
atomic.AddInt32(&c.pendingRequests, 1)
|
||||
atomic.AddUint64(&c.pendingRequests, 1)
|
||||
for {
|
||||
retry, err = c.do(req, resp)
|
||||
if err == nil || !retry {
|
||||
|
@ -1079,7 +969,7 @@ func (c *HostClient) Do(req *Request, resp *Response) error {
|
|||
break
|
||||
}
|
||||
}
|
||||
atomic.AddInt32(&c.pendingRequests, -1)
|
||||
atomic.AddUint64(&c.pendingRequests, ^uint64(0))
|
||||
|
||||
if err == io.EOF {
|
||||
err = ErrConnectionClosed
|
||||
|
@ -1093,7 +983,7 @@ func (c *HostClient) Do(req *Request, resp *Response) error {
|
|||
// This function may be used for balancing load among multiple HostClient
|
||||
// instances.
|
||||
func (c *HostClient) PendingRequests() int {
|
||||
return int(atomic.LoadInt32(&c.pendingRequests))
|
||||
return int(atomic.LoadUint64(&c.pendingRequests))
|
||||
}
|
||||
|
||||
func isIdempotent(req *Request) bool {
|
||||
|
@ -1124,37 +1014,29 @@ func (c *HostClient) doNonNilReqResp(req *Request, resp *Response) (bool, error)
|
|||
panic("BUG: resp cannot be nil")
|
||||
}
|
||||
|
||||
atomic.StoreUint32(&c.lastUseTime, uint32(time.Now().Unix()-startTimeUnix))
|
||||
atomic.StoreUint32(&c.lastUseTime, uint32(CoarseTimeNow().Unix()-startTimeUnix))
|
||||
|
||||
// Free up resources occupied by response before sending the request,
|
||||
// so the GC may reclaim these resources (e.g. response body).
|
||||
resp.Reset()
|
||||
|
||||
// If we detected a redirect to another schema
|
||||
if req.schemaUpdate {
|
||||
c.IsTLS = bytes.Equal(req.URI().Scheme(), strHTTPS)
|
||||
c.Addr = addMissingPort(string(req.Host()), c.IsTLS)
|
||||
c.addrIdx = 0
|
||||
c.addrs = nil
|
||||
req.schemaUpdate = false
|
||||
req.SetConnectionClose()
|
||||
}
|
||||
|
||||
cc, err := c.acquireConn()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
conn := cc.c
|
||||
|
||||
resp.parseNetConn(conn)
|
||||
|
||||
if c.WriteTimeout > 0 {
|
||||
// Set Deadline every time, since golang has fixed the performance issue
|
||||
// See https://github.com/golang/go/issues/15133#issuecomment-271571395 for details
|
||||
currentTime := time.Now()
|
||||
if err = conn.SetWriteDeadline(currentTime.Add(c.WriteTimeout)); err != nil {
|
||||
c.closeConn(cc)
|
||||
return true, err
|
||||
// Optimization: update write deadline only if more than 25%
|
||||
// of the last write deadline exceeded.
|
||||
// See https://github.com/golang/go/issues/15133 for details.
|
||||
currentTime := CoarseTimeNow()
|
||||
if currentTime.Sub(cc.lastWriteDeadlineTime) > (c.WriteTimeout >> 2) {
|
||||
if err = conn.SetWriteDeadline(currentTime.Add(c.WriteTimeout)); err != nil {
|
||||
c.closeConn(cc)
|
||||
return true, err
|
||||
}
|
||||
cc.lastWriteDeadlineTime = currentTime
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1170,6 +1052,9 @@ func (c *HostClient) doNonNilReqResp(req *Request, resp *Response) (bool, error)
|
|||
}
|
||||
bw := c.acquireWriter(conn)
|
||||
err = req.Write(bw)
|
||||
if len(userAgentOld) == 0 {
|
||||
req.Header.userAgent = userAgentOld
|
||||
}
|
||||
|
||||
if resetConnection {
|
||||
req.Header.ResetConnectionClose()
|
||||
|
@ -1186,29 +1071,28 @@ func (c *HostClient) doNonNilReqResp(req *Request, resp *Response) (bool, error)
|
|||
c.releaseWriter(bw)
|
||||
|
||||
if c.ReadTimeout > 0 {
|
||||
// Set Deadline every time, since golang has fixed the performance issue
|
||||
// See https://github.com/golang/go/issues/15133#issuecomment-271571395 for details
|
||||
currentTime := time.Now()
|
||||
if err = conn.SetReadDeadline(currentTime.Add(c.ReadTimeout)); err != nil {
|
||||
c.closeConn(cc)
|
||||
return true, err
|
||||
// Optimization: update read deadline only if more than 25%
|
||||
// of the last read deadline exceeded.
|
||||
// See https://github.com/golang/go/issues/15133 for details.
|
||||
currentTime := CoarseTimeNow()
|
||||
if currentTime.Sub(cc.lastReadDeadlineTime) > (c.ReadTimeout >> 2) {
|
||||
if err = conn.SetReadDeadline(currentTime.Add(c.ReadTimeout)); err != nil {
|
||||
c.closeConn(cc)
|
||||
return true, err
|
||||
}
|
||||
cc.lastReadDeadlineTime = currentTime
|
||||
}
|
||||
}
|
||||
|
||||
if !req.Header.IsGet() && req.Header.IsHead() {
|
||||
resp.SkipBody = true
|
||||
}
|
||||
if c.DisableHeaderNamesNormalizing {
|
||||
resp.Header.DisableNormalizing()
|
||||
}
|
||||
|
||||
br := c.acquireReader(conn)
|
||||
if err = resp.ReadLimitBody(br, c.MaxResponseBodySize); err != nil {
|
||||
c.releaseReader(br)
|
||||
c.closeConn(cc)
|
||||
// Don't retry in case of ErrBodyTooLarge since we will just get the same again.
|
||||
retry := err != ErrBodyTooLarge
|
||||
return retry, err
|
||||
return true, err
|
||||
}
|
||||
c.releaseReader(br)
|
||||
|
||||
|
@ -1243,12 +1127,6 @@ var (
|
|||
"Make sure the server returns 'Connection: close' response header before closing the connection")
|
||||
)
|
||||
|
||||
func (c *HostClient) SetMaxConns(newMaxConns int) {
|
||||
c.connsLock.Lock()
|
||||
c.MaxConns = newMaxConns
|
||||
c.connsLock.Unlock()
|
||||
}
|
||||
|
||||
func (c *HostClient) acquireConn() (*clientConn, error) {
|
||||
var cc *clientConn
|
||||
createConn := false
|
||||
|
@ -1318,12 +1196,6 @@ func (c *HostClient) connsCleaner() {
|
|||
for i < n && currentTime.Sub(conns[i].lastUseTime) > maxIdleConnDuration {
|
||||
i++
|
||||
}
|
||||
sleepFor := maxIdleConnDuration
|
||||
if i < n {
|
||||
// + 1 so we actually sleep past the expiration time and not up to it.
|
||||
// Otherwise the > check above would still fail.
|
||||
sleepFor = maxIdleConnDuration - currentTime.Sub(conns[i].lastUseTime) + 1
|
||||
}
|
||||
scratch = append(scratch[:0], conns[:i]...)
|
||||
if i > 0 {
|
||||
m := copy(conns, conns[i:])
|
||||
|
@ -1351,7 +1223,7 @@ func (c *HostClient) connsCleaner() {
|
|||
break
|
||||
}
|
||||
|
||||
time.Sleep(sleepFor)
|
||||
time.Sleep(maxIdleConnDuration)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1374,20 +1246,19 @@ func acquireClientConn(conn net.Conn) *clientConn {
|
|||
}
|
||||
cc := v.(*clientConn)
|
||||
cc.c = conn
|
||||
cc.createdTime = time.Now()
|
||||
cc.createdTime = CoarseTimeNow()
|
||||
return cc
|
||||
}
|
||||
|
||||
func releaseClientConn(cc *clientConn) {
|
||||
// Reset all fields.
|
||||
*cc = clientConn{}
|
||||
cc.c = nil
|
||||
clientConnPool.Put(cc)
|
||||
}
|
||||
|
||||
var clientConnPool sync.Pool
|
||||
|
||||
func (c *HostClient) releaseConn(cc *clientConn) {
|
||||
cc.lastUseTime = time.Now()
|
||||
cc.lastUseTime = CoarseTimeNow()
|
||||
c.connsLock.Lock()
|
||||
c.conns = append(c.conns, cc)
|
||||
c.connsLock.Unlock()
|
||||
|
@ -1581,7 +1452,7 @@ func (c *HostClient) getClientName() []byte {
|
|||
var clientName []byte
|
||||
if v == nil {
|
||||
clientName = []byte(c.Name)
|
||||
if len(clientName) == 0 && !c.NoDefaultUserAgentHeader {
|
||||
if len(clientName) == 0 {
|
||||
clientName = defaultUserAgent
|
||||
}
|
||||
c.clientName.Store(clientName)
|
||||
|
@ -1623,7 +1494,7 @@ type PipelineClient struct {
|
|||
|
||||
// The maximum number of concurrent connections to the Addr.
|
||||
//
|
||||
// A single connection is used by default.
|
||||
// A sinle connection is used by default.
|
||||
MaxConns int
|
||||
|
||||
// The maximum number of pending pipelined requests over
|
||||
|
@ -1748,11 +1619,6 @@ type pipelineWork struct {
|
|||
//
|
||||
// It is recommended obtaining req and resp via AcquireRequest
|
||||
// and AcquireResponse in performance-critical code.
|
||||
//
|
||||
// Warning: DoTimeout does not terminate the request itself. The request will
|
||||
// continue in the background and the response will be discarded.
|
||||
// If requests take too long and the connection pool gets filled up please
|
||||
// try setting a ReadTimeout.
|
||||
func (c *PipelineClient) DoTimeout(req *Request, resp *Response, timeout time.Duration) error {
|
||||
return c.DoDeadline(req, resp, time.Now().Add(timeout))
|
||||
}
|
||||
|
@ -2058,6 +1924,8 @@ func (c *pipelineConnClient) writer(conn net.Conn, stopCh <-chan struct{}) error
|
|||
|
||||
w *pipelineWork
|
||||
err error
|
||||
|
||||
lastWriteDeadlineTime time.Time
|
||||
)
|
||||
close(instantTimerCh)
|
||||
for {
|
||||
|
@ -2089,16 +1957,18 @@ func (c *pipelineConnClient) writer(conn net.Conn, stopCh <-chan struct{}) error
|
|||
continue
|
||||
}
|
||||
|
||||
w.resp.parseNetConn(conn)
|
||||
|
||||
if writeTimeout > 0 {
|
||||
// Set Deadline every time, since golang has fixed the performance issue
|
||||
// See https://github.com/golang/go/issues/15133#issuecomment-271571395 for details
|
||||
currentTime := time.Now()
|
||||
if err = conn.SetWriteDeadline(currentTime.Add(writeTimeout)); err != nil {
|
||||
w.err = err
|
||||
w.done <- struct{}{}
|
||||
return err
|
||||
// Optimization: update write deadline only if more than 25%
|
||||
// of the last write deadline exceeded.
|
||||
// See https://github.com/golang/go/issues/15133 for details.
|
||||
currentTime := CoarseTimeNow()
|
||||
if currentTime.Sub(lastWriteDeadlineTime) > (writeTimeout >> 2) {
|
||||
if err = conn.SetWriteDeadline(currentTime.Add(writeTimeout)); err != nil {
|
||||
w.err = err
|
||||
w.done <- struct{}{}
|
||||
return err
|
||||
}
|
||||
lastWriteDeadlineTime = currentTime
|
||||
}
|
||||
}
|
||||
if err = w.req.Write(bw); err != nil {
|
||||
|
@ -2152,6 +2022,8 @@ func (c *pipelineConnClient) reader(conn net.Conn, stopCh <-chan struct{}) error
|
|||
var (
|
||||
w *pipelineWork
|
||||
err error
|
||||
|
||||
lastReadDeadlineTime time.Time
|
||||
)
|
||||
for {
|
||||
select {
|
||||
|
@ -2167,13 +2039,17 @@ func (c *pipelineConnClient) reader(conn net.Conn, stopCh <-chan struct{}) error
|
|||
}
|
||||
|
||||
if readTimeout > 0 {
|
||||
// Set Deadline every time, since golang has fixed the performance issue
|
||||
// See https://github.com/golang/go/issues/15133#issuecomment-271571395 for details
|
||||
currentTime := time.Now()
|
||||
if err = conn.SetReadDeadline(currentTime.Add(readTimeout)); err != nil {
|
||||
w.err = err
|
||||
w.done <- struct{}{}
|
||||
return err
|
||||
// Optimization: update read deadline only if more than 25%
|
||||
// of the last read deadline exceeded.
|
||||
// See https://github.com/golang/go/issues/15133 for details.
|
||||
currentTime := CoarseTimeNow()
|
||||
if currentTime.Sub(lastReadDeadlineTime) > (readTimeout >> 2) {
|
||||
if err = conn.SetReadDeadline(currentTime.Add(readTimeout)); err != nil {
|
||||
w.err = err
|
||||
w.done <- struct{}{}
|
||||
return err
|
||||
}
|
||||
lastReadDeadlineTime = currentTime
|
||||
}
|
||||
}
|
||||
if err = w.resp.Read(br); err != nil {
|
28
vendor/github.com/VictoriaMetrics/fasthttp/coarseTime.go
generated
vendored
Normal file
28
vendor/github.com/VictoriaMetrics/fasthttp/coarseTime.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
package fasthttp
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CoarseTimeNow returns the current time truncated to the nearest second.
|
||||
//
|
||||
// This is a faster alternative to time.Now().
|
||||
func CoarseTimeNow() time.Time {
|
||||
tp := coarseTime.Load().(*time.Time)
|
||||
return *tp
|
||||
}
|
||||
|
||||
func init() {
|
||||
t := time.Now().Truncate(time.Second)
|
||||
coarseTime.Store(&t)
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
t := time.Now().Truncate(time.Second)
|
||||
coarseTime.Store(&t)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
var coarseTime atomic.Value
|
|
@ -7,11 +7,11 @@ import (
|
|||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/VictoriaMetrics/fasthttp/stackless"
|
||||
"github.com/klauspost/compress/flate"
|
||||
"github.com/klauspost/compress/gzip"
|
||||
"github.com/klauspost/compress/zlib"
|
||||
"github.com/valyala/bytebufferpool"
|
||||
"github.com/valyala/fasthttp/stackless"
|
||||
)
|
||||
|
||||
// Supported compression levels.
|
||||
|
@ -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{
|
||||
|
@ -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,
|
||||
|
@ -422,7 +424,7 @@ func isFileCompressible(f *os.File, minCompressRatio float64) bool {
|
|||
|
||||
n := 4096 - lr.N
|
||||
zn := len(b.B)
|
||||
bytebufferpool.Put(b)
|
||||
ReleaseByteBuffer(b)
|
||||
return float64(zn) < float64(n)*minCompressRatio
|
||||
}
|
||||
|
|
@ -18,21 +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
|
||||
)
|
||||
|
||||
// AcquireCookie returns an empty Cookie object from the pool.
|
||||
//
|
||||
// The returned object may be returned back to the pool with ReleaseCookie.
|
||||
|
@ -67,13 +52,11 @@ type Cookie struct {
|
|||
key []byte
|
||||
value []byte
|
||||
expire time.Time
|
||||
maxAge int
|
||||
domain []byte
|
||||
path []byte
|
||||
|
||||
httpOnly bool
|
||||
secure bool
|
||||
sameSite CookieSameSite
|
||||
|
||||
bufKV argsKV
|
||||
buf []byte
|
||||
|
@ -85,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.
|
||||
|
@ -113,16 +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.
|
||||
func (c *Cookie) SetSameSite(mode CookieSameSite) {
|
||||
c.sameSite = mode
|
||||
}
|
||||
|
||||
// Path returns cookie path.
|
||||
func (c *Cookie) Path() []byte {
|
||||
return c.path
|
||||
|
@ -157,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
|
||||
|
@ -231,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
|
||||
|
@ -248,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...)
|
||||
|
@ -274,21 +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...)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
|
@ -337,75 +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
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -440,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
|
||||
|
@ -518,17 +394,3 @@ func decodeCookieArg(dst, src []byte, skipQuotes bool) []byte {
|
|||
}
|
||||
return append(dst[:0], src...)
|
||||
}
|
||||
|
||||
// caseInsensitiveCompare does a case insensitive equality comparison of
|
||||
// two []byte. Assumes only letters need to be matched.
|
||||
func caseInsensitiveCompare(a, b []byte) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i]|0x20 != b[i]|0x20 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
|
@ -7,6 +7,9 @@ Fasthttp provides the following features:
|
|||
concurrent keep-alive connections on modern hardware.
|
||||
* Optimized for low memory usage.
|
||||
* Easy 'Connection: Upgrade' support via RequestCtx.Hijack.
|
||||
* Server supports requests' pipelining. Multiple requests may be read from
|
||||
a single network packet and multiple responses may be sent in a single
|
||||
network packet. This may be useful for highly loaded REST services.
|
||||
* Server provides the following anti-DoS limits:
|
||||
|
||||
* The number of concurrent connections.
|
|
@ -8,23 +8,18 @@ import (
|
|||
|
||||
// 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),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,8 +33,7 @@ func (ln *InmemoryListener) Accept() (net.Conn, error) {
|
|||
if !ok {
|
||||
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.
|
||||
|
@ -65,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) {
|
||||
|
@ -75,11 +68,8 @@ 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()
|
||||
cConn.Close()
|
|
@ -8,7 +8,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// NewPipeConns returns new bi-directional connection pipe.
|
||||
// NewPipeConns returns new bi-directonal connection pipe.
|
||||
func NewPipeConns() *PipeConns {
|
||||
ch1 := make(chan *byteBuffer, 4)
|
||||
ch2 := make(chan *byteBuffer, 4)
|
|
@ -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()
|
||||
}
|
||||
|
@ -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
|
||||
}
|
9
vendor/github.com/VictoriaMetrics/fasthttp/go.mod
generated
vendored
Normal file
9
vendor/github.com/VictoriaMetrics/fasthttp/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
module github.com/VictoriaMetrics/fasthttp
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.10.5
|
||||
github.com/valyala/bytebufferpool v1.0.0
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a
|
||||
)
|
6
vendor/github.com/VictoriaMetrics/fasthttp/go.sum
generated
vendored
Normal file
6
vendor/github.com/VictoriaMetrics/fasthttp/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
github.com/klauspost/compress v1.10.5 h1:7q6vHIqubShURwQz8cQK6yIe/xC3IF0Vm7TGfqjewrc=
|
||||
github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
|
@ -20,10 +20,8 @@ import (
|
|||
type ResponseHeader struct {
|
||||
noCopy noCopy
|
||||
|
||||
disableNormalizing bool
|
||||
noHTTP11 bool
|
||||
connectionClose bool
|
||||
noDefaultContentType bool
|
||||
noHTTP11 bool
|
||||
connectionClose bool
|
||||
|
||||
statusCode int
|
||||
contentLength int
|
||||
|
@ -48,9 +46,9 @@ type ResponseHeader struct {
|
|||
type RequestHeader struct {
|
||||
noCopy noCopy
|
||||
|
||||
disableNormalizing bool
|
||||
noHTTP11 bool
|
||||
connectionClose bool
|
||||
noHTTP11 bool
|
||||
connectionClose bool
|
||||
isGet bool
|
||||
|
||||
// These two fields have been moved close to other bool fields
|
||||
// for reducing RequestHeader object size.
|
||||
|
@ -72,10 +70,6 @@ type RequestHeader struct {
|
|||
cookies []argsKV
|
||||
|
||||
rawHeaders []byte
|
||||
|
||||
// stores an immutable copy of headers as they were received from the
|
||||
// wire.
|
||||
rawHeadersCopy []byte
|
||||
}
|
||||
|
||||
// SetContentRange sets 'Content-Range: bytes startPos-endPos/contentLength'
|
||||
|
@ -161,6 +155,12 @@ func (h *RequestHeader) ConnectionClose() bool {
|
|||
return h.connectionClose
|
||||
}
|
||||
|
||||
func (h *RequestHeader) connectionCloseFast() bool {
|
||||
// h.parseRawHeaders() isn't called for performance reasons.
|
||||
// Use ConnectionClose for triggering raw headers parsing.
|
||||
return h.connectionClose
|
||||
}
|
||||
|
||||
// SetConnectionClose sets 'Connection: close' header.
|
||||
func (h *RequestHeader) SetConnectionClose() {
|
||||
// h.parseRawHeaders() isn't called for performance reasons.
|
||||
|
@ -187,11 +187,6 @@ func (h *RequestHeader) ConnectionUpgrade() bool {
|
|||
return hasHeaderValue(h.Peek("Connection"), strUpgrade)
|
||||
}
|
||||
|
||||
// PeekCookie is able to returns cookie by a given key from response.
|
||||
func (h *ResponseHeader) PeekCookie(key string) []byte {
|
||||
return peekArgStr(h.cookies, key)
|
||||
}
|
||||
|
||||
// ContentLength returns Content-Length header value.
|
||||
//
|
||||
// It may be negative:
|
||||
|
@ -221,7 +216,7 @@ func (h *ResponseHeader) SetContentLength(contentLength int) {
|
|||
h.SetConnectionClose()
|
||||
value = strIdentity
|
||||
}
|
||||
h.h = setArgBytes(h.h, strTransferEncoding, value, argsHasValue)
|
||||
h.h = setArgBytes(h.h, strTransferEncoding, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,15 +239,9 @@ func (h *ResponseHeader) mustSkipContentLength() bool {
|
|||
// It may be negative:
|
||||
// -1 means Transfer-Encoding: chunked.
|
||||
func (h *RequestHeader) ContentLength() int {
|
||||
if h.ignoreBody() {
|
||||
if h.noBody() {
|
||||
return 0
|
||||
}
|
||||
return h.realContentLength()
|
||||
}
|
||||
|
||||
// realContentLength returns the actual Content-Length set in the request,
|
||||
// including positive lengths for GET/HEAD requests.
|
||||
func (h *RequestHeader) realContentLength() int {
|
||||
h.parseRawHeaders()
|
||||
return h.contentLength
|
||||
}
|
||||
|
@ -268,7 +257,7 @@ func (h *RequestHeader) SetContentLength(contentLength int) {
|
|||
h.h = delAllArgsBytes(h.h, strTransferEncoding)
|
||||
} else {
|
||||
h.contentLengthBytes = h.contentLengthBytes[:0]
|
||||
h.h = setArgBytes(h.h, strTransferEncoding, strChunked, argsHasValue)
|
||||
h.h = setArgBytes(h.h, strTransferEncoding, strChunked)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,7 +270,7 @@ func (h *ResponseHeader) isCompressibleContentType() bool {
|
|||
// ContentType returns Content-Type header value.
|
||||
func (h *ResponseHeader) ContentType() []byte {
|
||||
contentType := h.contentType
|
||||
if !h.noDefaultContentType && len(h.contentType) == 0 {
|
||||
if len(h.contentType) == 0 {
|
||||
contentType = defaultContentType
|
||||
}
|
||||
return contentType
|
||||
|
@ -513,10 +502,14 @@ func (h *RequestHeader) SetRequestURIBytes(requestURI []byte) {
|
|||
|
||||
// IsGet returns true if request method is GET.
|
||||
func (h *RequestHeader) IsGet() bool {
|
||||
return bytes.Equal(h.Method(), strGet)
|
||||
// Optimize fast path for GET requests.
|
||||
if !h.isGet {
|
||||
h.isGet = bytes.Equal(h.Method(), strGet)
|
||||
}
|
||||
return h.isGet
|
||||
}
|
||||
|
||||
// IsPost returns true if request method is POST.
|
||||
// IsPost returns true if request methos is POST.
|
||||
func (h *RequestHeader) IsPost() bool {
|
||||
return bytes.Equal(h.Method(), strPost)
|
||||
}
|
||||
|
@ -528,6 +521,10 @@ func (h *RequestHeader) IsPut() bool {
|
|||
|
||||
// IsHead returns true if request method is HEAD.
|
||||
func (h *RequestHeader) IsHead() bool {
|
||||
// Fast path
|
||||
if h.isGet {
|
||||
return false
|
||||
}
|
||||
return bytes.Equal(h.Method(), strHead)
|
||||
}
|
||||
|
||||
|
@ -536,26 +533,6 @@ func (h *RequestHeader) IsDelete() bool {
|
|||
return bytes.Equal(h.Method(), strDelete)
|
||||
}
|
||||
|
||||
// IsConnect returns true if request method is CONNECT.
|
||||
func (h *RequestHeader) IsConnect() bool {
|
||||
return bytes.Equal(h.Method(), strConnect)
|
||||
}
|
||||
|
||||
// IsOptions returns true if request method is OPTIONS.
|
||||
func (h *RequestHeader) IsOptions() bool {
|
||||
return bytes.Equal(h.Method(), strOptions)
|
||||
}
|
||||
|
||||
// IsTrace returns true if request method is TRACE.
|
||||
func (h *RequestHeader) IsTrace() bool {
|
||||
return bytes.Equal(h.Method(), strTrace)
|
||||
}
|
||||
|
||||
// IsPatch returns true if request method is PATCH.
|
||||
func (h *RequestHeader) IsPatch() bool {
|
||||
return bytes.Equal(h.Method(), strPatch)
|
||||
}
|
||||
|
||||
// IsHTTP11 returns true if the request is HTTP/1.1.
|
||||
func (h *RequestHeader) IsHTTP11() bool {
|
||||
return !h.noHTTP11
|
||||
|
@ -607,46 +584,8 @@ func (h *RequestHeader) Len() int {
|
|||
return n
|
||||
}
|
||||
|
||||
// DisableNormalizing disables header names' normalization.
|
||||
//
|
||||
// By default all the header names are normalized by uppercasing
|
||||
// the first letter and all the first letters following dashes,
|
||||
// while lowercasing all the other letters.
|
||||
// Examples:
|
||||
//
|
||||
// * CONNECTION -> Connection
|
||||
// * conteNT-tYPE -> Content-Type
|
||||
// * foo-bar-baz -> Foo-Bar-Baz
|
||||
//
|
||||
// Disable header names' normalization only if know what are you doing.
|
||||
func (h *RequestHeader) DisableNormalizing() {
|
||||
h.disableNormalizing = true
|
||||
}
|
||||
|
||||
// DisableNormalizing disables header names' normalization.
|
||||
//
|
||||
// By default all the header names are normalized by uppercasing
|
||||
// the first letter and all the first letters following dashes,
|
||||
// while lowercasing all the other letters.
|
||||
// Examples:
|
||||
//
|
||||
// * CONNECTION -> Connection
|
||||
// * conteNT-tYPE -> Content-Type
|
||||
// * foo-bar-baz -> Foo-Bar-Baz
|
||||
//
|
||||
// Disable header names' normalization only if know what are you doing.
|
||||
func (h *ResponseHeader) DisableNormalizing() {
|
||||
h.disableNormalizing = true
|
||||
}
|
||||
|
||||
// Reset clears response header.
|
||||
func (h *ResponseHeader) Reset() {
|
||||
h.disableNormalizing = false
|
||||
h.noDefaultContentType = false
|
||||
h.resetSkipNormalize()
|
||||
}
|
||||
|
||||
func (h *ResponseHeader) resetSkipNormalize() {
|
||||
h.noHTTP11 = false
|
||||
h.connectionClose = false
|
||||
|
||||
|
@ -663,13 +602,9 @@ func (h *ResponseHeader) resetSkipNormalize() {
|
|||
|
||||
// Reset clears request header.
|
||||
func (h *RequestHeader) Reset() {
|
||||
h.disableNormalizing = false
|
||||
h.resetSkipNormalize()
|
||||
}
|
||||
|
||||
func (h *RequestHeader) resetSkipNormalize() {
|
||||
h.noHTTP11 = false
|
||||
h.connectionClose = false
|
||||
h.isGet = false
|
||||
|
||||
h.contentLength = 0
|
||||
h.contentLengthBytes = h.contentLengthBytes[:0]
|
||||
|
@ -692,10 +627,8 @@ func (h *RequestHeader) resetSkipNormalize() {
|
|||
func (h *ResponseHeader) CopyTo(dst *ResponseHeader) {
|
||||
dst.Reset()
|
||||
|
||||
dst.disableNormalizing = h.disableNormalizing
|
||||
dst.noHTTP11 = h.noHTTP11
|
||||
dst.connectionClose = h.connectionClose
|
||||
dst.noDefaultContentType = h.noDefaultContentType
|
||||
|
||||
dst.statusCode = h.statusCode
|
||||
dst.contentLength = h.contentLength
|
||||
|
@ -710,9 +643,9 @@ func (h *ResponseHeader) CopyTo(dst *ResponseHeader) {
|
|||
func (h *RequestHeader) CopyTo(dst *RequestHeader) {
|
||||
dst.Reset()
|
||||
|
||||
dst.disableNormalizing = h.disableNormalizing
|
||||
dst.noHTTP11 = h.noHTTP11
|
||||
dst.connectionClose = h.connectionClose
|
||||
dst.isGet = h.isGet
|
||||
|
||||
dst.contentLength = h.contentLength
|
||||
dst.contentLengthBytes = append(dst.contentLengthBytes[:0], h.contentLengthBytes...)
|
||||
|
@ -726,7 +659,6 @@ func (h *RequestHeader) CopyTo(dst *RequestHeader) {
|
|||
dst.cookiesCollected = h.cookiesCollected
|
||||
dst.rawHeaders = append(dst.rawHeaders[:0], h.rawHeaders...)
|
||||
dst.rawHeadersParsed = h.rawHeadersParsed
|
||||
dst.rawHeadersCopy = append(dst.rawHeadersCopy[:0], h.rawHeadersCopy...)
|
||||
}
|
||||
|
||||
// VisitAll calls f for each header.
|
||||
|
@ -780,8 +712,6 @@ func (h *RequestHeader) VisitAllCookie(f func(key, value []byte)) {
|
|||
//
|
||||
// f must not retain references to key and/or value after returning.
|
||||
// Copy key and/or value contents before returning if you need retaining them.
|
||||
//
|
||||
// To get the headers in order they were received use VisitAllInOrder.
|
||||
func (h *RequestHeader) VisitAll(f func(key, value []byte)) {
|
||||
h.parseRawHeaders()
|
||||
host := h.Host()
|
||||
|
@ -811,35 +741,16 @@ func (h *RequestHeader) VisitAll(f func(key, value []byte)) {
|
|||
}
|
||||
}
|
||||
|
||||
// VisitAllInOrder calls f for each header in the order they were received.
|
||||
//
|
||||
// f must not retain references to key and/or value after returning.
|
||||
// Copy key and/or value contents before returning if you need retaining them.
|
||||
//
|
||||
// This function is slightly slower than VisitAll because it has to reparse the
|
||||
// raw headers to get the order.
|
||||
func (h *RequestHeader) VisitAllInOrder(f func(key, value []byte)) {
|
||||
h.parseRawHeaders()
|
||||
var s headerScanner
|
||||
s.b = h.rawHeaders
|
||||
s.disableNormalizing = h.disableNormalizing
|
||||
for s.next() {
|
||||
if len(s.key) > 0 {
|
||||
f(s.key, s.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Del deletes header with the given key.
|
||||
func (h *ResponseHeader) Del(key string) {
|
||||
k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing)
|
||||
k := getHeaderKeyBytes(&h.bufKV, key)
|
||||
h.del(k)
|
||||
}
|
||||
|
||||
// DelBytes deletes header with the given key.
|
||||
func (h *ResponseHeader) DelBytes(key []byte) {
|
||||
h.bufKV.key = append(h.bufKV.key[:0], key...)
|
||||
normalizeHeaderKey(h.bufKV.key, h.disableNormalizing)
|
||||
normalizeHeaderKey(h.bufKV.key)
|
||||
h.del(h.bufKV.key)
|
||||
}
|
||||
|
||||
|
@ -863,7 +774,7 @@ func (h *ResponseHeader) del(key []byte) {
|
|||
// Del deletes header with the given key.
|
||||
func (h *RequestHeader) Del(key string) {
|
||||
h.parseRawHeaders()
|
||||
k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing)
|
||||
k := getHeaderKeyBytes(&h.bufKV, key)
|
||||
h.del(k)
|
||||
}
|
||||
|
||||
|
@ -871,7 +782,7 @@ func (h *RequestHeader) Del(key string) {
|
|||
func (h *RequestHeader) DelBytes(key []byte) {
|
||||
h.parseRawHeaders()
|
||||
h.bufKV.key = append(h.bufKV.key[:0], key...)
|
||||
normalizeHeaderKey(h.bufKV.key, h.disableNormalizing)
|
||||
normalizeHeaderKey(h.bufKV.key)
|
||||
h.del(h.bufKV.key)
|
||||
}
|
||||
|
||||
|
@ -899,8 +810,8 @@ func (h *RequestHeader) del(key []byte) {
|
|||
// Multiple headers with the same key may be added with this function.
|
||||
// Use Set for setting a single header for the given key.
|
||||
func (h *ResponseHeader) Add(key, value string) {
|
||||
k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing)
|
||||
h.h = appendArg(h.h, b2s(k), value, argsHasValue)
|
||||
k := getHeaderKeyBytes(&h.bufKV, key)
|
||||
h.h = appendArg(h.h, b2s(k), value)
|
||||
}
|
||||
|
||||
// AddBytesK adds the given 'key: value' header.
|
||||
|
@ -931,7 +842,7 @@ func (h *ResponseHeader) AddBytesKV(key, value []byte) {
|
|||
//
|
||||
// Use Add for setting multiple header values under the same key.
|
||||
func (h *ResponseHeader) Set(key, value string) {
|
||||
initHeaderKV(&h.bufKV, key, value, h.disableNormalizing)
|
||||
initHeaderKV(&h.bufKV, key, value)
|
||||
h.SetCanonical(h.bufKV.key, h.bufKV.value)
|
||||
}
|
||||
|
||||
|
@ -947,7 +858,7 @@ func (h *ResponseHeader) SetBytesK(key []byte, value string) {
|
|||
//
|
||||
// Use AddBytesV for setting multiple header values under the same key.
|
||||
func (h *ResponseHeader) SetBytesV(key string, value []byte) {
|
||||
k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing)
|
||||
k := getHeaderKeyBytes(&h.bufKV, key)
|
||||
h.SetCanonical(k, value)
|
||||
}
|
||||
|
||||
|
@ -956,7 +867,7 @@ func (h *ResponseHeader) SetBytesV(key string, value []byte) {
|
|||
// Use AddBytesKV for setting multiple header values under the same key.
|
||||
func (h *ResponseHeader) SetBytesKV(key, value []byte) {
|
||||
h.bufKV.key = append(h.bufKV.key[:0], key...)
|
||||
normalizeHeaderKey(h.bufKV.key, h.disableNormalizing)
|
||||
normalizeHeaderKey(h.bufKV.key)
|
||||
h.SetCanonical(h.bufKV.key, value)
|
||||
}
|
||||
|
||||
|
@ -983,14 +894,14 @@ func (h *ResponseHeader) SetCanonical(key, value []byte) {
|
|||
h.SetConnectionClose()
|
||||
} else {
|
||||
h.ResetConnectionClose()
|
||||
h.h = setArgBytes(h.h, key, value, argsHasValue)
|
||||
h.h = setArgBytes(h.h, key, value)
|
||||
}
|
||||
case "Transfer-Encoding":
|
||||
// Transfer-Encoding is managed automatically.
|
||||
case "Date":
|
||||
// Date is managed automatically.
|
||||
default:
|
||||
h.h = setArgBytes(h.h, key, value, argsHasValue)
|
||||
h.h = setArgBytes(h.h, key, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -998,14 +909,14 @@ func (h *ResponseHeader) SetCanonical(key, value []byte) {
|
|||
//
|
||||
// It is save re-using the cookie after the function returns.
|
||||
func (h *ResponseHeader) SetCookie(cookie *Cookie) {
|
||||
h.cookies = setArgBytes(h.cookies, cookie.Key(), cookie.Cookie(), argsHasValue)
|
||||
h.cookies = setArgBytes(h.cookies, cookie.Key(), cookie.Cookie())
|
||||
}
|
||||
|
||||
// SetCookie sets 'key: value' cookies.
|
||||
func (h *RequestHeader) SetCookie(key, value string) {
|
||||
h.parseRawHeaders()
|
||||
h.collectCookies()
|
||||
h.cookies = setArg(h.cookies, key, value, argsHasValue)
|
||||
h.cookies = setArg(h.cookies, key, value)
|
||||
}
|
||||
|
||||
// SetCookieBytesK sets 'key: value' cookies.
|
||||
|
@ -1083,8 +994,8 @@ func (h *RequestHeader) DelAllCookies() {
|
|||
// Multiple headers with the same key may be added with this function.
|
||||
// Use Set for setting a single header for the given key.
|
||||
func (h *RequestHeader) Add(key, value string) {
|
||||
k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing)
|
||||
h.h = appendArg(h.h, b2s(k), value, argsHasValue)
|
||||
k := getHeaderKeyBytes(&h.bufKV, key)
|
||||
h.h = appendArg(h.h, b2s(k), value)
|
||||
}
|
||||
|
||||
// AddBytesK adds the given 'key: value' header.
|
||||
|
@ -1115,7 +1026,7 @@ func (h *RequestHeader) AddBytesKV(key, value []byte) {
|
|||
//
|
||||
// Use Add for setting multiple header values under the same key.
|
||||
func (h *RequestHeader) Set(key, value string) {
|
||||
initHeaderKV(&h.bufKV, key, value, h.disableNormalizing)
|
||||
initHeaderKV(&h.bufKV, key, value)
|
||||
h.SetCanonical(h.bufKV.key, h.bufKV.value)
|
||||
}
|
||||
|
||||
|
@ -1131,7 +1042,7 @@ func (h *RequestHeader) SetBytesK(key []byte, value string) {
|
|||
//
|
||||
// Use AddBytesV for setting multiple header values under the same key.
|
||||
func (h *RequestHeader) SetBytesV(key string, value []byte) {
|
||||
k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing)
|
||||
k := getHeaderKeyBytes(&h.bufKV, key)
|
||||
h.SetCanonical(k, value)
|
||||
}
|
||||
|
||||
|
@ -1140,7 +1051,7 @@ func (h *RequestHeader) SetBytesV(key string, value []byte) {
|
|||
// Use AddBytesKV for setting multiple header values under the same key.
|
||||
func (h *RequestHeader) SetBytesKV(key, value []byte) {
|
||||
h.bufKV.key = append(h.bufKV.key[:0], key...)
|
||||
normalizeHeaderKey(h.bufKV.key, h.disableNormalizing)
|
||||
normalizeHeaderKey(h.bufKV.key)
|
||||
h.SetCanonical(h.bufKV.key, value)
|
||||
}
|
||||
|
||||
|
@ -1168,12 +1079,12 @@ func (h *RequestHeader) SetCanonical(key, value []byte) {
|
|||
h.SetConnectionClose()
|
||||
} else {
|
||||
h.ResetConnectionClose()
|
||||
h.h = setArgBytes(h.h, key, value, argsHasValue)
|
||||
h.h = setArgBytes(h.h, key, value)
|
||||
}
|
||||
case "Transfer-Encoding":
|
||||
// Transfer-Encoding is managed automatically.
|
||||
default:
|
||||
h.h = setArgBytes(h.h, key, value, argsHasValue)
|
||||
h.h = setArgBytes(h.h, key, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1182,7 +1093,7 @@ func (h *RequestHeader) SetCanonical(key, value []byte) {
|
|||
// Returned value is valid until the next call to ResponseHeader.
|
||||
// Do not store references to returned value. Make copies instead.
|
||||
func (h *ResponseHeader) Peek(key string) []byte {
|
||||
k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing)
|
||||
k := getHeaderKeyBytes(&h.bufKV, key)
|
||||
return h.peek(k)
|
||||
}
|
||||
|
||||
|
@ -1192,7 +1103,7 @@ func (h *ResponseHeader) Peek(key string) []byte {
|
|||
// Do not store references to returned value. Make copies instead.
|
||||
func (h *ResponseHeader) PeekBytes(key []byte) []byte {
|
||||
h.bufKV.key = append(h.bufKV.key[:0], key...)
|
||||
normalizeHeaderKey(h.bufKV.key, h.disableNormalizing)
|
||||
normalizeHeaderKey(h.bufKV.key)
|
||||
return h.peek(h.bufKV.key)
|
||||
}
|
||||
|
||||
|
@ -1201,7 +1112,7 @@ func (h *ResponseHeader) PeekBytes(key []byte) []byte {
|
|||
// Returned value is valid until the next call to RequestHeader.
|
||||
// Do not store references to returned value. Make copies instead.
|
||||
func (h *RequestHeader) Peek(key string) []byte {
|
||||
k := getHeaderKeyBytes(&h.bufKV, key, h.disableNormalizing)
|
||||
k := getHeaderKeyBytes(&h.bufKV, key)
|
||||
return h.peek(k)
|
||||
}
|
||||
|
||||
|
@ -1211,7 +1122,7 @@ func (h *RequestHeader) Peek(key string) []byte {
|
|||
// Do not store references to returned value. Make copies instead.
|
||||
func (h *RequestHeader) PeekBytes(key []byte) []byte {
|
||||
h.bufKV.key = append(h.bufKV.key[:0], key...)
|
||||
normalizeHeaderKey(h.bufKV.key, h.disableNormalizing)
|
||||
normalizeHeaderKey(h.bufKV.key)
|
||||
return h.peek(h.bufKV.key)
|
||||
}
|
||||
|
||||
|
@ -1228,8 +1139,6 @@ func (h *ResponseHeader) peek(key []byte) []byte {
|
|||
return peekArgBytes(h.h, key)
|
||||
case "Content-Length":
|
||||
return h.contentLengthBytes
|
||||
case "Set-Cookie":
|
||||
return appendResponseCookieBytes(nil, h.cookies)
|
||||
default:
|
||||
return peekArgBytes(h.h, key)
|
||||
}
|
||||
|
@ -1251,12 +1160,6 @@ func (h *RequestHeader) peek(key []byte) []byte {
|
|||
return peekArgBytes(h.h, key)
|
||||
case "Content-Length":
|
||||
return h.contentLengthBytes
|
||||
case "Cookie":
|
||||
if h.cookiesCollected {
|
||||
return appendRequestCookieBytes(nil, h.cookies)
|
||||
} else {
|
||||
return peekArgBytes(h.h, key)
|
||||
}
|
||||
default:
|
||||
return peekArgBytes(h.h, key)
|
||||
}
|
||||
|
@ -1299,7 +1202,7 @@ func (h *ResponseHeader) Read(r *bufio.Reader) error {
|
|||
return nil
|
||||
}
|
||||
if err != errNeedMore {
|
||||
h.resetSkipNormalize()
|
||||
h.Reset()
|
||||
return err
|
||||
}
|
||||
n = r.Buffered() + 1
|
||||
|
@ -1307,7 +1210,7 @@ func (h *ResponseHeader) Read(r *bufio.Reader) error {
|
|||
}
|
||||
|
||||
func (h *ResponseHeader) tryRead(r *bufio.Reader, n int) error {
|
||||
h.resetSkipNormalize()
|
||||
h.Reset()
|
||||
b, err := r.Peek(n)
|
||||
if len(b) == 0 {
|
||||
// treat all errors on the first byte read as EOF
|
||||
|
@ -1370,7 +1273,7 @@ func (h *RequestHeader) Read(r *bufio.Reader) error {
|
|||
return nil
|
||||
}
|
||||
if err != errNeedMore {
|
||||
h.resetSkipNormalize()
|
||||
h.Reset()
|
||||
return err
|
||||
}
|
||||
n = r.Buffered() + 1
|
||||
|
@ -1378,15 +1281,12 @@ func (h *RequestHeader) Read(r *bufio.Reader) error {
|
|||
}
|
||||
|
||||
func (h *RequestHeader) tryRead(r *bufio.Reader, n int) error {
|
||||
h.resetSkipNormalize()
|
||||
h.Reset()
|
||||
b, err := r.Peek(n)
|
||||
if len(b) == 0 {
|
||||
if err == io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
panic("bufio.Reader.Peek() returned nil, nil")
|
||||
// treat all errors on the first byte read as EOF
|
||||
if n == 1 || err == io.EOF {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
// This is for go 1.6 bug. See https://github.com/golang/go/issues/14121 .
|
||||
|
@ -1396,11 +1296,6 @@ func (h *RequestHeader) tryRead(r *bufio.Reader, n int) error {
|
|||
}
|
||||
}
|
||||
|
||||
if n == 1 {
|
||||
// We didn't read a single byte.
|
||||
return errNothingRead
|
||||
}
|
||||
|
||||
return fmt.Errorf("error when reading request headers: %s", err)
|
||||
}
|
||||
b = mustPeekBuffered(r)
|
||||
|
@ -1490,9 +1385,10 @@ func (h *ResponseHeader) AppendBytes(dst []byte) []byte {
|
|||
dst = append(dst, statusLine(statusCode)...)
|
||||
|
||||
server := h.Server()
|
||||
if len(server) != 0 {
|
||||
dst = appendHeaderLine(dst, strServer, server)
|
||||
if len(server) == 0 {
|
||||
server = defaultServerName
|
||||
}
|
||||
dst = appendHeaderLine(dst, strServer, server)
|
||||
dst = appendHeaderLine(dst, strDate, serverDate.Load().([]byte))
|
||||
|
||||
// Append Content-Type only for non-zero responses
|
||||
|
@ -1550,20 +1446,6 @@ func (h *RequestHeader) Header() []byte {
|
|||
return h.bufKV.value
|
||||
}
|
||||
|
||||
// RawHeaders returns raw header key/value bytes.
|
||||
//
|
||||
// Depending on server configuration, header keys may be normalized to
|
||||
// capital-case in place.
|
||||
//
|
||||
// This copy is set aside during parsing, so empty slice is returned for all
|
||||
// cases where parsing did not happen. Similarly, request line is not stored
|
||||
// during parsing and can not be returned.
|
||||
//
|
||||
// The slice is not safe to use after the handler returns.
|
||||
func (h *RequestHeader) RawHeaders() []byte {
|
||||
return h.rawHeadersCopy
|
||||
}
|
||||
|
||||
// String returns request header representation.
|
||||
func (h *RequestHeader) String() string {
|
||||
return string(h.Header())
|
||||
|
@ -1585,9 +1467,10 @@ func (h *RequestHeader) AppendBytes(dst []byte) []byte {
|
|||
}
|
||||
|
||||
userAgent := h.UserAgent()
|
||||
if len(userAgent) > 0 {
|
||||
dst = appendHeaderLine(dst, strUserAgent, userAgent)
|
||||
if len(userAgent) == 0 {
|
||||
userAgent = defaultUserAgent
|
||||
}
|
||||
dst = appendHeaderLine(dst, strUserAgent, userAgent)
|
||||
|
||||
host := h.Host()
|
||||
if len(host) > 0 {
|
||||
|
@ -1595,7 +1478,7 @@ func (h *RequestHeader) AppendBytes(dst []byte) []byte {
|
|||
}
|
||||
|
||||
contentType := h.ContentType()
|
||||
if !h.ignoreBody() {
|
||||
if !h.noBody() {
|
||||
if len(contentType) == 0 {
|
||||
contentType = strPostArgsContentType
|
||||
}
|
||||
|
@ -1649,7 +1532,7 @@ func (h *ResponseHeader) parse(buf []byte) (int, error) {
|
|||
return m + n, nil
|
||||
}
|
||||
|
||||
func (h *RequestHeader) ignoreBody() bool {
|
||||
func (h *RequestHeader) noBody() bool {
|
||||
return h.IsGet() || h.IsHead()
|
||||
}
|
||||
|
||||
|
@ -1660,20 +1543,18 @@ func (h *RequestHeader) parse(buf []byte) (int, error) {
|
|||
}
|
||||
|
||||
var n int
|
||||
var rawHeaders []byte
|
||||
rawHeaders, n, err = readRawHeaders(h.rawHeaders[:0], buf[m:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
h.rawHeadersCopy = append(h.rawHeadersCopy[:0], rawHeaders...)
|
||||
if !h.ignoreBody() || h.noHTTP11 {
|
||||
if !h.noBody() || h.noHTTP11 {
|
||||
n, err = h.parseHeaders(buf[m:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
h.rawHeaders = append(h.rawHeaders[:0], buf[m:m+n]...)
|
||||
h.rawHeadersParsed = true
|
||||
} else {
|
||||
var rawHeaders []byte
|
||||
rawHeaders, n, err = readRawHeaders(h.rawHeaders[:0], buf[m:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
h.rawHeaders = rawHeaders
|
||||
}
|
||||
return m + n, nil
|
||||
|
@ -1807,57 +1688,40 @@ func (h *ResponseHeader) parseHeaders(buf []byte) (int, error) {
|
|||
|
||||
var s headerScanner
|
||||
s.b = buf
|
||||
s.disableNormalizing = h.disableNormalizing
|
||||
var err error
|
||||
var kv *argsKV
|
||||
for s.next() {
|
||||
if len(s.key) > 0 {
|
||||
switch s.key[0] | 0x20 {
|
||||
case 'c':
|
||||
if caseInsensitiveCompare(s.key, strContentType) {
|
||||
h.contentType = append(h.contentType[:0], s.value...)
|
||||
continue
|
||||
}
|
||||
if caseInsensitiveCompare(s.key, strContentLength) {
|
||||
if h.contentLength != -1 {
|
||||
if h.contentLength, err = parseContentLength(s.value); err != nil {
|
||||
h.contentLength = -2
|
||||
} else {
|
||||
h.contentLengthBytes = append(h.contentLengthBytes[:0], s.value...)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if caseInsensitiveCompare(s.key, strConnection) {
|
||||
if bytes.Equal(s.value, strClose) {
|
||||
h.connectionClose = true
|
||||
} else {
|
||||
h.connectionClose = false
|
||||
h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue)
|
||||
}
|
||||
continue
|
||||
}
|
||||
case 's':
|
||||
if caseInsensitiveCompare(s.key, strServer) {
|
||||
h.server = append(h.server[:0], s.value...)
|
||||
continue
|
||||
}
|
||||
if caseInsensitiveCompare(s.key, strSetCookie) {
|
||||
h.cookies, kv = allocArg(h.cookies)
|
||||
kv.key = getCookieKey(kv.key, s.value)
|
||||
kv.value = append(kv.value[:0], s.value...)
|
||||
continue
|
||||
}
|
||||
case 't':
|
||||
if caseInsensitiveCompare(s.key, strTransferEncoding) {
|
||||
if !bytes.Equal(s.value, strIdentity) {
|
||||
h.contentLength = -1
|
||||
h.h = setArgBytes(h.h, strTransferEncoding, strChunked, argsHasValue)
|
||||
}
|
||||
continue
|
||||
switch string(s.key) {
|
||||
case "Content-Type":
|
||||
h.contentType = append(h.contentType[:0], s.value...)
|
||||
case "Server":
|
||||
h.server = append(h.server[:0], s.value...)
|
||||
case "Content-Length":
|
||||
if h.contentLength != -1 {
|
||||
if h.contentLength, err = parseContentLength(s.value); err != nil {
|
||||
h.contentLength = -2
|
||||
} else {
|
||||
h.contentLengthBytes = append(h.contentLengthBytes[:0], s.value...)
|
||||
}
|
||||
}
|
||||
h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue)
|
||||
case "Transfer-Encoding":
|
||||
if !bytes.Equal(s.value, strIdentity) {
|
||||
h.contentLength = -1
|
||||
h.h = setArgBytes(h.h, strTransferEncoding, strChunked)
|
||||
}
|
||||
case "Set-Cookie":
|
||||
h.cookies, kv = allocArg(h.cookies)
|
||||
kv.key = getCookieKey(kv.key, s.value)
|
||||
kv.value = append(kv.value[:0], s.value...)
|
||||
case "Connection":
|
||||
if bytes.Equal(s.value, strClose) {
|
||||
h.connectionClose = true
|
||||
} else {
|
||||
h.connectionClose = false
|
||||
h.h = appendArgBytes(h.h, s.key, s.value)
|
||||
}
|
||||
default:
|
||||
h.h = appendArgBytes(h.h, s.key, s.value)
|
||||
}
|
||||
}
|
||||
if s.err != nil {
|
||||
|
@ -1869,13 +1733,13 @@ func (h *ResponseHeader) parseHeaders(buf []byte) (int, error) {
|
|||
h.contentLengthBytes = h.contentLengthBytes[:0]
|
||||
}
|
||||
if h.contentLength == -2 && !h.ConnectionUpgrade() && !h.mustSkipContentLength() {
|
||||
h.h = setArgBytes(h.h, strTransferEncoding, strIdentity, argsHasValue)
|
||||
h.h = setArgBytes(h.h, strTransferEncoding, strIdentity)
|
||||
h.connectionClose = true
|
||||
}
|
||||
if h.noHTTP11 && !h.connectionClose {
|
||||
// close connection for non-http/1.1 response unless 'Connection: keep-alive' is set.
|
||||
v := peekArgBytes(h.h, strConnection)
|
||||
h.connectionClose = !hasHeaderValue(v, strKeepAlive)
|
||||
h.connectionClose = !hasHeaderValue(v, strKeepAlive) && !hasHeaderValue(v, strKeepAliveCamelCase)
|
||||
}
|
||||
|
||||
return len(buf) - len(s.b), nil
|
||||
|
@ -1886,56 +1750,38 @@ func (h *RequestHeader) parseHeaders(buf []byte) (int, error) {
|
|||
|
||||
var s headerScanner
|
||||
s.b = buf
|
||||
s.disableNormalizing = h.disableNormalizing
|
||||
var err error
|
||||
for s.next() {
|
||||
if len(s.key) > 0 {
|
||||
switch s.key[0] | 0x20 {
|
||||
case 'h':
|
||||
if caseInsensitiveCompare(s.key, strHost) {
|
||||
h.host = append(h.host[:0], s.value...)
|
||||
continue
|
||||
}
|
||||
case 'u':
|
||||
if caseInsensitiveCompare(s.key, strUserAgent) {
|
||||
h.userAgent = append(h.userAgent[:0], s.value...)
|
||||
continue
|
||||
}
|
||||
case 'c':
|
||||
if caseInsensitiveCompare(s.key, strContentType) {
|
||||
h.contentType = append(h.contentType[:0], s.value...)
|
||||
continue
|
||||
}
|
||||
if caseInsensitiveCompare(s.key, strContentLength) {
|
||||
if h.contentLength != -1 {
|
||||
if h.contentLength, err = parseContentLength(s.value); err != nil {
|
||||
h.contentLength = -2
|
||||
} else {
|
||||
h.contentLengthBytes = append(h.contentLengthBytes[:0], s.value...)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if caseInsensitiveCompare(s.key, strConnection) {
|
||||
if bytes.Equal(s.value, strClose) {
|
||||
h.connectionClose = true
|
||||
} else {
|
||||
h.connectionClose = false
|
||||
h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue)
|
||||
}
|
||||
continue
|
||||
}
|
||||
case 't':
|
||||
if caseInsensitiveCompare(s.key, strTransferEncoding) {
|
||||
if !bytes.Equal(s.value, strIdentity) {
|
||||
h.contentLength = -1
|
||||
h.h = setArgBytes(h.h, strTransferEncoding, strChunked, argsHasValue)
|
||||
}
|
||||
continue
|
||||
switch string(s.key) {
|
||||
case "Host":
|
||||
h.host = append(h.host[:0], s.value...)
|
||||
case "User-Agent":
|
||||
h.userAgent = append(h.userAgent[:0], s.value...)
|
||||
case "Content-Type":
|
||||
h.contentType = append(h.contentType[:0], s.value...)
|
||||
case "Content-Length":
|
||||
if h.contentLength != -1 {
|
||||
if h.contentLength, err = parseContentLength(s.value); err != nil {
|
||||
h.contentLength = -2
|
||||
} else {
|
||||
h.contentLengthBytes = append(h.contentLengthBytes[:0], s.value...)
|
||||
}
|
||||
}
|
||||
case "Transfer-Encoding":
|
||||
if !bytes.Equal(s.value, strIdentity) {
|
||||
h.contentLength = -1
|
||||
h.h = setArgBytes(h.h, strTransferEncoding, strChunked)
|
||||
}
|
||||
case "Connection":
|
||||
if bytes.Equal(s.value, strClose) {
|
||||
h.connectionClose = true
|
||||
} else {
|
||||
h.connectionClose = false
|
||||
h.h = appendArgBytes(h.h, s.key, s.value)
|
||||
}
|
||||
default:
|
||||
h.h = appendArgBytes(h.h, s.key, s.value)
|
||||
}
|
||||
h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue)
|
||||
}
|
||||
if s.err != nil {
|
||||
h.connectionClose = true
|
||||
|
@ -1945,12 +1791,17 @@ func (h *RequestHeader) parseHeaders(buf []byte) (int, error) {
|
|||
if h.contentLength < 0 {
|
||||
h.contentLengthBytes = h.contentLengthBytes[:0]
|
||||
}
|
||||
if h.noBody() {
|
||||
h.contentLength = 0
|
||||
h.contentLengthBytes = h.contentLengthBytes[:0]
|
||||
}
|
||||
if h.noHTTP11 && !h.connectionClose {
|
||||
// close connection for non-http/1.1 request unless 'Connection: keep-alive' is set.
|
||||
v := peekArgBytes(h.h, strConnection)
|
||||
h.connectionClose = !hasHeaderValue(v, strKeepAlive)
|
||||
h.connectionClose = !hasHeaderValue(v, strKeepAlive) && !hasHeaderValue(v, strKeepAliveCamelCase)
|
||||
}
|
||||
return s.hLen, nil
|
||||
|
||||
return len(buf) - len(s.b), nil
|
||||
}
|
||||
|
||||
func (h *RequestHeader) parseRawHeaders() {
|
||||
|
@ -2000,23 +1851,16 @@ type headerScanner struct {
|
|||
key []byte
|
||||
value []byte
|
||||
err error
|
||||
|
||||
// hLen stores header subslice len
|
||||
hLen int
|
||||
|
||||
disableNormalizing bool
|
||||
}
|
||||
|
||||
func (s *headerScanner) next() bool {
|
||||
bLen := len(s.b)
|
||||
if bLen >= 2 && s.b[0] == '\r' && s.b[1] == '\n' {
|
||||
s.b = s.b[2:]
|
||||
s.hLen += 2
|
||||
return false
|
||||
}
|
||||
if bLen >= 1 && s.b[0] == '\n' {
|
||||
s.b = s.b[1:]
|
||||
s.hLen++
|
||||
return false
|
||||
}
|
||||
n := bytes.IndexByte(s.b, ':')
|
||||
|
@ -2025,12 +1869,11 @@ func (s *headerScanner) next() bool {
|
|||
return false
|
||||
}
|
||||
s.key = s.b[:n]
|
||||
normalizeHeaderKey(s.key, s.disableNormalizing)
|
||||
normalizeHeaderKey(s.key)
|
||||
n++
|
||||
for len(s.b) > n && s.b[n] == ' ' {
|
||||
n++
|
||||
}
|
||||
s.hLen += n
|
||||
s.b = s.b[n:]
|
||||
n = bytes.IndexByte(s.b, '\n')
|
||||
if n < 0 {
|
||||
|
@ -2038,7 +1881,6 @@ func (s *headerScanner) next() bool {
|
|||
return false
|
||||
}
|
||||
s.value = s.b[:n]
|
||||
s.hLen += n + 1
|
||||
s.b = s.b[n+1:]
|
||||
|
||||
if n > 0 && s.value[n-1] == '\r' {
|
||||
|
@ -2086,7 +1928,7 @@ func hasHeaderValue(s, value []byte) bool {
|
|||
var vs headerValueScanner
|
||||
vs.b = s
|
||||
for vs.next() {
|
||||
if caseInsensitiveCompare(vs.value, value) {
|
||||
if bytes.Equal(vs.value, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -2105,22 +1947,18 @@ func nextLine(b []byte) ([]byte, []byte, error) {
|
|||
return b[:n], b[nNext+1:], nil
|
||||
}
|
||||
|
||||
func initHeaderKV(kv *argsKV, key, value string, disableNormalizing bool) {
|
||||
kv.key = getHeaderKeyBytes(kv, key, disableNormalizing)
|
||||
func initHeaderKV(kv *argsKV, key, value string) {
|
||||
kv.key = getHeaderKeyBytes(kv, key)
|
||||
kv.value = append(kv.value[:0], value...)
|
||||
}
|
||||
|
||||
func getHeaderKeyBytes(kv *argsKV, key string, disableNormalizing bool) []byte {
|
||||
func getHeaderKeyBytes(kv *argsKV, key string) []byte {
|
||||
kv.key = append(kv.key[:0], key...)
|
||||
normalizeHeaderKey(kv.key, disableNormalizing)
|
||||
normalizeHeaderKey(kv.key)
|
||||
return kv.key
|
||||
}
|
||||
|
||||
func normalizeHeaderKey(b []byte, disableNormalizing bool) {
|
||||
if disableNormalizing {
|
||||
return
|
||||
}
|
||||
|
||||
func normalizeHeaderKey(b []byte) {
|
||||
n := len(b)
|
||||
if n == 0 {
|
||||
return
|
||||
|
@ -2152,7 +1990,7 @@ func normalizeHeaderKey(b []byte, disableNormalizing bool) {
|
|||
// * foo-bar-baz -> Foo-Bar-Baz
|
||||
func AppendNormalizedHeaderKey(dst []byte, key string) []byte {
|
||||
dst = append(dst, key...)
|
||||
normalizeHeaderKey(dst[len(dst)-len(key):], false)
|
||||
normalizeHeaderKey(dst[len(dst)-len(key):])
|
||||
return dst
|
||||
}
|
||||
|
||||
|
@ -2173,7 +2011,6 @@ func AppendNormalizedHeaderKeyBytes(dst, key []byte) []byte {
|
|||
var (
|
||||
errNeedMore = errors.New("need more data: cannot find trailing lf")
|
||||
errSmallBuffer = errors.New("small read buffer. Increase ReadBufferSize")
|
||||
errNothingRead = errors.New("read timeout with nothing read")
|
||||
)
|
||||
|
||||
// ErrSmallBuffer is returned when the provided buffer size is too small
|
|
@ -7,7 +7,6 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
|
@ -45,9 +44,6 @@ type Request struct {
|
|||
keepBodyBuffer bool
|
||||
|
||||
isTLS bool
|
||||
|
||||
// To detect scheme changes in redirects
|
||||
schemaUpdate bool
|
||||
}
|
||||
|
||||
// Response represents HTTP response.
|
||||
|
@ -76,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.
|
||||
|
@ -287,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.
|
||||
|
@ -372,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
|
||||
|
@ -399,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
|
||||
|
@ -650,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) {
|
||||
|
@ -739,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
|
||||
}
|
||||
|
@ -750,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")
|
||||
}
|
||||
|
@ -772,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)
|
||||
}
|
||||
|
@ -845,8 +817,6 @@ func (resp *Response) Reset() {
|
|||
resp.Header.Reset()
|
||||
resp.resetSkipHeader()
|
||||
resp.SkipBody = false
|
||||
resp.raddr = nil
|
||||
resp.laddr = nil
|
||||
}
|
||||
|
||||
func (resp *Response) resetSkipHeader() {
|
||||
|
@ -874,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.
|
||||
//
|
||||
|
@ -910,7 +878,11 @@ func (req *Request) readLimitBody(r *bufio.Reader, maxBodySize int, getOnly bool
|
|||
return err
|
||||
}
|
||||
if getOnly && !req.Header.IsGet() {
|
||||
return ErrGetOnly
|
||||
return errGetOnly
|
||||
}
|
||||
|
||||
if req.Header.noBody() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if req.MayContinue() {
|
||||
|
@ -946,7 +918,7 @@ func (req *Request) MayContinue() bool {
|
|||
// then ErrBodyTooLarge is returned.
|
||||
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
|
||||
|
@ -1016,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))
|
||||
|
@ -1136,11 +1109,8 @@ func (req *Request) Write(w *bufio.Writer) error {
|
|||
req.Header.SetMultipartFormBoundary(req.multipartFormBoundary)
|
||||
}
|
||||
|
||||
hasBody := !req.Header.ignoreBody()
|
||||
hasBody := !req.Header.noBody()
|
||||
if hasBody {
|
||||
if len(body) == 0 {
|
||||
body = req.postArgs.QueryString()
|
||||
}
|
||||
req.Header.SetContentLength(len(body))
|
||||
}
|
||||
if err = req.Header.Write(w); err != nil {
|
||||
|
@ -1488,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()
|
||||
|
@ -1497,7 +1467,7 @@ func getHTTPString(hw httpWriter) string {
|
|||
return err.Error()
|
||||
}
|
||||
s := string(w.B)
|
||||
bytebufferpool.Put(w)
|
||||
ReleaseByteBuffer(w)
|
||||
return s
|
||||
}
|
||||
|
||||
|
@ -1680,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")
|
||||
|
@ -1704,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 {
|
||||
|
@ -1720,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
|
||||
}
|
|
@ -59,7 +59,7 @@ type LBClient struct {
|
|||
// 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
|
|
@ -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{}
|
||||
|
||||
func (*noCopy) Lock() {}
|
||||
func (*noCopy) Unlock() {}
|
||||
func (*noCopy) Lock() {}
|
File diff suppressed because it is too large
Load diff
|
@ -3,9 +3,8 @@ package stackless
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/valyala/bytebufferpool"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Writer is an interface stackless writer must conform to.
|
|
@ -5,7 +5,7 @@ import (
|
|||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/valyala/fasthttp/fasthttputil"
|
||||
"github.com/VictoriaMetrics/fasthttp/fasthttputil"
|
||||
)
|
||||
|
||||
// StreamWriter must write data to w.
|
|
@ -22,15 +22,11 @@ var (
|
|||
|
||||
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")
|
||||
strConnect = []byte("CONNECT")
|
||||
strOptions = []byte("OPTIONS")
|
||||
strTrace = []byte("TRACE")
|
||||
strPatch = []byte("PATCH")
|
||||
strGet = []byte("GET")
|
||||
strHead = []byte("HEAD")
|
||||
strPost = []byte("POST")
|
||||
strPut = []byte("PUT")
|
||||
strDelete = []byte("DELETE")
|
||||
|
||||
strExpect = []byte("Expect")
|
||||
strConnection = []byte("Connection")
|
||||
|
@ -53,20 +49,17 @@ var (
|
|||
strRange = []byte("Range")
|
||||
strContentRange = []byte("Content-Range")
|
||||
|
||||
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")
|
||||
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")
|
|
@ -33,7 +33,7 @@ import (
|
|||
// * foo.bar:80
|
||||
// * aaa.com:8080
|
||||
func Dial(addr string) (net.Conn, error) {
|
||||
return defaultDialer.Dial(addr)
|
||||
return getDialer(DefaultDialTimeout, false)(addr)
|
||||
}
|
||||
|
||||
// DialTimeout dials the given TCP addr using tcp4 using the given timeout.
|
||||
|
@ -58,7 +58,7 @@ func Dial(addr string) (net.Conn, error) {
|
|||
// * foo.bar:80
|
||||
// * aaa.com:8080
|
||||
func DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
|
||||
return defaultDialer.DialTimeout(addr, timeout)
|
||||
return getDialer(timeout, false)(addr)
|
||||
}
|
||||
|
||||
// DialDualStack dials the given TCP addr using both tcp4 and tcp6.
|
||||
|
@ -86,7 +86,7 @@ func DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
|
|||
// * foo.bar:80
|
||||
// * aaa.com:8080
|
||||
func DialDualStack(addr string) (net.Conn, error) {
|
||||
return defaultDialer.DialDualStack(addr)
|
||||
return getDialer(DefaultDialTimeout, true)(addr)
|
||||
}
|
||||
|
||||
// DialDualStackTimeout dials the given TCP addr using both tcp4 and tcp6
|
||||
|
@ -112,22 +112,45 @@ func DialDualStack(addr string) (net.Conn, error) {
|
|||
// * foo.bar:80
|
||||
// * aaa.com:8080
|
||||
func DialDualStackTimeout(addr string, timeout time.Duration) (net.Conn, error) {
|
||||
return defaultDialer.DialDualStackTimeout(addr, timeout)
|
||||
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 (
|
||||
defaultDialer = &TCPDialer{Concurrency: 1000}
|
||||
dialerStd = &tcpDialer{}
|
||||
dialerDualStack = &tcpDialer{DualStack: true}
|
||||
|
||||
dialMap = make(map[int]DialFunc)
|
||||
dialDualStackMap = make(map[int]DialFunc)
|
||||
dialMapLock sync.Mutex
|
||||
)
|
||||
|
||||
// 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
|
||||
type tcpDialer struct {
|
||||
DualStack bool
|
||||
|
||||
tcpAddrsLock sync.Mutex
|
||||
tcpAddrsMap map[string]*tcpAddrEntry
|
||||
|
@ -137,145 +160,41 @@ type TCPDialer 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)
|
||||
}
|
||||
const maxDialConcurrency = 1000
|
||||
|
||||
// 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) {
|
||||
func (d *tcpDialer) NewDial(timeout time.Duration) DialFunc {
|
||||
d.once.Do(func() {
|
||||
if d.Concurrency > 0 {
|
||||
d.concurrencyCh = make(chan struct{}, d.Concurrency)
|
||||
}
|
||||
d.concurrencyCh = make(chan struct{}, maxDialConcurrency)
|
||||
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 = tryDial(network, &addrs[idx%n], deadline, d.concurrencyCh)
|
||||
if err == nil {
|
||||
return conn, nil
|
||||
}
|
||||
if err == ErrDialTimeout {
|
||||
return func(addr string) (net.Conn, error) {
|
||||
addrs, idx, err := d.getTCPAddrs(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
idx++
|
||||
n--
|
||||
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
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func tryDial(network string, addr *net.TCPAddr, deadline time.Time, concurrencyCh chan struct{}) (net.Conn, error) {
|
||||
|
@ -284,22 +203,26 @@ func tryDial(network string, addr *net.TCPAddr, deadline time.Time, concurrencyC
|
|||
return nil, ErrDialTimeout
|
||||
}
|
||||
|
||||
if concurrencyCh != nil {
|
||||
select {
|
||||
case concurrencyCh <- struct{}{}:
|
||||
default:
|
||||
tc := acquireTimer(timeout)
|
||||
isTimeout := false
|
||||
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
|
||||
}
|
||||
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()
|
||||
|
@ -311,9 +234,7 @@ func tryDial(network string, addr *net.TCPAddr, deadline time.Time, concurrencyC
|
|||
var dr dialResult
|
||||
dr.conn, dr.err = net.DialTCP(network, nil, addr)
|
||||
ch <- dr
|
||||
if concurrencyCh != nil {
|
||||
<-concurrencyCh
|
||||
}
|
||||
<-concurrencyCh
|
||||
}()
|
||||
|
||||
var (
|
||||
|
@ -321,7 +242,7 @@ func tryDial(network string, addr *net.TCPAddr, deadline time.Time, concurrencyC
|
|||
err error
|
||||
)
|
||||
|
||||
tc := AcquireTimer(timeout)
|
||||
tc := acquireTimer(timeout)
|
||||
select {
|
||||
case dr := <-ch:
|
||||
conn = dr.conn
|
||||
|
@ -330,7 +251,7 @@ func tryDial(network string, addr *net.TCPAddr, deadline time.Time, concurrencyC
|
|||
case <-tc.C:
|
||||
err = ErrDialTimeout
|
||||
}
|
||||
ReleaseTimer(tc)
|
||||
releaseTimer(tc)
|
||||
|
||||
return conn, err
|
||||
}
|
||||
|
@ -361,7 +282,7 @@ type tcpAddrEntry struct {
|
|||
// by Dial* functions.
|
||||
const DefaultDNSCacheDuration = time.Minute
|
||||
|
||||
func (d *TCPDialer) tcpAddrsClean() {
|
||||
func (d *tcpDialer) tcpAddrsClean() {
|
||||
expireDuration := 2 * DefaultDNSCacheDuration
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
|
@ -377,7 +298,7 @@ func (d *TCPDialer) tcpAddrsClean() {
|
|||
}
|
||||
}
|
||||
|
||||
func (d *TCPDialer) getTCPAddrs(addr string, dualStack bool) ([]net.TCPAddr, uint32, error) {
|
||||
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 {
|
||||
|
@ -387,7 +308,7 @@ func (d *TCPDialer) getTCPAddrs(addr string, dualStack bool) ([]net.TCPAddr, uin
|
|||
d.tcpAddrsLock.Unlock()
|
||||
|
||||
if e == nil {
|
||||
addrs, err := resolveTCPAddrs(addr, dualStack)
|
||||
addrs, err := resolveTCPAddrs(addr, d.DualStack)
|
||||
if err != nil {
|
||||
d.tcpAddrsLock.Lock()
|
||||
e = d.tcpAddrsMap[addr]
|
|
@ -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)
|
||||
}
|
|
@ -180,7 +180,7 @@ func (u *URI) Reset() {
|
|||
u.parsedQueryArgs = 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().
|
|
@ -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 {
|
||||
|
@ -189,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()
|
||||
|
@ -213,16 +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, "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 {
|
||||
if err != errHijacked {
|
||||
c.Close()
|
||||
wp.connState(c, StateClosed)
|
||||
}
|
||||
c = nil
|
||||
|
36
vendor/github.com/valyala/fasthttp/.travis.yml
generated
vendored
36
vendor/github.com/valyala/fasthttp/.travis.yml
generated
vendored
|
@ -1,36 +0,0 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- tip
|
||||
- 1.11.x
|
||||
- 1.10.x
|
||||
- 1.9.x
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- tip
|
||||
fast_finish: true
|
||||
|
||||
before_install:
|
||||
- go get -t -v ./...
|
||||
# - go get -v golang.org/x/tools/cmd/goimports
|
||||
|
||||
script:
|
||||
# TODO(@kirilldanshin)
|
||||
# - test -z "$(goimports -d $(find . -type f -name '*.go' -not -path "./vendor/*"))"
|
||||
# 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 ./...
|
585
vendor/github.com/valyala/fasthttp/README.md
generated
vendored
585
vendor/github.com/valyala/fasthttp/README.md
generated
vendored
|
@ -1,585 +0,0 @@
|
|||
[![Build Status](https://travis-ci.org/valyala/fasthttp.svg)](https://travis-ci.org/valyala/fasthttp)
|
||||
[![GoDoc](https://godoc.org/github.com/valyala/fasthttp?status.svg)](http://godoc.org/github.com/valyala/fasthttp)
|
||||
[![Go Report](https://goreportcard.com/badge/github.com/valyala/fasthttp)](https://goreportcard.com/report/github.com/valyala/fasthttp)
|
||||
|
||||
# fasthttp
|
||||
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 12 results](https://www.techempower.com/benchmarks/#section=data-r12&hw=peak&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)
|
||||
|
||||
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.6 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.
|
||||
* [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) - Micro-framework to make simple the use of routing and middlewares.
|
||||
* [kratgo](https://github.com/savsgio/kratgo) - Simple, lightweight and ultra-fast HTTP Cache to speed up your websites.
|
||||
|
||||
|
||||
# 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)
|
||||
|
||||
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).
|
13
vendor/github.com/valyala/fasthttp/coarseTime.go
generated
vendored
13
vendor/github.com/valyala/fasthttp/coarseTime.go
generated
vendored
|
@ -1,13 +0,0 @@
|
|||
package fasthttp
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// CoarseTimeNow returns the current time truncated to the nearest second.
|
||||
//
|
||||
// Deprecated: This is slower than calling time.Now() directly.
|
||||
// This is now time.Now().Truncate(time.Second) shortcut.
|
||||
func CoarseTimeNow() time.Time {
|
||||
return time.Now().Truncate(time.Second)
|
||||
}
|
9
vendor/github.com/valyala/fasthttp/go.mod
generated
vendored
9
vendor/github.com/valyala/fasthttp/go.mod
generated
vendored
|
@ -1,9 +0,0 @@
|
|||
module github.com/valyala/fasthttp
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.4.0
|
||||
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a
|
||||
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3
|
||||
)
|
10
vendor/github.com/valyala/fasthttp/go.sum
generated
vendored
10
vendor/github.com/valyala/fasthttp/go.sum
generated
vendored
|
@ -1,10 +0,0 @@
|
|||
github.com/klauspost/compress v1.4.0 h1:8nsMz3tWa9SWWPL60G1V6CUsf4lLjWLTNEtibhe8gh8=
|
||||
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e h1:+lIPJOWl+jSiJOc70QXJ07+2eg2Jy2EC7Mi11BWujeM=
|
||||
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
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/net v0.0.0-20180911220305-26e67e76b6c3 h1:czFLhve3vsQetD6JOJ8NZZvGQIXlnN3/yXxbT6/awxI=
|
||||
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
8
vendor/modules.txt
vendored
8
vendor/modules.txt
vendored
|
@ -12,6 +12,10 @@ cloud.google.com/go/storage
|
|||
github.com/BurntSushi/toml
|
||||
# github.com/VictoriaMetrics/fastcache v1.5.7
|
||||
github.com/VictoriaMetrics/fastcache
|
||||
# github.com/VictoriaMetrics/fasthttp v1.0.1
|
||||
github.com/VictoriaMetrics/fasthttp
|
||||
github.com/VictoriaMetrics/fasthttp/fasthttputil
|
||||
github.com/VictoriaMetrics/fasthttp/stackless
|
||||
# github.com/VictoriaMetrics/metrics v1.11.2
|
||||
github.com/VictoriaMetrics/metrics
|
||||
# github.com/VictoriaMetrics/metricsql v0.1.0
|
||||
|
@ -96,10 +100,6 @@ github.com/klauspost/compress/zstd
|
|||
github.com/klauspost/compress/zstd/internal/xxhash
|
||||
# github.com/valyala/bytebufferpool v1.0.0
|
||||
github.com/valyala/bytebufferpool
|
||||
# github.com/valyala/fasthttp v1.2.0
|
||||
github.com/valyala/fasthttp
|
||||
github.com/valyala/fasthttp/fasthttputil
|
||||
github.com/valyala/fasthttp/stackless
|
||||
# github.com/valyala/fastjson v1.5.1
|
||||
github.com/valyala/fastjson
|
||||
github.com/valyala/fastjson/fastfloat
|
||||
|
|
Loading…
Reference in a new issue