vendor: make vendor-update

This commit is contained in:
Aliaksandr Valialkin 2020-04-17 13:24:08 +03:00
parent 79fb595732
commit 43bbffebb3
20 changed files with 988 additions and 238 deletions

8
go.mod
View file

@ -5,21 +5,21 @@ require (
cloud.google.com/go/storage v1.6.0
github.com/VictoriaMetrics/fastcache v1.5.7
github.com/VictoriaMetrics/metrics v1.11.2
github.com/aws/aws-sdk-go v1.30.7
github.com/aws/aws-sdk-go v1.30.8
github.com/cespare/xxhash/v2 v2.1.1
github.com/golang/protobuf v1.4.0 // indirect
github.com/golang/snappy v0.0.1
github.com/klauspost/compress v1.10.4
github.com/valyala/fasthttp v1.9.0
github.com/valyala/fasthttp v1.10.0
github.com/valyala/fastjson v1.5.0
github.com/valyala/fastrand v1.0.0
github.com/valyala/gozstd v1.6.4
github.com/valyala/histogram v1.0.1
github.com/valyala/quicktemplate v1.4.1
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4
golang.org/x/tools v0.0.0-20200415034506-5d8e1897c761 // indirect
golang.org/x/tools v0.0.0-20200416214402-fc959738d646 // indirect
google.golang.org/api v0.21.0
google.golang.org/genproto v0.0.0-20200413115906-b5235f65be36 // indirect
google.golang.org/genproto v0.0.0-20200416231807-8751e049a2a0 // indirect
google.golang.org/grpc v1.28.1 // indirect
gopkg.in/yaml.v2 v2.2.8
)

16
go.sum
View file

@ -40,8 +40,8 @@ github.com/VictoriaMetrics/metrics v1.11.2 h1:t/ceLP6SvagUqypCKU7cI7+tQn54+TIV/t
github.com/VictoriaMetrics/metrics v1.11.2/go.mod h1:LU2j9qq7xqZYXz8tF3/RQnB2z2MbZms5TDiIg9/NHiQ=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/aws/aws-sdk-go v1.30.7 h1:IaXfqtioP6p9SFAnNfsqdNczbR5UNbYqvcZUSsCAdTY=
github.com/aws/aws-sdk-go v1.30.7/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.30.8 h1:4BHbh8K3qKmcnAgToZ2LShldRF9inoqIBccpCLNCy3I=
github.com/aws/aws-sdk-go v1.30.8/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@ -144,8 +144,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
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/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
github.com/valyala/fasthttp v1.9.0 h1:hNpmUdy/+ZXYpGy0OBfm7K0UQTzb73W0T0U4iJIVrMw=
github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
github.com/valyala/fasthttp v1.10.0 h1:OcUaVkFSir/TK5oHYpsxBDzCgUcwCm+Ns8GnouOmcGw=
github.com/valyala/fasthttp v1.10.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
github.com/valyala/fastjson v1.5.0 h1:DGrb4wEYso2HdGLyLmNoyNCQnCWfjd8yhghPv5/5YQg=
github.com/valyala/fastjson v1.5.0/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
github.com/valyala/fastrand v1.0.0 h1:LUKT9aKer2dVQNUi3waewTbKV+7H17kvWFNKs2ObdkI=
@ -303,8 +303,8 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200415034506-5d8e1897c761 h1:FVw4lelfGRNPqB3C8qX1m+QyeM2vzToIwlFhEZX42y8=
golang.org/x/tools v0.0.0-20200415034506-5d8e1897c761/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200416214402-fc959738d646 h1:7CEkhBsBejkW845gR1AmglqMfc1yGzn42FBmtM4jxyM=
golang.org/x/tools v0.0.0-20200416214402-fc959738d646/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
@ -349,8 +349,8 @@ google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce h1:1mbrb1tUU+Zmt5C
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=
google.golang.org/genproto v0.0.0-20200413115906-b5235f65be36 h1:j7CmVRD4Kec0+f8VuBAc2Ak2MFfXm5Q2/RxuJLL+76E=
google.golang.org/genproto v0.0.0-20200413115906-b5235f65be36/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200416231807-8751e049a2a0 h1:N5O9PpTbQrkvH0IQ1q+mmGyg8Gt6iKcu6b6+gmz3jnA=
google.golang.org/genproto v0.0.0-20200416231807-8751e049a2a0/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=

View file

@ -724,32 +724,26 @@ var awsPartition = partition{
"eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"eu-west-3": endpoint{},
"fips-ca-central-1": endpoint{
Hostname: "batch-fips.ca-central-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "ca-central-1",
},
},
"fips-us-east-1": endpoint{
Hostname: "batch-fips.us-east-1.amazonaws.com",
Hostname: "fips.batch.us-east-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-east-1",
},
},
"fips-us-east-2": endpoint{
Hostname: "batch-fips.us-east-2.amazonaws.com",
Hostname: "fips.batch.us-east-2.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-east-2",
},
},
"fips-us-west-1": endpoint{
Hostname: "batch-fips.us-west-1.amazonaws.com",
Hostname: "fips.batch.us-west-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-west-1",
},
},
"fips-us-west-2": endpoint{
Hostname: "batch-fips.us-west-2.amazonaws.com",
Hostname: "fips.batch.us-west-2.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-west-2",
},
@ -1776,12 +1770,42 @@ var awsPartition = partition{
"eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"eu-west-3": endpoint{},
"me-south-1": endpoint{},
"sa-east-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-1": endpoint{},
"us-west-2": endpoint{},
"fips-ca-central-1": endpoint{
Hostname: "ec2-fips.ca-central-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "ca-central-1",
},
},
"fips-us-east-1": endpoint{
Hostname: "ec2-fips.us-east-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-east-1",
},
},
"fips-us-east-2": endpoint{
Hostname: "ec2-fips.us-east-2.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-east-2",
},
},
"fips-us-west-1": endpoint{
Hostname: "ec2-fips.us-west-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-west-1",
},
},
"fips-us-west-2": endpoint{
Hostname: "ec2-fips.us-west-2.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-west-2",
},
},
"me-south-1": endpoint{},
"sa-east-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-1": endpoint{},
"us-west-2": endpoint{},
},
},
"ec2metadata": service{
@ -1956,12 +1980,120 @@ var awsPartition = partition{
"eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"eu-west-3": endpoint{},
"me-south-1": endpoint{},
"sa-east-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-1": endpoint{},
"us-west-2": endpoint{},
"fips-ap-east-1": endpoint{
Hostname: "elasticfilesystem-fips.ap-east-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "ap-east-1",
},
},
"fips-ap-northeast-1": endpoint{
Hostname: "elasticfilesystem-fips.ap-northeast-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "ap-northeast-1",
},
},
"fips-ap-northeast-2": endpoint{
Hostname: "elasticfilesystem-fips.ap-northeast-2.amazonaws.com",
CredentialScope: credentialScope{
Region: "ap-northeast-2",
},
},
"fips-ap-south-1": endpoint{
Hostname: "elasticfilesystem-fips.ap-south-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "ap-south-1",
},
},
"fips-ap-southeast-1": endpoint{
Hostname: "elasticfilesystem-fips.ap-southeast-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "ap-southeast-1",
},
},
"fips-ap-southeast-2": endpoint{
Hostname: "elasticfilesystem-fips.ap-southeast-2.amazonaws.com",
CredentialScope: credentialScope{
Region: "ap-southeast-2",
},
},
"fips-ca-central-1": endpoint{
Hostname: "elasticfilesystem-fips.ca-central-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "ca-central-1",
},
},
"fips-eu-central-1": endpoint{
Hostname: "elasticfilesystem-fips.eu-central-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "eu-central-1",
},
},
"fips-eu-north-1": endpoint{
Hostname: "elasticfilesystem-fips.eu-north-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "eu-north-1",
},
},
"fips-eu-west-1": endpoint{
Hostname: "elasticfilesystem-fips.eu-west-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "eu-west-1",
},
},
"fips-eu-west-2": endpoint{
Hostname: "elasticfilesystem-fips.eu-west-2.amazonaws.com",
CredentialScope: credentialScope{
Region: "eu-west-2",
},
},
"fips-eu-west-3": endpoint{
Hostname: "elasticfilesystem-fips.eu-west-3.amazonaws.com",
CredentialScope: credentialScope{
Region: "eu-west-3",
},
},
"fips-me-south-1": endpoint{
Hostname: "elasticfilesystem-fips.me-south-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "me-south-1",
},
},
"fips-sa-east-1": endpoint{
Hostname: "elasticfilesystem-fips.sa-east-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "sa-east-1",
},
},
"fips-us-east-1": endpoint{
Hostname: "elasticfilesystem-fips.us-east-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-east-1",
},
},
"fips-us-east-2": endpoint{
Hostname: "elasticfilesystem-fips.us-east-2.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-east-2",
},
},
"fips-us-west-1": endpoint{
Hostname: "elasticfilesystem-fips.us-west-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-west-1",
},
},
"fips-us-west-2": endpoint{
Hostname: "elasticfilesystem-fips.us-west-2.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-west-2",
},
},
"me-south-1": endpoint{},
"sa-east-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-1": endpoint{},
"us-west-2": endpoint{},
},
},
"elasticloadbalancing": service{
@ -2995,12 +3127,36 @@ var awsPartition = partition{
"eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"eu-west-3": endpoint{},
"me-south-1": endpoint{},
"sa-east-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-1": endpoint{},
"us-west-2": endpoint{},
"fips-us-east-1": endpoint{
Hostname: "lambda-fips.us-east-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-east-1",
},
},
"fips-us-east-2": endpoint{
Hostname: "lambda-fips.us-east-2.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-east-2",
},
},
"fips-us-west-1": endpoint{
Hostname: "lambda-fips.us-west-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-west-1",
},
},
"fips-us-west-2": endpoint{
Hostname: "lambda-fips.us-west-2.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-west-2",
},
},
"me-south-1": endpoint{},
"sa-east-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-1": endpoint{},
"us-west-2": endpoint{},
},
},
"license-manager": service{
@ -3564,6 +3720,12 @@ var awsPartition = partition{
Region: "us-east-1",
},
},
"fips-aws-global": endpoint{
Hostname: "organizations-fips.us-east-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-east-1",
},
},
},
},
"outposts": service{
@ -5622,6 +5784,13 @@ var awscnPartition = partition{
},
},
},
"api.sagemaker": service{
Endpoints: endpoints{
"cn-north-1": endpoint{},
"cn-northwest-1": endpoint{},
},
},
"apigateway": service{
Endpoints: endpoints{
@ -5647,6 +5816,7 @@ var awscnPartition = partition{
"athena": service{
Endpoints: endpoints{
"cn-north-1": endpoint{},
"cn-northwest-1": endpoint{},
},
},
@ -5810,6 +5980,18 @@ var awscnPartition = partition{
Endpoints: endpoints{
"cn-north-1": endpoint{},
"cn-northwest-1": endpoint{},
"fips-cn-north-1": endpoint{
Hostname: "elasticfilesystem-fips.cn-north-1.amazonaws.com.cn",
CredentialScope: credentialScope{
Region: "cn-north-1",
},
},
"fips-cn-northwest-1": endpoint{
Hostname: "elasticfilesystem-fips.cn-northwest-1.amazonaws.com.cn",
CredentialScope: credentialScope{
Region: "cn-northwest-1",
},
},
},
},
"elasticloadbalancing": service{
@ -5869,6 +6051,7 @@ var awscnPartition = partition{
"glue": service{
Endpoints: endpoints{
"cn-north-1": endpoint{},
"cn-northwest-1": endpoint{},
},
},
@ -5919,6 +6102,13 @@ var awscnPartition = partition{
"cn-northwest-1": endpoint{},
},
},
"kafka": service{
Endpoints: endpoints{
"cn-north-1": endpoint{},
"cn-northwest-1": endpoint{},
},
},
"kinesis": service{
Endpoints: endpoints{
@ -6005,6 +6195,13 @@ var awscnPartition = partition{
"cn-northwest-1": endpoint{},
},
},
"runtime.sagemaker": service{
Endpoints: endpoints{
"cn-north-1": endpoint{},
"cn-northwest-1": endpoint{},
},
},
"s3": service{
Defaults: endpoint{
Protocols: []string{"http", "https"},
@ -6579,8 +6776,18 @@ var awsusgovPartition = partition{
"ec2": service{
Endpoints: endpoints{
"us-gov-east-1": endpoint{},
"us-gov-west-1": endpoint{},
"us-gov-east-1": endpoint{
Hostname: "ec2.us-gov-east-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-gov-east-1",
},
},
"us-gov-west-1": endpoint{
Hostname: "ec2.us-gov-west-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-gov-west-1",
},
},
},
},
"ec2metadata": service{
@ -6646,6 +6853,18 @@ var awsusgovPartition = partition{
"elasticfilesystem": service{
Endpoints: endpoints{
"fips-us-gov-east-1": endpoint{
Hostname: "elasticfilesystem-fips.us-gov-east-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-gov-east-1",
},
},
"fips-us-gov-west-1": endpoint{
Hostname: "elasticfilesystem-fips.us-gov-west-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-gov-west-1",
},
},
"us-gov-east-1": endpoint{},
"us-gov-west-1": endpoint{},
},
@ -6839,6 +7058,18 @@ var awsusgovPartition = partition{
"lambda": service{
Endpoints: endpoints{
"fips-us-gov-east-1": endpoint{
Hostname: "lambda-fips.us-gov-east-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-gov-east-1",
},
},
"fips-us-gov-west-1": endpoint{
Hostname: "lambda-fips.us-gov-west-1.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-gov-west-1",
},
},
"us-gov-east-1": endpoint{},
"us-gov-west-1": endpoint{},
},

View file

@ -5,4 +5,4 @@ package aws
const SDKName = "aws-sdk-go"
// SDKVersion is the version of this SDK
const SDKVersion = "1.30.7"
const SDKVersion = "1.30.8"

View file

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

View file

@ -11,6 +11,7 @@ os:
- osx
go:
- tip
- 1.14.x
- 1.13.x
- 1.12.x
- 1.11.x
@ -49,7 +50,7 @@ jobs:
os:
- linux
go:
- 1.13
- 1.14
script:
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then ./fuzzit.sh fuzzing; fi
- if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then ./fuzzit.sh local-regression; fi

View file

@ -280,6 +280,7 @@ with fasthttp support:
* [fasthttprouter](https://github.com/buaazp/fasthttprouter)
* [lu](https://github.com/vincentLiuxiang/lu)
* [atreugo](https://github.com/savsgio/atreugo)
* [Fiber](https://github.com/gofiber/fiber)
Net/http code with simple ServeMux is trivially converted to fasthttp code:
@ -485,6 +486,8 @@ uintBuf := fasthttp.AppendUint(nil, 1234)
powerful routing package for fasthttp servers.
* [fasthttprouter](https://github.com/buaazp/fasthttprouter) - a high
performance fasthttp request router that scales well.
* [fastws](https://github.com/fasthttp/fastws) - Bloatless WebSocket package made for fasthttp
to handle Read/Write operations concurrently.
* [gramework](https://github.com/gramework/gramework) - a web framework made by one of fasthttp maintainers
* [lu](https://github.com/vincentLiuxiang/lu) - a high performance
go middleware web framework which is based on fasthttp.
@ -494,6 +497,7 @@ uintBuf := fasthttp.AppendUint(nil, 1234)
* [atreugo](https://github.com/savsgio/atreugo) - High performance and extensible micro web framework with zero memory allocations in hot paths.
* [kratgo](https://github.com/savsgio/kratgo) - Simple, lightweight and ultra-fast HTTP Cache to speed up your websites.
* [kit-plugins](https://github.com/wencan/kit-plugins/tree/master/transport/fasthttp) - go-kit transport implementation for fasthttp.
* [Fiber](https://github.com/gofiber/fiber) - An Expressjs inspired web framework running on Fasthttp
# FAQ
@ -565,6 +569,7 @@ uintBuf := fasthttp.AppendUint(nil, 1234)
* [gramework](https://github.com/gramework/gramework)
* [lu](https://github.com/vincentLiuxiang/lu)
* [atreugo](https://github.com/savsgio/atreugo)
* [Fiber](https://github.com/gofiber/fiber)
See also [this issue](https://github.com/valyala/fasthttp/issues/9) for more info.

View file

@ -330,6 +330,7 @@ func lowercaseBytes(b []byte) {
// Note it may break if string and/or slice header will change
// in the future go versions.
func b2s(b []byte) string {
/* #nosec G103 */
return *(*string)(unsafe.Pointer(&b))
}
@ -338,7 +339,9 @@ func b2s(b []byte) string {
// Note it may break if string and/or slice header will change
// in the future go versions.
func s2b(s string) (b []byte) {
/* #nosec G103 */
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
/* #nosec G103 */
sh := *(*reflect.StringHeader)(unsafe.Pointer(&s))
bh.Data = sh.Data
bh.Len = sh.Len

View file

@ -97,6 +97,30 @@ func DoDeadline(req *Request, resp *Response, deadline time.Time) error {
return defaultClient.DoDeadline(req, resp, deadline)
}
// DoRedirects performs the given http request and fills the given http response,
// following up to maxRedirectsCount redirects. When the redirect count exceeds
// maxRedirectsCount, ErrTooManyRedirects is returned.
//
// Request must contain at least non-zero RequestURI with full url (including
// scheme and host) or non-zero Host header + RequestURI.
//
// Client determines the server to be requested in the following order:
//
// - from RequestURI if it contains full url with scheme and host;
// - from Host header otherwise.
//
// Response is ignored if resp is nil.
//
// ErrNoFreeConns is returned if all DefaultMaxConnsPerHost connections
// to the requested host are busy.
//
// It is recommended obtaining req and resp via AcquireRequest
// and AcquireResponse in performance-critical code.
func DoRedirects(req *Request, resp *Response, maxRedirectsCount int) error {
_, _, err := doRequestFollowRedirects(req, resp, req.URI().String(), maxRedirectsCount, &defaultClient)
return err
}
// Get returns the status code and body of url.
//
// The contents of dst will be replaced by the body and returned, if the dst
@ -260,6 +284,11 @@ type Client struct {
// extra slashes are removed, special characters are encoded.
DisablePathNormalizing bool
// Maximum duration for waiting for a free connection.
//
// By default will not waiting, return ErrNoFreeConns immediately
MaxConnWaitTimeout time.Duration
mLock sync.Mutex
m map[string]*HostClient
ms map[string]*HostClient
@ -372,6 +401,30 @@ func (c *Client) DoDeadline(req *Request, resp *Response, deadline time.Time) er
return clientDoDeadline(req, resp, deadline, c)
}
// DoRedirects performs the given http request and fills the given http response,
// following up to maxRedirectsCount redirects. When the redirect count exceeds
// maxRedirectsCount, ErrTooManyRedirects is returned.
//
// Request must contain at least non-zero RequestURI with full url (including
// scheme and host) or non-zero Host header + RequestURI.
//
// Client determines the server to be requested in the following order:
//
// - from RequestURI if it contains full url with scheme and host;
// - from Host header otherwise.
//
// Response is ignored if resp is nil.
//
// ErrNoFreeConns is returned if all DefaultMaxConnsPerHost connections
// to the requested host are busy.
//
// It is recommended obtaining req and resp via AcquireRequest
// and AcquireResponse in performance-critical code.
func (c *Client) DoRedirects(req *Request, resp *Response, maxRedirectsCount int) error {
_, _, err := doRequestFollowRedirects(req, resp, req.URI().String(), maxRedirectsCount, c)
return err
}
// Do performs the given http request and fills the given http response.
//
// Request must contain at least non-zero RequestURI with full url (including
@ -439,6 +492,7 @@ func (c *Client) Do(req *Request, resp *Response) error {
MaxResponseBodySize: c.MaxResponseBodySize,
DisableHeaderNamesNormalizing: c.DisableHeaderNamesNormalizing,
DisablePathNormalizing: c.DisablePathNormalizing,
MaxConnWaitTimeout: c.MaxConnWaitTimeout,
}
m[string(host)] = hc
if len(m) == 1 {
@ -639,12 +693,18 @@ type HostClient struct {
// extra slashes are removed, special characters are encoded.
DisablePathNormalizing bool
// Maximum duration for waiting for a free connection.
//
// By default will not waiting, return ErrNoFreeConns immediately
MaxConnWaitTimeout time.Duration
clientName atomic.Value
lastUseTime uint32
connsLock sync.Mutex
connsCount int
conns []*clientConn
connsWait *wantConnQueue
addrsLock sync.Mutex
addrs []string
@ -731,7 +791,7 @@ type clientDoer interface {
func clientGetURL(dst []byte, url string, c clientDoer) (statusCode int, body []byte, err error) {
req := AcquireRequest()
statusCode, body, err = doRequestFollowRedirects(req, dst, url, c)
statusCode, body, err = doRequestFollowRedirectsBuffer(req, dst, url, c)
ReleaseRequest(req)
return statusCode, body, err
@ -771,7 +831,7 @@ func clientGetURLDeadline(dst []byte, url string, deadline time.Time, c clientDo
// concurrent requests, since timed out requests on client side
// usually continue execution on the host.
go func() {
statusCodeCopy, bodyCopy, errCopy := doRequestFollowRedirects(req, dst, url, c)
statusCodeCopy, bodyCopy, errCopy := doRequestFollowRedirectsBuffer(req, dst, url, c)
ch <- clientURLResponse{
statusCode: statusCodeCopy,
body: bodyCopy,
@ -808,29 +868,45 @@ func clientPostURL(dst []byte, url string, postArgs *Args, c clientDoer) (status
}
}
statusCode, body, err = doRequestFollowRedirects(req, dst, url, c)
statusCode, body, err = doRequestFollowRedirectsBuffer(req, dst, url, c)
ReleaseRequest(req)
return statusCode, body, err
}
var (
errMissingLocation = errors.New("missing Location header for http redirect")
errTooManyRedirects = errors.New("too many redirects detected when doing the request")
// ErrMissingLocation is returned by clients when the Location header is missing on
// an HTTP response with a redirect status code.
ErrMissingLocation = errors.New("missing Location header for http redirect")
// ErrTooManyRedirects is returned by clients when the number of redirects followed
// exceed the max count.
ErrTooManyRedirects = errors.New("too many redirects detected when doing the request")
)
const maxRedirectsCount = 16
const defaultMaxRedirectsCount = 16
func doRequestFollowRedirects(req *Request, dst []byte, url string, c clientDoer) (statusCode int, body []byte, err error) {
func doRequestFollowRedirectsBuffer(req *Request, dst []byte, url string, c clientDoer) (statusCode int, body []byte, err error) {
resp := AcquireResponse()
bodyBuf := resp.bodyBuffer()
resp.keepBodyBuffer = true
oldBody := bodyBuf.B
bodyBuf.B = dst
statusCode, body, err = doRequestFollowRedirects(req, resp, url, defaultMaxRedirectsCount, c)
body = bodyBuf.B
bodyBuf.B = oldBody
resp.keepBodyBuffer = false
ReleaseResponse(resp)
return statusCode, body, err
}
func doRequestFollowRedirects(req *Request, resp *Response, url string, maxRedirectsCount int, c clientDoer) (statusCode int, body []byte, err error) {
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()) {
@ -859,22 +935,17 @@ func doRequestFollowRedirects(req *Request, dst []byte, url string, c clientDoer
redirectsCount++
if redirectsCount > maxRedirectsCount {
err = errTooManyRedirects
err = ErrTooManyRedirects
break
}
location := resp.Header.peek(strLocation)
if len(location) == 0 {
err = errMissingLocation
err = ErrMissingLocation
break
}
url = getRedirectURL(url, location)
}
body = bodyBuf.B
bodyBuf.B = oldBody
resp.keepBodyBuffer = false
ReleaseResponse(resp)
return statusCode, body, err
}
@ -994,6 +1065,30 @@ func (c *HostClient) DoDeadline(req *Request, resp *Response, deadline time.Time
return clientDoDeadline(req, resp, deadline, c)
}
// DoRedirects performs the given http request and fills the given http response,
// following up to maxRedirectsCount redirects. When the redirect count exceeds
// maxRedirectsCount, ErrTooManyRedirects is returned.
//
// Request must contain at least non-zero RequestURI with full url (including
// scheme and host) or non-zero Host header + RequestURI.
//
// Client determines the server to be requested in the following order:
//
// - from RequestURI if it contains full url with scheme and host;
// - from Host header otherwise.
//
// Response is ignored if resp is nil.
//
// ErrNoFreeConns is returned if all DefaultMaxConnsPerHost connections
// to the requested host are busy.
//
// It is recommended obtaining req and resp via AcquireRequest
// and AcquireResponse in performance-critical code.
func (c *HostClient) DoRedirects(req *Request, resp *Response, maxRedirectsCount int) error {
_, _, err := doRequestFollowRedirects(req, resp, req.URI().String(), maxRedirectsCount, c)
return err
}
func clientDoTimeout(req *Request, resp *Response, timeout time.Duration, c clientDoer) error {
deadline := time.Now().Add(timeout)
return clientDoDeadline(req, resp, deadline, c)
@ -1036,6 +1131,7 @@ func clientDoDeadline(req *Request, resp *Response, deadline time.Time, c client
var timedout bool
go func() {
reqCopy.timeout = timeout
errDo := c.Do(reqCopy, respCopy)
mu.Lock()
{
@ -1101,6 +1197,7 @@ func (c *HostClient) Do(req *Request, resp *Response) error {
maxAttempts = DefaultMaxIdemponentCallAttempts
}
attempts := 0
hasBodyStream := req.IsBodyStream()
atomic.AddInt32(&c.pendingRequests, 1)
for {
@ -1109,6 +1206,9 @@ func (c *HostClient) Do(req *Request, resp *Response) error {
break
}
if hasBodyStream {
break
}
if !isIdempotent(req) {
// Retry non-idempotent requests if the server closes
// the connection before sending the response.
@ -1195,7 +1295,7 @@ func (c *HostClient) doNonNilReqResp(req *Request, resp *Response) (bool, error)
req.SetConnectionClose()
}
cc, err := c.acquireConn()
cc, err := c.acquireConn(req.timeout)
if err != nil {
return false, err
}
@ -1322,8 +1422,7 @@ func (c *HostClient) SetMaxConns(newMaxConns int) {
c.connsLock.Unlock()
}
func (c *HostClient) acquireConn() (*clientConn, error) {
var cc *clientConn
func (c *HostClient) acquireConn(reqTimeout time.Duration) (cc *clientConn, err error) {
createConn := false
startCleaner := false
@ -1355,7 +1454,47 @@ func (c *HostClient) acquireConn() (*clientConn, error) {
return cc, nil
}
if !createConn {
return nil, ErrNoFreeConns
if c.MaxConnWaitTimeout <= 0 {
return nil, ErrNoFreeConns
}
// reqTimeout c.MaxConnWaitTimeout wait duration
// d1 d2 min(d1, d2)
// 0(not set) d2 d2
// d1 0(don't wait) 0(don't wait)
// 0(not set) d2 d2
timeout := c.MaxConnWaitTimeout
timeoutOverridden := false
// reqTimeout == 0 means not set
if reqTimeout > 0 && reqTimeout < timeout {
timeout = reqTimeout
timeoutOverridden = true
}
// wait for a free connection
tc := AcquireTimer(timeout)
defer ReleaseTimer(tc)
w := &wantConn{
ready: make(chan struct{}, 1),
}
defer func() {
if err != nil {
w.cancel(c, err)
}
}()
c.queueForIdle(w)
select {
case <-w.ready:
return w.conn, w.err
case <-tc.C:
if timeoutOverridden {
return nil, ErrTimeout
}
return nil, ErrNoFreeConns
}
}
if startCleaner {
@ -1372,6 +1511,33 @@ func (c *HostClient) acquireConn() (*clientConn, error) {
return cc, nil
}
func (c *HostClient) queueForIdle(w *wantConn) {
c.connsLock.Lock()
defer c.connsLock.Unlock()
if c.connsWait == nil {
c.connsWait = &wantConnQueue{}
}
c.connsWait.clearFront()
c.connsWait.pushBack(w)
}
func (c *HostClient) dialConnFor(w *wantConn) {
conn, err := c.dialHostHard()
if err != nil {
w.tryDeliver(nil, err)
c.decConnsCount()
return
}
cc := acquireClientConn(conn)
delivered := w.tryDeliver(cc, nil)
if !delivered {
// not delivered, return idle connection
c.releaseConn(cc)
}
}
func (c *HostClient) connsCleaner() {
var (
scratch []*clientConn
@ -1435,9 +1601,30 @@ func (c *HostClient) closeConn(cc *clientConn) {
}
func (c *HostClient) decConnsCount() {
if c.MaxConnWaitTimeout <= 0 {
c.connsLock.Lock()
c.connsCount--
c.connsLock.Unlock()
return
}
c.connsLock.Lock()
c.connsCount--
c.connsLock.Unlock()
defer c.connsLock.Unlock()
dialed := false
if q := c.connsWait; q != nil && q.len() > 0 {
for q.len() > 0 {
w := q.popFront()
if w.waiting() {
go c.dialConnFor(w)
dialed = true
break
}
}
}
if !dialed {
c.connsCount--
}
}
func acquireClientConn(conn net.Conn) *clientConn {
@ -1461,9 +1648,29 @@ var clientConnPool sync.Pool
func (c *HostClient) releaseConn(cc *clientConn) {
cc.lastUseTime = time.Now()
if c.MaxConnWaitTimeout <= 0 {
c.connsLock.Lock()
c.conns = append(c.conns, cc)
c.connsLock.Unlock()
return
}
// try to deliver an idle connection to a *wantConn
c.connsLock.Lock()
c.conns = append(c.conns, cc)
c.connsLock.Unlock()
defer c.connsLock.Unlock()
delivered := false
if q := c.connsWait; q != nil && q.len() > 0 {
for q.len() > 0 {
w := q.popFront()
if w.waiting() {
delivered = w.tryDeliver(cc, nil)
break
}
}
}
if !delivered {
c.conns = append(c.conns, cc)
}
}
func (c *HostClient) acquireWriter(conn net.Conn) *bufio.Writer {
@ -1506,34 +1713,7 @@ func newClientTLSConfig(c *tls.Config, addr string) *tls.Config {
if c == nil {
c = &tls.Config{}
} else {
// TODO: substitute this with c.Clone() after go1.8 becomes mainstream :)
c = &tls.Config{
Rand: c.Rand,
Time: c.Time,
Certificates: c.Certificates,
NameToCertificate: c.NameToCertificate,
GetCertificate: c.GetCertificate,
RootCAs: c.RootCAs,
NextProtos: c.NextProtos,
ServerName: c.ServerName,
// Do not copy ClientAuth, since it is server-related stuff
// Do not copy ClientCAs, since it is server-related stuff
InsecureSkipVerify: c.InsecureSkipVerify,
CipherSuites: c.CipherSuites,
// Do not copy PreferServerCipherSuites - this is server stuff
SessionTicketsDisabled: c.SessionTicketsDisabled,
// Do not copy SessionTicketKey - this is server stuff
ClientSessionCache: c.ClientSessionCache,
MinVersion: c.MinVersion,
MaxVersion: c.MaxVersion,
CurvePreferences: c.CurvePreferences,
}
c = c.Clone()
}
if c.ClientSessionCache == nil {
@ -1716,6 +1896,137 @@ func addMissingPort(addr string, isTLS bool) string {
return net.JoinHostPort(addr, strconv.Itoa(port))
}
// A wantConn records state about a wanted connection
// (that is, an active call to getConn).
// The conn may be gotten by dialing or by finding an idle connection,
// or a cancellation may make the conn no longer wanted.
// These three options are racing against each other and use
// wantConn to coordinate and agree about the winning outcome.
//
// inspired by net/http/transport.go
type wantConn struct {
ready chan struct{}
mu sync.Mutex // protects conn, err, close(ready)
conn *clientConn
err error
}
// waiting reports whether w is still waiting for an answer (connection or error).
func (w *wantConn) waiting() bool {
select {
case <-w.ready:
return false
default:
return true
}
}
// tryDeliver attempts to deliver conn, err to w and reports whether it succeeded.
func (w *wantConn) tryDeliver(conn *clientConn, err error) bool {
w.mu.Lock()
defer w.mu.Unlock()
if w.conn != nil || w.err != nil {
return false
}
w.conn = conn
w.err = err
if w.conn == nil && w.err == nil {
panic("fasthttp: internal error: misuse of tryDeliver")
}
close(w.ready)
return true
}
// cancel marks w as no longer wanting a result (for example, due to cancellation).
// If a connection has been delivered already, cancel returns it with c.releaseConn.
func (w *wantConn) cancel(c *HostClient, err error) {
w.mu.Lock()
if w.conn == nil && w.err == nil {
close(w.ready) // catch misbehavior in future delivery
}
conn := w.conn
w.conn = nil
w.err = err
w.mu.Unlock()
if conn != nil {
c.releaseConn(conn)
}
}
// A wantConnQueue is a queue of wantConns.
//
// inspired by net/http/transport.go
type wantConnQueue struct {
// This is a queue, not a deque.
// It is split into two stages - head[headPos:] and tail.
// popFront is trivial (headPos++) on the first stage, and
// pushBack is trivial (append) on the second stage.
// If the first stage is empty, popFront can swap the
// first and second stages to remedy the situation.
//
// This two-stage split is analogous to the use of two lists
// in Okasaki's purely functional queue but without the
// overhead of reversing the list when swapping stages.
head []*wantConn
headPos int
tail []*wantConn
}
// len returns the number of items in the queue.
func (q *wantConnQueue) len() int {
return len(q.head) - q.headPos + len(q.tail)
}
// pushBack adds w to the back of the queue.
func (q *wantConnQueue) pushBack(w *wantConn) {
q.tail = append(q.tail, w)
}
// popFront removes and returns the wantConn at the front of the queue.
func (q *wantConnQueue) popFront() *wantConn {
if q.headPos >= len(q.head) {
if len(q.tail) == 0 {
return nil
}
// Pick up tail as new head, clear tail.
q.head, q.headPos, q.tail = q.tail, 0, q.head[:0]
}
w := q.head[q.headPos]
q.head[q.headPos] = nil
q.headPos++
return w
}
// peekFront returns the wantConn at the front of the queue without removing it.
func (q *wantConnQueue) peekFront() *wantConn {
if q.headPos < len(q.head) {
return q.head[q.headPos]
}
if len(q.tail) > 0 {
return q.tail[0]
}
return nil
}
// cleanFront pops any wantConns that are no longer waiting from the head of the
// queue, reporting whether any were popped.
func (q *wantConnQueue) clearFront() (cleaned bool) {
for {
w := q.peekFront()
if w == nil || w.waiting() {
return cleaned
}
q.popFront()
cleaned = true
}
}
// PipelineClient pipelines requests over a limited set of concurrent
// connections to the given Addr.
//

View file

@ -84,8 +84,8 @@ func (ln *InmemoryListener) Dial() (net.Conn, error) {
// Wait until the connection has been accepted.
<-accepted
} else {
sConn.Close()
cConn.Close()
sConn.Close() //nolint:errcheck
cConn.Close() //nolint:errcheck
cConn = nil
}
ln.lock.Unlock()

View file

@ -25,6 +25,7 @@ type ResponseHeader struct {
noHTTP11 bool
connectionClose bool
noDefaultContentType bool
noDefaultDate bool
statusCode int
contentLength int
@ -641,6 +642,7 @@ func (h *ResponseHeader) DisableNormalizing() {
func (h *ResponseHeader) Reset() {
h.disableNormalizing = false
h.noDefaultContentType = false
h.noDefaultDate = false
h.resetSkipNormalize()
}
@ -694,6 +696,7 @@ func (h *ResponseHeader) CopyTo(dst *ResponseHeader) {
dst.noHTTP11 = h.noHTTP11
dst.connectionClose = h.connectionClose
dst.noDefaultContentType = h.noDefaultContentType
dst.noDefaultDate = h.noDefaultDate
dst.statusCode = h.statusCode
dst.contentLength = h.contentLength
@ -1499,8 +1502,10 @@ func (h *ResponseHeader) AppendBytes(dst []byte) []byte {
dst = appendHeaderLine(dst, strServer, server)
}
serverDateOnce.Do(updateServerDate)
dst = appendHeaderLine(dst, strDate, serverDate.Load().([]byte))
if !h.noDefaultDate {
serverDateOnce.Do(updateServerDate)
dst = appendHeaderLine(dst, strDate, serverDate.Load().([]byte))
}
// Append Content-Type only for non-zero responses
// or if it is explicitly set.
@ -1518,7 +1523,7 @@ func (h *ResponseHeader) AppendBytes(dst []byte) []byte {
for i, n := 0, len(h.h); i < n; i++ {
kv := &h.h[i]
if !bytes.Equal(kv.key, strDate) {
if h.noDefaultDate || !bytes.Equal(kv.key, strDate) {
dst = appendHeaderLine(dst, kv.key, kv.value)
}
}

View file

@ -11,6 +11,7 @@ import (
"net"
"os"
"sync"
"time"
"github.com/valyala/bytebufferpool"
)
@ -49,6 +50,10 @@ type Request struct {
// To detect scheme changes in redirects
schemaUpdate bool
// Request timeout. Usually set by DoDealine or DoTimeout
// if <= 0, means not set
timeout time.Duration
}
// Response represents HTTP response.
@ -845,6 +850,7 @@ func readMultipartForm(r io.Reader, boundary string, size, maxInMemoryFileSize i
func (req *Request) Reset() {
req.Header.Reset()
req.resetSkipHeader()
req.timeout = 0
}
func (req *Request) resetSkipHeader() {
@ -931,10 +937,10 @@ func (req *Request) ReadLimitBody(r *bufio.Reader, maxBodySize int) error {
return err
}
return req.readLimitBody(r, maxBodySize, false)
return req.readLimitBody(r, maxBodySize, false, true)
}
func (req *Request) readLimitBody(r *bufio.Reader, maxBodySize int, getOnly bool) error {
func (req *Request) readLimitBody(r *bufio.Reader, maxBodySize int, getOnly bool, preParseMultipartForm bool) error {
// Do not reset the request here - the caller must reset it before
// calling this method.
@ -949,7 +955,7 @@ func (req *Request) readLimitBody(r *bufio.Reader, maxBodySize int, getOnly bool
return nil
}
return req.ContinueReadBody(r, maxBodySize)
return req.ContinueReadBody(r, maxBodySize, preParseMultipartForm)
}
// MayContinue returns true if the request contains
@ -973,7 +979,7 @@ func (req *Request) MayContinue() bool {
//
// If maxBodySize > 0 and the body size exceeds maxBodySize,
// then ErrBodyTooLarge is returned.
func (req *Request) ContinueReadBody(r *bufio.Reader, maxBodySize int) error {
func (req *Request) ContinueReadBody(r *bufio.Reader, maxBodySize int, preParseMultipartForm ...bool) error {
var err error
contentLength := req.Header.realContentLength()
if contentLength > 0 {
@ -981,16 +987,18 @@ func (req *Request) ContinueReadBody(r *bufio.Reader, maxBodySize int) error {
return ErrBodyTooLarge
}
// Pre-read multipart form data of known length.
// This way we limit memory usage for large file uploads, since their contents
// is streamed into temporary files if file size exceeds defaultMaxInMemoryFileSize.
req.multipartFormBoundary = string(req.Header.MultipartFormBoundary())
if len(req.multipartFormBoundary) > 0 && len(req.Header.peek(strContentEncoding)) == 0 {
req.multipartForm, err = readMultipartForm(r, req.multipartFormBoundary, contentLength, defaultMaxInMemoryFileSize)
if err != nil {
req.Reset()
if len(preParseMultipartForm) == 0 || preParseMultipartForm[0] {
// Pre-read multipart form data of known length.
// This way we limit memory usage for large file uploads, since their contents
// is streamed into temporary files if file size exceeds defaultMaxInMemoryFileSize.
req.multipartFormBoundary = string(req.Header.MultipartFormBoundary())
if len(req.multipartFormBoundary) > 0 && len(req.Header.peek(strContentEncoding)) == 0 {
req.multipartForm, err = readMultipartForm(r, req.multipartFormBoundary, contentLength, defaultMaxInMemoryFileSize)
if err != nil {
req.Reset()
}
return err
}
return err
}
}

View file

@ -280,6 +280,14 @@ type Server struct {
// Server accepts all the requests by default.
GetOnly bool
// Will not pre parse Multipart Form data if set to true.
//
// This option is useful for servers that desire to treat
// multipart form data as a binary blob, or choose when to parse the data.
//
// Server pre parses multipart form data by default.
DisablePreParseMultipartForm bool
// Logs all errors, including the most frequent
// 'connection reset by peer', 'broken pipe' and 'connection timeout'
// errors. Such errors are common in production serving real-world
@ -322,6 +330,13 @@ type Server struct {
// value is explicitly provided during a request.
NoDefaultServerHeader bool
// NoDefaultDate, when set to true, causes the default Date
// header to be excluded from the Response.
//
// The default Date header value is the current date value. When
// set to true, the Date will not be present.
NoDefaultDate bool
// NoDefaultContentType, when set to true, causes the default Content-Type
// header to be excluded from the Response.
//
@ -876,16 +891,21 @@ func (ctx *RequestCtx) FormFile(key string) (*multipart.FileHeader, error) {
var ErrMissingFile = errors.New("there is no uploaded file associated with the given key")
// SaveMultipartFile saves multipart file fh under the given filename path.
func SaveMultipartFile(fh *multipart.FileHeader, path string) error {
f, err := fh.Open()
func SaveMultipartFile(fh *multipart.FileHeader, path string) (err error) {
var (
f multipart.File
ff *os.File
)
f, err = fh.Open()
if err != nil {
return err
return
}
if ff, ok := f.(*os.File); ok {
var ok bool
if ff, ok = f.(*os.File); ok {
// Windows can't rename files that are opened.
if err := f.Close(); err != nil {
return err
if err = f.Close(); err != nil {
return
}
// If renaming fails we try the normal copying method.
@ -895,21 +915,29 @@ func SaveMultipartFile(fh *multipart.FileHeader, path string) error {
}
// Reopen f for the code below.
f, err = fh.Open()
if err != nil {
return err
if f, err = fh.Open(); err != nil {
return
}
}
defer f.Close()
defer func() {
e := f.Close()
if err == nil {
err = e
}
}()
ff, err := os.Create(path)
if err != nil {
return err
if ff, err = os.Create(path); err != nil {
return
}
defer ff.Close()
defer func() {
e := ff.Close()
if err == nil {
err = e
}
}()
_, err = copyZeroAlloc(ff, f)
return err
return
}
// FormValue returns form value associated with the given key.
@ -1822,7 +1850,15 @@ func (s *Server) GetCurrentConcurrency() uint32 {
//
// This function is intended be used by monitoring systems
func (s *Server) GetOpenConnectionsCount() int32 {
return atomic.LoadInt32(&s.open) - 1
if atomic.LoadInt32(&s.stop) == 0 {
// Decrement by one to avoid reporting the extra open value that gets
// counted while the server is listening.
return atomic.LoadInt32(&s.open) - 1
}
// This is not perfect, because s.stop could have changed to zero
// before we load the value of s.open. However, in the common case
// this avoids underreporting open connections by 1 during server shutdown.
return atomic.LoadInt32(&s.open)
}
func (s *Server) getConcurrency() int {
@ -1930,6 +1966,7 @@ func (s *Server) serveConn(c net.Conn) (err error) {
ctx.Request.isTLS = isTLS
ctx.Response.Header.noDefaultContentType = s.NoDefaultContentType
ctx.Response.Header.noDefaultDate = s.NoDefaultDate
if err == nil {
if s.ReadTimeout > 0 {
@ -1959,7 +1996,7 @@ func (s *Server) serveConn(c net.Conn) (err error) {
}
}
//read body
err = ctx.Request.readLimitBody(br, maxRequestBodySize, s.GetOnly)
err = ctx.Request.readLimitBody(br, maxRequestBodySize, s.GetOnly, !s.DisablePreParseMultipartForm)
}
if err == nil {
// If we read any bytes off the wire, we're active.
@ -2018,7 +2055,7 @@ func (s *Server) serveConn(c net.Conn) (err error) {
if br == nil {
br = acquireReader(ctx)
}
err = ctx.Request.ContinueReadBody(br, maxRequestBodySize)
err = ctx.Request.ContinueReadBody(br, maxRequestBodySize, !s.DisablePreParseMultipartForm)
if (s.ReduceMemoryUsage && br.Buffered() == 0) || err != nil {
releaseReader(s, br)
br = nil
@ -2058,7 +2095,7 @@ func (s *Server) serveConn(c net.Conn) (err error) {
hijackHandler = ctx.hijackHandler
ctx.hijackHandler = nil
hijackNoResponse = ctx.hijackNoResponse
hijackNoResponse = ctx.hijackNoResponse && hijackHandler != nil
ctx.hijackNoResponse = false
ctx.userValues.Reset()
@ -2492,16 +2529,20 @@ func (s *Server) writeFastError(w io.Writer, statusCode int, msg string) {
server = fmt.Sprintf("Server: %s\r\n", s.getServerName())
}
serverDateOnce.Do(updateServerDate)
date := ""
if !s.NoDefaultDate {
serverDateOnce.Do(updateServerDate)
date = fmt.Sprintf("Date: %s\r\n", serverDate.Load())
}
fmt.Fprintf(w, "Connection: close\r\n"+
server+
"Date: %s\r\n"+
date+
"Content-Type: text/plain\r\n"+
"Content-Length: %d\r\n"+
"\r\n"+
"%s",
serverDate.Load(), len(msg), msg)
len(msg), msg)
}
func defaultErrorHandler(ctx *RequestCtx, err error) {

View file

@ -135,6 +135,11 @@ type TCPDialer struct {
// Changes made after the first Dial will not affect anything.
Concurrency int
// LocalAddr is the local address to use when dialing an
// address.
// If nil, a local address is automatically chosen.
LocalAddr *net.TCPAddr
// This may be used to override DNS resolving policy, like this:
// var dialer = &fasthttp.TCPDialer{
// Resolver: &net.Resolver{
@ -284,7 +289,7 @@ func (d *TCPDialer) dial(addr string, dualStack bool, timeout time.Duration) (ne
n := uint32(len(addrs))
deadline := time.Now().Add(timeout)
for n > 0 {
conn, err = tryDial(network, &addrs[idx%n], deadline, d.concurrencyCh)
conn, err = d.tryDial(network, &addrs[idx%n], deadline, d.concurrencyCh)
if err == nil {
return conn, nil
}
@ -297,7 +302,7 @@ func (d *TCPDialer) dial(addr string, dualStack bool, timeout time.Duration) (ne
return nil, err
}
func tryDial(network string, addr *net.TCPAddr, deadline time.Time, concurrencyCh chan struct{}) (net.Conn, error) {
func (d *TCPDialer) tryDial(network string, addr *net.TCPAddr, deadline time.Time, concurrencyCh chan struct{}) (net.Conn, error) {
timeout := -time.Since(deadline)
if timeout <= 0 {
return nil, ErrDialTimeout
@ -328,7 +333,7 @@ func tryDial(network string, addr *net.TCPAddr, deadline time.Time, concurrencyC
ch := chv.(chan dialResult)
go func() {
var dr dialResult
dr.conn, dr.err = net.DialTCP(network, nil, addr)
dr.conn, dr.err = net.DialTCP(network, d.LocalAddr, addr)
ch <- dr
if concurrencyCh != nil {
<-concurrencyCh

View file

@ -401,10 +401,6 @@ func (u *URI) RequestURI() []byte {
dst = append(dst, '?')
dst = append(dst, u.queryString...)
}
if len(u.hash) > 0 {
dst = append(dst, '#')
dst = append(dst, u.hash...)
}
u.requestURI = dst
return u.requestURI
}
@ -519,7 +515,12 @@ func (u *URI) FullURI() []byte {
// AppendBytes appends full uri to dst and returns the extended dst.
func (u *URI) AppendBytes(dst []byte) []byte {
dst = u.appendSchemeHost(dst)
return append(dst, u.RequestURI()...)
dst = append(dst, u.RequestURI()...)
if len(u.hash) > 0 {
dst = append(dst, '#')
dst = append(dst, u.hash...)
}
return dst
}
func (u *URI) appendSchemeHost(dst []byte) []byte {

View file

@ -5,6 +5,7 @@ import (
"fmt"
"go/parser"
"go/token"
"log"
"os"
"path/filepath"
"sort"
@ -22,10 +23,15 @@ func (state *golistState) processGolistOverlay(response *responseDeduper) (modif
needPkgsSet := make(map[string]bool)
modifiedPkgsSet := make(map[string]bool)
pkgOfDir := make(map[string][]*Package)
for _, pkg := range response.dr.Packages {
// This is an approximation of import path to id. This can be
// wrong for tests, vendored packages, and a number of other cases.
havePkgs[pkg.PkgPath] = pkg.ID
x := commonDir(pkg.GoFiles)
if x != "" {
pkgOfDir[x] = append(pkgOfDir[x], pkg)
}
}
// If no new imports are added, it is safe to avoid loading any needPkgs.
@ -64,6 +70,9 @@ func (state *golistState) processGolistOverlay(response *responseDeduper) (modif
// to the overlay.
continue
}
// if all the overlay files belong to a different package, change the package
// name to that package. Otherwise leave it alone; there will be an error message.
maybeFixPackageName(pkgName, pkgOfDir, dir)
nextPackage:
for _, p := range response.dr.Packages {
if pkgName != p.Name && p.ID != "command-line-arguments" {
@ -384,3 +393,46 @@ func extractPackageName(filename string, contents []byte) (string, bool) {
}
return f.Name.Name, true
}
func commonDir(a []string) string {
seen := make(map[string]bool)
x := append([]string{}, a...)
for _, f := range x {
seen[filepath.Dir(f)] = true
}
if len(seen) > 1 {
log.Fatalf("commonDir saw %v for %v", seen, x)
}
for k := range seen {
// len(seen) == 1
return k
}
return "" // no files
}
// It is possible that the files in the disk directory dir have a different package
// name from newName, which is deduced from the overlays. If they all have a different
// package name, and they all have the same package name, then that name becomes
// the package name.
// It returns true if it changes the package name, false otherwise.
func maybeFixPackageName(newName string, pkgOfDir map[string][]*Package, dir string) bool {
names := make(map[string]int)
for _, p := range pkgOfDir[dir] {
names[p.Name]++
}
if len(names) != 1 {
// some files are in different packages
return false
}
oldName := ""
for k := range names {
oldName = k
}
if newName == oldName {
return false
}
for _, p := range pkgOfDir[dir] {
p.Name = newName
}
return true
}

View file

@ -72,7 +72,7 @@ func (ev Event) Format(f fmt.State, r rune) {
tag := ev.Tag(index)
// msg and err were both already printed above, so we skip them to avoid
// double printing
if !tag.Valid() || tag.Key == Msg || tag.Key == Err {
if !tag.Valid() || tag.Key() == Msg || tag.Key() == Err {
continue
}
fmt.Fprintf(f, "\n\t%v", tag)
@ -90,14 +90,14 @@ func (ev Event) Tag(index int) Tag {
return ev.dynamic[index-len(ev.static)]
}
func (ev Event) Find(key interface{}) Tag {
func (ev Event) Find(key Key) Tag {
for _, tag := range ev.static {
if tag.Key == key {
if tag.Key() == key {
return tag
}
}
for _, tag := range ev.dynamic {
if tag.Key == key {
if tag.Key() == key {
return tag
}
}

View file

@ -5,7 +5,10 @@
package event
import (
"fmt"
"io"
"math"
"strconv"
)
var (
@ -17,20 +20,20 @@ var (
Err = NewErrorKey("error", "an error that occurred")
)
// Key is the interface shared by all key implementations.
// Key is used as the identity of a Tag.
// Keys are intended to be compared by pointer only, the name should be unique
// for communicating with external systems, but it is not required or enforced.
type Key interface {
// Name returns the key name.
Name() string
// Description returns a string that can be used to describe the value.
Description() string
}
// key is used as the identity of a Tag.
// Keys are intended to be compared by pointer only, the name should be unique
// for communicating with external systems, but it is not required or enforced.
type key struct {
name string
description string
// Format is used in formatting to append the value of the tag to the
// supplied buffer.
// The formatter may use the supplied buf as a scratch area to avoid
// allocations.
Format(w io.Writer, buf []byte, tag Tag)
}
// ValueKey represents a key for untyped values.
@ -47,6 +50,10 @@ func NewKey(name, description string) *ValueKey {
func (k *ValueKey) Name() string { return k.name }
func (k *ValueKey) Description() string { return k.description }
func (k *ValueKey) Format(w io.Writer, buf []byte, tag Tag) {
fmt.Fprint(w, k.From(tag))
}
// Get can be used to get a tag for the key from a TagMap.
func (k *ValueKey) Get(tags TagMap) interface{} {
if t := tags.Find(k); t.Valid() {
@ -56,10 +63,10 @@ func (k *ValueKey) Get(tags TagMap) interface{} {
}
// From can be used to get a value from a Tag.
func (k *ValueKey) From(t Tag) interface{} { return t.untyped }
func (k *ValueKey) From(t Tag) interface{} { return t.UnpackValue() }
// Of creates a new Tag with this key and the supplied value.
func (k *ValueKey) Of(value interface{}) Tag { return Tag{Key: k, untyped: value} }
func (k *ValueKey) Of(value interface{}) Tag { return TagOfValue(k, value) }
// IntKey represents a key
type IntKey struct {
@ -75,8 +82,12 @@ func NewIntKey(name, description string) *IntKey {
func (k *IntKey) Name() string { return k.name }
func (k *IntKey) Description() string { return k.description }
func (k *IntKey) Format(w io.Writer, buf []byte, tag Tag) {
w.Write(strconv.AppendInt(buf, int64(k.From(tag)), 10))
}
// Of creates a new Tag with this key and the supplied value.
func (k *IntKey) Of(v int) Tag { return Tag{Key: k, packed: uint64(v)} }
func (k *IntKey) Of(v int) Tag { return TagOf64(k, uint64(v)) }
// Get can be used to get a tag for the key from a TagMap.
func (k *IntKey) Get(tags TagMap) int {
@ -87,7 +98,7 @@ func (k *IntKey) Get(tags TagMap) int {
}
// From can be used to get a value from a Tag.
func (k *IntKey) From(t Tag) int { return int(t.packed) }
func (k *IntKey) From(t Tag) int { return int(t.Unpack64()) }
// Int8Key represents a key
type Int8Key struct {
@ -103,8 +114,12 @@ func NewInt8Key(name, description string) *Int8Key {
func (k *Int8Key) Name() string { return k.name }
func (k *Int8Key) Description() string { return k.description }
func (k *Int8Key) Format(w io.Writer, buf []byte, tag Tag) {
w.Write(strconv.AppendInt(buf, int64(k.From(tag)), 10))
}
// Of creates a new Tag with this key and the supplied value.
func (k *Int8Key) Of(v int8) Tag { return Tag{Key: k, packed: uint64(v)} }
func (k *Int8Key) Of(v int8) Tag { return TagOf64(k, uint64(v)) }
// Get can be used to get a tag for the key from a TagMap.
func (k *Int8Key) Get(tags TagMap) int8 {
@ -115,7 +130,7 @@ func (k *Int8Key) Get(tags TagMap) int8 {
}
// From can be used to get a value from a Tag.
func (k *Int8Key) From(t Tag) int8 { return int8(t.packed) }
func (k *Int8Key) From(t Tag) int8 { return int8(t.Unpack64()) }
// Int16Key represents a key
type Int16Key struct {
@ -131,8 +146,12 @@ func NewInt16Key(name, description string) *Int16Key {
func (k *Int16Key) Name() string { return k.name }
func (k *Int16Key) Description() string { return k.description }
func (k *Int16Key) Format(w io.Writer, buf []byte, tag Tag) {
w.Write(strconv.AppendInt(buf, int64(k.From(tag)), 10))
}
// Of creates a new Tag with this key and the supplied value.
func (k *Int16Key) Of(v int16) Tag { return Tag{Key: k, packed: uint64(v)} }
func (k *Int16Key) Of(v int16) Tag { return TagOf64(k, uint64(v)) }
// Get can be used to get a tag for the key from a TagMap.
func (k *Int16Key) Get(tags TagMap) int16 {
@ -143,7 +162,7 @@ func (k *Int16Key) Get(tags TagMap) int16 {
}
// From can be used to get a value from a Tag.
func (k *Int16Key) From(t Tag) int16 { return int16(t.packed) }
func (k *Int16Key) From(t Tag) int16 { return int16(t.Unpack64()) }
// Int32Key represents a key
type Int32Key struct {
@ -159,8 +178,12 @@ func NewInt32Key(name, description string) *Int32Key {
func (k *Int32Key) Name() string { return k.name }
func (k *Int32Key) Description() string { return k.description }
func (k *Int32Key) Format(w io.Writer, buf []byte, tag Tag) {
w.Write(strconv.AppendInt(buf, int64(k.From(tag)), 10))
}
// Of creates a new Tag with this key and the supplied value.
func (k *Int32Key) Of(v int32) Tag { return Tag{Key: k, packed: uint64(v)} }
func (k *Int32Key) Of(v int32) Tag { return TagOf64(k, uint64(v)) }
// Get can be used to get a tag for the key from a TagMap.
func (k *Int32Key) Get(tags TagMap) int32 {
@ -171,7 +194,7 @@ func (k *Int32Key) Get(tags TagMap) int32 {
}
// From can be used to get a value from a Tag.
func (k *Int32Key) From(t Tag) int32 { return int32(t.packed) }
func (k *Int32Key) From(t Tag) int32 { return int32(t.Unpack64()) }
// Int64Key represents a key
type Int64Key struct {
@ -187,8 +210,12 @@ func NewInt64Key(name, description string) *Int64Key {
func (k *Int64Key) Name() string { return k.name }
func (k *Int64Key) Description() string { return k.description }
func (k *Int64Key) Format(w io.Writer, buf []byte, tag Tag) {
w.Write(strconv.AppendInt(buf, k.From(tag), 10))
}
// Of creates a new Tag with this key and the supplied value.
func (k *Int64Key) Of(v int64) Tag { return Tag{Key: k, packed: uint64(v)} }
func (k *Int64Key) Of(v int64) Tag { return TagOf64(k, uint64(v)) }
// Get can be used to get a tag for the key from a TagMap.
func (k *Int64Key) Get(tags TagMap) int64 {
@ -199,7 +226,7 @@ func (k *Int64Key) Get(tags TagMap) int64 {
}
// From can be used to get a value from a Tag.
func (k *Int64Key) From(t Tag) int64 { return int64(t.packed) }
func (k *Int64Key) From(t Tag) int64 { return int64(t.Unpack64()) }
// UIntKey represents a key
type UIntKey struct {
@ -215,8 +242,12 @@ func NewUIntKey(name, description string) *UIntKey {
func (k *UIntKey) Name() string { return k.name }
func (k *UIntKey) Description() string { return k.description }
func (k *UIntKey) Format(w io.Writer, buf []byte, tag Tag) {
w.Write(strconv.AppendUint(buf, uint64(k.From(tag)), 10))
}
// Of creates a new Tag with this key and the supplied value.
func (k *UIntKey) Of(v uint) Tag { return Tag{Key: k, packed: uint64(v)} }
func (k *UIntKey) Of(v uint) Tag { return TagOf64(k, uint64(v)) }
// Get can be used to get a tag for the key from a TagMap.
func (k *UIntKey) Get(tags TagMap) uint {
@ -227,7 +258,7 @@ func (k *UIntKey) Get(tags TagMap) uint {
}
// From can be used to get a value from a Tag.
func (k *UIntKey) From(t Tag) uint { return uint(t.packed) }
func (k *UIntKey) From(t Tag) uint { return uint(t.Unpack64()) }
// UInt8Key represents a key
type UInt8Key struct {
@ -243,8 +274,12 @@ func NewUInt8Key(name, description string) *UInt8Key {
func (k *UInt8Key) Name() string { return k.name }
func (k *UInt8Key) Description() string { return k.description }
func (k *UInt8Key) Format(w io.Writer, buf []byte, tag Tag) {
w.Write(strconv.AppendUint(buf, uint64(k.From(tag)), 10))
}
// Of creates a new Tag with this key and the supplied value.
func (k *UInt8Key) Of(v uint8) Tag { return Tag{Key: k, packed: uint64(v)} }
func (k *UInt8Key) Of(v uint8) Tag { return TagOf64(k, uint64(v)) }
// Get can be used to get a tag for the key from a TagMap.
func (k *UInt8Key) Get(tags TagMap) uint8 {
@ -255,7 +290,7 @@ func (k *UInt8Key) Get(tags TagMap) uint8 {
}
// From can be used to get a value from a Tag.
func (k *UInt8Key) From(t Tag) uint8 { return uint8(t.packed) }
func (k *UInt8Key) From(t Tag) uint8 { return uint8(t.Unpack64()) }
// UInt16Key represents a key
type UInt16Key struct {
@ -271,8 +306,12 @@ func NewUInt16Key(name, description string) *UInt16Key {
func (k *UInt16Key) Name() string { return k.name }
func (k *UInt16Key) Description() string { return k.description }
func (k *UInt16Key) Format(w io.Writer, buf []byte, tag Tag) {
w.Write(strconv.AppendUint(buf, uint64(k.From(tag)), 10))
}
// Of creates a new Tag with this key and the supplied value.
func (k *UInt16Key) Of(v uint16) Tag { return Tag{Key: k, packed: uint64(v)} }
func (k *UInt16Key) Of(v uint16) Tag { return TagOf64(k, uint64(v)) }
// Get can be used to get a tag for the key from a TagMap.
func (k *UInt16Key) Get(tags TagMap) uint16 {
@ -283,7 +322,7 @@ func (k *UInt16Key) Get(tags TagMap) uint16 {
}
// From can be used to get a value from a Tag.
func (k *UInt16Key) From(t Tag) uint16 { return uint16(t.packed) }
func (k *UInt16Key) From(t Tag) uint16 { return uint16(t.Unpack64()) }
// UInt32Key represents a key
type UInt32Key struct {
@ -299,8 +338,12 @@ func NewUInt32Key(name, description string) *UInt32Key {
func (k *UInt32Key) Name() string { return k.name }
func (k *UInt32Key) Description() string { return k.description }
func (k *UInt32Key) Format(w io.Writer, buf []byte, tag Tag) {
w.Write(strconv.AppendUint(buf, uint64(k.From(tag)), 10))
}
// Of creates a new Tag with this key and the supplied value.
func (k *UInt32Key) Of(v uint32) Tag { return Tag{Key: k, packed: uint64(v)} }
func (k *UInt32Key) Of(v uint32) Tag { return TagOf64(k, uint64(v)) }
// Get can be used to get a tag for the key from a TagMap.
func (k *UInt32Key) Get(tags TagMap) uint32 {
@ -311,7 +354,7 @@ func (k *UInt32Key) Get(tags TagMap) uint32 {
}
// From can be used to get a value from a Tag.
func (k *UInt32Key) From(t Tag) uint32 { return uint32(t.packed) }
func (k *UInt32Key) From(t Tag) uint32 { return uint32(t.Unpack64()) }
// UInt64Key represents a key
type UInt64Key struct {
@ -327,8 +370,12 @@ func NewUInt64Key(name, description string) *UInt64Key {
func (k *UInt64Key) Name() string { return k.name }
func (k *UInt64Key) Description() string { return k.description }
func (k *UInt64Key) Format(w io.Writer, buf []byte, tag Tag) {
w.Write(strconv.AppendUint(buf, k.From(tag), 10))
}
// Of creates a new Tag with this key and the supplied value.
func (k *UInt64Key) Of(v uint64) Tag { return Tag{Key: k, packed: v} }
func (k *UInt64Key) Of(v uint64) Tag { return TagOf64(k, v) }
// Get can be used to get a tag for the key from a TagMap.
func (k *UInt64Key) Get(tags TagMap) uint64 {
@ -339,7 +386,7 @@ func (k *UInt64Key) Get(tags TagMap) uint64 {
}
// From can be used to get a value from a Tag.
func (k *UInt64Key) From(t Tag) uint64 { return t.packed }
func (k *UInt64Key) From(t Tag) uint64 { return t.Unpack64() }
// Float32Key represents a key
type Float32Key struct {
@ -355,9 +402,13 @@ func NewFloat32Key(name, description string) *Float32Key {
func (k *Float32Key) Name() string { return k.name }
func (k *Float32Key) Description() string { return k.description }
func (k *Float32Key) Format(w io.Writer, buf []byte, tag Tag) {
w.Write(strconv.AppendFloat(buf, float64(k.From(tag)), 'E', -1, 32))
}
// Of creates a new Tag with this key and the supplied value.
func (k *Float32Key) Of(v float32) Tag {
return Tag{Key: k, packed: uint64(math.Float32bits(v))}
return TagOf64(k, uint64(math.Float32bits(v)))
}
// Get can be used to get a tag for the key from a TagMap.
@ -370,7 +421,7 @@ func (k *Float32Key) Get(tags TagMap) float32 {
// From can be used to get a value from a Tag.
func (k *Float32Key) From(t Tag) float32 {
return math.Float32frombits(uint32(t.packed))
return math.Float32frombits(uint32(t.Unpack64()))
}
// Float64Key represents a key
@ -387,9 +438,13 @@ func NewFloat64Key(name, description string) *Float64Key {
func (k *Float64Key) Name() string { return k.name }
func (k *Float64Key) Description() string { return k.description }
func (k *Float64Key) Format(w io.Writer, buf []byte, tag Tag) {
w.Write(strconv.AppendFloat(buf, k.From(tag), 'E', -1, 64))
}
// Of creates a new Tag with this key and the supplied value.
func (k *Float64Key) Of(v float64) Tag {
return Tag{Key: k, packed: math.Float64bits(v)}
return TagOf64(k, math.Float64bits(v))
}
// Get can be used to get a tag for the key from a TagMap.
@ -402,7 +457,7 @@ func (k *Float64Key) Get(tags TagMap) float64 {
// From can be used to get a value from a Tag.
func (k *Float64Key) From(t Tag) float64 {
return math.Float64frombits(t.packed)
return math.Float64frombits(t.Unpack64())
}
// StringKey represents a key
@ -419,8 +474,12 @@ func NewStringKey(name, description string) *StringKey {
func (k *StringKey) Name() string { return k.name }
func (k *StringKey) Description() string { return k.description }
func (k *StringKey) Format(w io.Writer, buf []byte, tag Tag) {
w.Write(strconv.AppendQuote(buf, k.From(tag)))
}
// Of creates a new Tag with this key and the supplied value.
func (k *StringKey) Of(v string) Tag { return Tag{Key: k, str: v} }
func (k *StringKey) Of(v string) Tag { return TagOfString(k, v) }
// Get can be used to get a tag for the key from a TagMap.
func (k *StringKey) Get(tags TagMap) string {
@ -431,7 +490,7 @@ func (k *StringKey) Get(tags TagMap) string {
}
// From can be used to get a value from a Tag.
func (k *StringKey) From(t Tag) string { return t.str }
func (k *StringKey) From(t Tag) string { return t.UnpackString() }
// BooleanKey represents a key
type BooleanKey struct {
@ -447,13 +506,16 @@ func NewBooleanKey(name, description string) *BooleanKey {
func (k *BooleanKey) Name() string { return k.name }
func (k *BooleanKey) Description() string { return k.description }
func (k *BooleanKey) Format(w io.Writer, buf []byte, tag Tag) {
w.Write(strconv.AppendBool(buf, k.From(tag)))
}
// Of creates a new Tag with this key and the supplied value.
func (k *BooleanKey) Of(v bool) Tag {
t := Tag{Key: k}
if v {
t.packed = 1
return TagOf64(k, 1)
}
return t
return TagOf64(k, 0)
}
// Get can be used to get a tag for the key from a TagMap.
@ -465,7 +527,7 @@ func (k *BooleanKey) Get(tags TagMap) bool {
}
// From can be used to get a value from a Tag.
func (k *BooleanKey) From(t Tag) bool { return t.packed > 0 }
func (k *BooleanKey) From(t Tag) bool { return t.Unpack64() > 0 }
// ErrorKey represents a key
type ErrorKey struct {
@ -481,8 +543,12 @@ func NewErrorKey(name, description string) *ErrorKey {
func (k *ErrorKey) Name() string { return k.name }
func (k *ErrorKey) Description() string { return k.description }
func (k *ErrorKey) Format(w io.Writer, buf []byte, tag Tag) {
io.WriteString(w, k.From(tag).Error())
}
// Of creates a new Tag with this key and the supplied value.
func (k *ErrorKey) Of(v error) Tag { return Tag{Key: k, untyped: v} }
func (k *ErrorKey) Of(v error) Tag { return TagOfValue(k, v) }
// Get can be used to get a tag for the key from a TagMap.
func (k *ErrorKey) Get(tags TagMap) error {
@ -494,6 +560,6 @@ func (k *ErrorKey) Get(tags TagMap) error {
// From can be used to get a value from a Tag.
func (k *ErrorKey) From(t Tag) error {
err, _ := t.untyped.(error)
err, _ := t.UnpackValue().(error)
return err
}

View file

@ -6,21 +6,23 @@ package event
import (
"fmt"
"io"
"reflect"
"unsafe"
)
// Tag holds a key and value pair.
// It is normally used when passing around lists of tags.
type Tag struct {
Key Key
key Key
packed uint64
str string
untyped interface{}
}
// TagMap is the interface to a collection of Tags indexed by key.
type TagMap interface {
// Find returns the tag that matches the supplied key.
Find(key interface{}) Tag
Find(key Key) Tag
}
// TagList is the interface to something that provides an iterable
@ -55,51 +57,69 @@ type tagMapChain struct {
maps []TagMap
}
// TagOfValue creates a new tag from the key and value.
// This method is for implementing new key types, tag creation should
// normally be done with the Of method of the key.
func TagOfValue(k Key, value interface{}) Tag { return Tag{key: k, untyped: value} }
// UnpackValue assumes the tag was built using TagOfValue and returns the value
// that was passed to that constructor.
// This method is for implementing new key types, for type safety normal
// access should be done with the From method of the key.
func (t Tag) UnpackValue() interface{} { return t.untyped }
// TagOf64 creates a new tag from a key and a uint64. This is often
// used for non uint64 values that can be packed into a uint64.
// This method is for implementing new key types, tag creation should
// normally be done with the Of method of the key.
func TagOf64(k Key, v uint64) Tag { return Tag{key: k, packed: v} }
// Unpack64 assumes the tag was built using TagOf64 and returns the value that
// was passed to that constructor.
// This method is for implementing new key types, for type safety normal
// access should be done with the From method of the key.
func (t Tag) Unpack64() uint64 { return t.packed }
// TagOfString creates a new tag from a key and a string.
// This method is for implementing new key types, tag creation should
// normally be done with the Of method of the key.
func TagOfString(k Key, v string) Tag {
hdr := (*reflect.StringHeader)(unsafe.Pointer(&v))
return Tag{
key: k,
packed: uint64(hdr.Len),
untyped: unsafe.Pointer(hdr.Data),
}
}
// UnpackString assumes the tag was built using TagOfString and returns the
// value that was passed to that constructor.
// This method is for implementing new key types, for type safety normal
// access should be done with the From method of the key.
func (t Tag) UnpackString() string {
var v string
hdr := (*reflect.StringHeader)(unsafe.Pointer(&v))
hdr.Data = uintptr(t.untyped.(unsafe.Pointer))
hdr.Len = int(t.packed)
return *(*string)(unsafe.Pointer(hdr))
}
// Valid returns true if the Tag is a valid one (it has a key).
func (t Tag) Valid() bool { return t.Key != nil }
func (t Tag) Valid() bool { return t.key != nil }
// Key returns the key of this Tag.
func (t Tag) Key() Key { return t.key }
// Format is used for debug printing of tags.
func (t Tag) Format(f fmt.State, r rune) {
if !t.Valid() {
fmt.Fprintf(f, `nil`)
io.WriteString(f, `nil`)
return
}
switch key := t.Key.(type) {
case *IntKey:
fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t))
case *Int8Key:
fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t))
case *Int16Key:
fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t))
case *Int32Key:
fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t))
case *Int64Key:
fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t))
case *UIntKey:
fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t))
case *UInt8Key:
fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t))
case *UInt16Key:
fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t))
case *UInt32Key:
fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t))
case *UInt64Key:
fmt.Fprintf(f, "%s=%d", key.Name(), key.From(t))
case *Float32Key:
fmt.Fprintf(f, "%s=%g", key.Name(), key.From(t))
case *Float64Key:
fmt.Fprintf(f, "%s=%g", key.Name(), key.From(t))
case *BooleanKey:
fmt.Fprintf(f, "%s=%t", key.Name(), key.From(t))
case *StringKey:
fmt.Fprintf(f, "%s=%q", key.Name(), key.From(t))
case *ErrorKey:
fmt.Fprintf(f, "%s=%v", key.Name(), key.From(t))
case *ValueKey:
fmt.Fprintf(f, "%s=%v", key.Name(), key.From(t))
default:
fmt.Fprintf(f, `%s="invalid type %T"`, key.Name(), key)
}
io.WriteString(f, t.Key().Name())
io.WriteString(f, "=")
var buf [128]byte
t.Key().Format(f, buf[:0], t)
}
func (l *tagList) Valid(index int) bool {
@ -117,23 +137,23 @@ func (f *tagFilter) Valid(index int) bool {
func (f *tagFilter) Tag(index int) Tag {
tag := f.underlying.Tag(index)
for _, f := range f.keys {
if tag.Key == f {
if tag.Key() == f {
return Tag{}
}
}
return tag
}
func (l tagMap) Find(key interface{}) Tag {
func (l tagMap) Find(key Key) Tag {
for _, tag := range l.tags {
if tag.Key == key {
if tag.Key() == key {
return tag
}
}
return Tag{}
}
func (c tagMapChain) Find(key interface{}) Tag {
func (c tagMapChain) Find(key Key) Tag {
for _, src := range c.maps {
tag := src.Find(key)
if tag.Valid() {

8
vendor/modules.txt vendored
View file

@ -14,7 +14,7 @@ github.com/BurntSushi/toml
github.com/VictoriaMetrics/fastcache
# github.com/VictoriaMetrics/metrics v1.11.2
github.com/VictoriaMetrics/metrics
# github.com/aws/aws-sdk-go v1.30.7
# github.com/aws/aws-sdk-go v1.30.8
github.com/aws/aws-sdk-go/aws
github.com/aws/aws-sdk-go/aws/arn
github.com/aws/aws-sdk-go/aws/awserr
@ -93,7 +93,7 @@ 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.9.0
# github.com/valyala/fasthttp v1.10.0
github.com/valyala/fasthttp
github.com/valyala/fasthttp/fasthttputil
github.com/valyala/fasthttp/stackless
@ -153,7 +153,7 @@ golang.org/x/text/secure/bidirule
golang.org/x/text/transform
golang.org/x/text/unicode/bidi
golang.org/x/text/unicode/norm
# golang.org/x/tools v0.0.0-20200415034506-5d8e1897c761
# golang.org/x/tools v0.0.0-20200416214402-fc959738d646
golang.org/x/tools/cmd/goimports
golang.org/x/tools/go/analysis
golang.org/x/tools/go/analysis/passes/inspect
@ -202,7 +202,7 @@ google.golang.org/appengine/internal/modules
google.golang.org/appengine/internal/remote_api
google.golang.org/appengine/internal/urlfetch
google.golang.org/appengine/urlfetch
# google.golang.org/genproto v0.0.0-20200413115906-b5235f65be36
# google.golang.org/genproto v0.0.0-20200416231807-8751e049a2a0
google.golang.org/genproto/googleapis/api/annotations
google.golang.org/genproto/googleapis/iam/v1
google.golang.org/genproto/googleapis/rpc/code