mirror of https://github.com/k3s-io/k3s
Update golang.org/x/net/... dependencies to release-branch.go1.11
- latest grpc-ecosystem/go-grpc-middleware Change-Id: Ida7d01e4606f6e0313e1355db6e85be0c0ef1dd1pull/564/head
parent
9cbccd3859
commit
317ecf58cc
|
@ -3,6 +3,9 @@
|
|||
"GoVersion": "go1.12",
|
||||
"GodepVersion": "v80-k8s-r1",
|
||||
"Packages": [
|
||||
"golang.org/x/net/internal/nettest",
|
||||
"golang.org/x/net/internal/socks",
|
||||
"golang.org/x/net/internal/sockstest",
|
||||
"github.com/bazelbuild/bazel-gazelle/cmd/gazelle",
|
||||
"github.com/bazelbuild/buildtools/buildozer",
|
||||
"github.com/cespare/prettybench",
|
||||
|
@ -2385,8 +2388,8 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/grpc-ecosystem/go-grpc-middleware",
|
||||
"Comment": "v1.0.0-4-g498ae206fc3cfe",
|
||||
"Rev": "498ae206fc3cfe81cd82e48c1d4354026fa5f9ec"
|
||||
"Comment": "v1.0.0-8-gcfaf5686ec79ff",
|
||||
"Rev": "cfaf5686ec79ff8344257723b6f5ba1ae0ffeb4d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/grpc-ecosystem/go-grpc-prometheus",
|
||||
|
@ -3622,19 +3625,23 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context/ctxhttp",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/html",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/html/atom",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/html/charset",
|
||||
|
@ -3642,35 +3649,43 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/internal/nettest",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/internal/socks",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/internal/sockstest",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/internal/timeseries",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/proxy",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/trace",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/websocket",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
|
|
|
@ -54,21 +54,21 @@
|
|||
"ImportPath": "github.com/stretchr/testify/require",
|
||||
"Rev": "c679ae2cc0cb27ec3293fea7e254e47386f05d69"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||
|
|
|
@ -488,7 +488,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/grpc-ecosystem/go-grpc-middleware",
|
||||
"Rev": "498ae206fc3cfe81cd82e48c1d4354026fa5f9ec"
|
||||
"Rev": "cfaf5686ec79ff8344257723b6f5ba1ae0ffeb4d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/grpc-ecosystem/go-grpc-prometheus",
|
||||
|
@ -688,35 +688,35 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/internal/timeseries",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/trace",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/websocket",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
|
|
|
@ -120,31 +120,31 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/html",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/html/atom",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/websocket",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||
|
|
|
@ -468,7 +468,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/grpc-ecosystem/go-grpc-middleware",
|
||||
"Rev": "498ae206fc3cfe81cd82e48c1d4354026fa5f9ec"
|
||||
"Rev": "cfaf5686ec79ff8344257723b6f5ba1ae0ffeb4d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/grpc-ecosystem/go-grpc-prometheus",
|
||||
|
@ -696,35 +696,35 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/internal/timeseries",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/trace",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/websocket",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
|
|
|
@ -168,23 +168,23 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
|
|
|
@ -192,27 +192,27 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context/ctxhttp",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
|
|
|
@ -84,23 +84,23 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
|
|
|
@ -18,21 +18,21 @@
|
|||
"ImportPath": "github.com/google/gofuzz",
|
||||
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||
|
|
|
@ -22,21 +22,21 @@
|
|||
"ImportPath": "github.com/spf13/pflag",
|
||||
"Rev": "583c0c0531f06d5278b7d917446061adc344b5cd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||
|
|
|
@ -84,23 +84,23 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
|
|
|
@ -18,21 +18,21 @@
|
|||
"ImportPath": "github.com/google/gofuzz",
|
||||
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||
|
|
|
@ -268,43 +268,43 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/html",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/html/atom",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/internal/timeseries",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/trace",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/websocket",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
|
|
|
@ -18,21 +18,21 @@
|
|||
"ImportPath": "github.com/google/gofuzz",
|
||||
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||
|
|
|
@ -18,21 +18,21 @@
|
|||
"ImportPath": "github.com/google/gofuzz",
|
||||
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||
|
|
|
@ -18,21 +18,21 @@
|
|||
"ImportPath": "github.com/google/gofuzz",
|
||||
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||
|
|
|
@ -18,21 +18,21 @@
|
|||
"ImportPath": "github.com/google/gofuzz",
|
||||
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||
|
|
|
@ -88,23 +88,23 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
|
|
|
@ -84,23 +84,23 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
|
|
|
@ -248,35 +248,35 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/internal/timeseries",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/trace",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/websocket",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
|
|
|
@ -160,23 +160,23 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
|
|
|
@ -96,23 +96,23 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/idna",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
||||
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
|
|
|
@ -69,7 +69,6 @@ var (
|
|||
func init() {
|
||||
cmdSave.Flag.BoolVar(&saveR, "r", false, "rewrite import paths")
|
||||
cmdSave.Flag.BoolVar(&saveT, "t", false, "save test files")
|
||||
|
||||
}
|
||||
|
||||
func runSave(cmd *Command, args []string) {
|
||||
|
|
|
@ -400,10 +400,13 @@ filegroup(
|
|||
"//vendor/golang.org/x/lint:all-srcs",
|
||||
"//vendor/golang.org/x/net/context:all-srcs",
|
||||
"//vendor/golang.org/x/net/html:all-srcs",
|
||||
"//vendor/golang.org/x/net/http/httpguts:all-srcs",
|
||||
"//vendor/golang.org/x/net/http2:all-srcs",
|
||||
"//vendor/golang.org/x/net/idna:all-srcs",
|
||||
"//vendor/golang.org/x/net/internal/nettest:all-srcs",
|
||||
"//vendor/golang.org/x/net/internal/socks:all-srcs",
|
||||
"//vendor/golang.org/x/net/internal/sockstest:all-srcs",
|
||||
"//vendor/golang.org/x/net/internal/timeseries:all-srcs",
|
||||
"//vendor/golang.org/x/net/lex/httplex:all-srcs",
|
||||
"//vendor/golang.org/x/net/proxy:all-srcs",
|
||||
"//vendor/golang.org/x/net/trace:all-srcs",
|
||||
"//vendor/golang.org/x/net/websocket:all-srcs",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.8.x
|
||||
- 1.11.x
|
||||
env:
|
||||
- DEP_VERSION="0.3.2"
|
||||
|
||||
|
@ -15,7 +15,6 @@ install:
|
|||
- dep ensure
|
||||
|
||||
script:
|
||||
- make checkdocs
|
||||
- make test
|
||||
|
||||
after_success:
|
||||
|
|
|
@ -1,166 +0,0 @@
|
|||
# grpc_middleware
|
||||
`import "github.com/grpc-ecosystem/go-grpc-middleware"`
|
||||
|
||||
* [Overview](#pkg-overview)
|
||||
* [Imported Packages](#pkg-imports)
|
||||
* [Index](#pkg-index)
|
||||
|
||||
## <a name="pkg-overview">Overview</a>
|
||||
`grpc_middleware` is a collection of gRPC middleware packages: interceptors, helpers and tools.
|
||||
|
||||
### Middleware
|
||||
gRPC is a fantastic RPC middleware, which sees a lot of adoption in the Golang world. However, the
|
||||
upstream gRPC codebase is relatively bare bones.
|
||||
|
||||
This package, and most of its child packages provides commonly needed middleware for gRPC:
|
||||
client-side interceptors for retires, server-side interceptors for input validation and auth,
|
||||
functions for chaining said interceptors, metadata convenience methods and more.
|
||||
|
||||
### Chaining
|
||||
By default, gRPC doesn't allow one to have more than one interceptor either on the client nor on
|
||||
the server side. `grpc_middleware` provides convenient chaining methods
|
||||
|
||||
Simple way of turning a multiple interceptors into a single interceptor. Here's an example for
|
||||
server chaining:
|
||||
|
||||
myServer := grpc.NewServer(
|
||||
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(loggingStream, monitoringStream, authStream)),
|
||||
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(loggingUnary, monitoringUnary, authUnary),
|
||||
)
|
||||
|
||||
These interceptors will be executed from left to right: logging, monitoring and auth.
|
||||
|
||||
Here's an example for client side chaining:
|
||||
|
||||
clientConn, err = grpc.Dial(
|
||||
address,
|
||||
grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(monitoringClientUnary, retryUnary)),
|
||||
grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient(monitoringClientStream, retryStream)),
|
||||
)
|
||||
client = pb_testproto.NewTestServiceClient(clientConn)
|
||||
resp, err := client.PingEmpty(s.ctx, &myservice.Request{Msg: "hello"})
|
||||
|
||||
These interceptors will be executed from left to right: monitoring and then retry logic.
|
||||
|
||||
The retry interceptor will call every interceptor that follows it whenever when a retry happens.
|
||||
|
||||
### Writing Your Own
|
||||
Implementing your own interceptor is pretty trivial: there are interfaces for that. But the interesting
|
||||
bit exposing common data to handlers (and other middleware), similarly to HTTP Middleware design.
|
||||
For example, you may want to pass the identity of the caller from the auth interceptor all the way
|
||||
to the handling function.
|
||||
|
||||
For example, a client side interceptor example for auth looks like:
|
||||
|
||||
func FakeAuthUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
newCtx := context.WithValue(ctx, "user_id", "john@example.com")
|
||||
return handler(newCtx, req)
|
||||
}
|
||||
|
||||
Unfortunately, it's not as easy for streaming RPCs. These have the `context.Context` embedded within
|
||||
the `grpc.ServerStream` object. To pass values through context, a wrapper (`WrappedServerStream`) is
|
||||
needed. For example:
|
||||
|
||||
func FakeAuthStreamingInterceptor(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
newStream := grpc_middleware.WrapServerStream(stream)
|
||||
newStream.WrappedContext = context.WithValue(ctx, "user_id", "john@example.com")
|
||||
return handler(srv, stream)
|
||||
}
|
||||
|
||||
## <a name="pkg-imports">Imported Packages</a>
|
||||
|
||||
- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context)
|
||||
- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc)
|
||||
|
||||
## <a name="pkg-index">Index</a>
|
||||
* [func ChainStreamClient(interceptors ...grpc.StreamClientInterceptor) grpc.StreamClientInterceptor](#ChainStreamClient)
|
||||
* [func ChainStreamServer(interceptors ...grpc.StreamServerInterceptor) grpc.StreamServerInterceptor](#ChainStreamServer)
|
||||
* [func ChainUnaryClient(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor](#ChainUnaryClient)
|
||||
* [func ChainUnaryServer(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor](#ChainUnaryServer)
|
||||
* [func WithStreamServerChain(interceptors ...grpc.StreamServerInterceptor) grpc.ServerOption](#WithStreamServerChain)
|
||||
* [func WithUnaryServerChain(interceptors ...grpc.UnaryServerInterceptor) grpc.ServerOption](#WithUnaryServerChain)
|
||||
* [type WrappedServerStream](#WrappedServerStream)
|
||||
* [func WrapServerStream(stream grpc.ServerStream) \*WrappedServerStream](#WrapServerStream)
|
||||
* [func (w \*WrappedServerStream) Context() context.Context](#WrappedServerStream.Context)
|
||||
|
||||
#### <a name="pkg-files">Package files</a>
|
||||
[chain.go](./chain.go) [doc.go](./doc.go) [wrappers.go](./wrappers.go)
|
||||
|
||||
## <a name="ChainStreamClient">func</a> [ChainStreamClient](./chain.go#L136)
|
||||
``` go
|
||||
func ChainStreamClient(interceptors ...grpc.StreamClientInterceptor) grpc.StreamClientInterceptor
|
||||
```
|
||||
ChainStreamClient creates a single interceptor out of a chain of many interceptors.
|
||||
|
||||
Execution is done in left-to-right order, including passing of context.
|
||||
For example ChainStreamClient(one, two, three) will execute one before two before three.
|
||||
|
||||
## <a name="ChainStreamServer">func</a> [ChainStreamServer](./chain.go#L58)
|
||||
``` go
|
||||
func ChainStreamServer(interceptors ...grpc.StreamServerInterceptor) grpc.StreamServerInterceptor
|
||||
```
|
||||
ChainStreamServer creates a single interceptor out of a chain of many interceptors.
|
||||
|
||||
Execution is done in left-to-right order, including passing of context.
|
||||
For example ChainUnaryServer(one, two, three) will execute one before two before three.
|
||||
If you want to pass context between interceptors, use WrapServerStream.
|
||||
|
||||
## <a name="ChainUnaryClient">func</a> [ChainUnaryClient](./chain.go#L97)
|
||||
``` go
|
||||
func ChainUnaryClient(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor
|
||||
```
|
||||
ChainUnaryClient creates a single interceptor out of a chain of many interceptors.
|
||||
|
||||
Execution is done in left-to-right order, including passing of context.
|
||||
For example ChainUnaryClient(one, two, three) will execute one before two before three.
|
||||
|
||||
## <a name="ChainUnaryServer">func</a> [ChainUnaryServer](./chain.go#L18)
|
||||
``` go
|
||||
func ChainUnaryServer(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor
|
||||
```
|
||||
ChainUnaryServer creates a single interceptor out of a chain of many interceptors.
|
||||
|
||||
Execution is done in left-to-right order, including passing of context.
|
||||
For example ChainUnaryServer(one, two, three) will execute one before two before three, and three
|
||||
will see context changes of one and two.
|
||||
|
||||
## <a name="WithStreamServerChain">func</a> [WithStreamServerChain](./chain.go#L181)
|
||||
``` go
|
||||
func WithStreamServerChain(interceptors ...grpc.StreamServerInterceptor) grpc.ServerOption
|
||||
```
|
||||
WithStreamServerChain is a grpc.Server config option that accepts multiple stream interceptors.
|
||||
Basically syntactic sugar.
|
||||
|
||||
## <a name="WithUnaryServerChain">func</a> [WithUnaryServerChain](./chain.go#L175)
|
||||
``` go
|
||||
func WithUnaryServerChain(interceptors ...grpc.UnaryServerInterceptor) grpc.ServerOption
|
||||
```
|
||||
Chain creates a single interceptor out of a chain of many interceptors.
|
||||
|
||||
WithUnaryServerChain is a grpc.Server config option that accepts multiple unary interceptors.
|
||||
Basically syntactic sugar.
|
||||
|
||||
## <a name="WrappedServerStream">type</a> [WrappedServerStream](./wrappers.go#L12-L16)
|
||||
``` go
|
||||
type WrappedServerStream struct {
|
||||
grpc.ServerStream
|
||||
// WrappedContext is the wrapper's own Context. You can assign it.
|
||||
WrappedContext context.Context
|
||||
}
|
||||
```
|
||||
WrappedServerStream is a thin wrapper around grpc.ServerStream that allows modifying context.
|
||||
|
||||
### <a name="WrapServerStream">func</a> [WrapServerStream](./wrappers.go#L24)
|
||||
``` go
|
||||
func WrapServerStream(stream grpc.ServerStream) *WrappedServerStream
|
||||
```
|
||||
WrapServerStream returns a ServerStream that has the ability to overwrite context.
|
||||
|
||||
### <a name="WrappedServerStream.Context">func</a> (\*WrappedServerStream) [Context](./wrappers.go#L19)
|
||||
``` go
|
||||
func (w *WrappedServerStream) Context() context.Context
|
||||
```
|
||||
Context returns the wrapper's WrappedContext, overwriting the nested grpc.ServerStream.Context()
|
||||
|
||||
- - -
|
||||
Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
|
|
@ -4,14 +4,14 @@
|
|||
[[projects]]
|
||||
name = "cloud.google.com/go"
|
||||
packages = ["compute/metadata"]
|
||||
revision = "2d3a6656c17a60b0815b7e06ab0be04eacb6e613"
|
||||
version = "v0.16.0"
|
||||
revision = "28a4bc8c44b3acbcc482cff0cdf7de29a4688b61"
|
||||
version = "v0.35.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
|
||||
version = "v1.1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gogo/protobuf"
|
||||
|
@ -23,7 +23,13 @@
|
|||
branch = "master"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = ["jsonpb","proto","ptypes","ptypes/any","ptypes/duration","ptypes/struct","ptypes/timestamp"]
|
||||
revision = "1e59b77b52bf8e4b449a57e6f79f21226d571845"
|
||||
revision = "347cf4a86c1cb8d262994d8ef5924d4576c5b331"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/konsorten/go-windows-terminal-sequences"
|
||||
packages = ["."]
|
||||
revision = "5c8c8bd35d3832f5d134ae1e1e375b69a4d25242"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/opentracing/opentracing-go"
|
||||
|
@ -40,20 +46,20 @@
|
|||
[[projects]]
|
||||
name = "github.com/sirupsen/logrus"
|
||||
packages = ["."]
|
||||
revision = "f006c2ac4710855cf0f916dd6b77acf6b048dc6e"
|
||||
version = "v1.0.3"
|
||||
revision = "e1e72e9de974bd926e5c56f83753fba2df402ce5"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = ["assert","require","suite"]
|
||||
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
|
||||
version = "v1.1.4"
|
||||
revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
name = "go.uber.org/atomic"
|
||||
packages = ["."]
|
||||
revision = "8474b86a5a6f79c443ce4b2992817ff32cf208b8"
|
||||
version = "v1.3.1"
|
||||
revision = "1ea20fb1cbb1cc08cbd0d913a96dead89aa18289"
|
||||
version = "v1.3.2"
|
||||
|
||||
[[projects]]
|
||||
name = "go.uber.org/multierr"
|
||||
|
@ -64,56 +70,56 @@
|
|||
[[projects]]
|
||||
name = "go.uber.org/zap"
|
||||
packages = [".","buffer","internal/bufferpool","internal/color","internal/exit","zapcore"]
|
||||
revision = "35aad584952c3e7020db7b839f6b102de6271f89"
|
||||
version = "v1.7.1"
|
||||
revision = "ff33455a0e382e8a81d14dd7c922020b6b5e7982"
|
||||
version = "v1.9.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["ssh/terminal"]
|
||||
revision = "94eea52f7b742c7cbe0b03b22f0c4c8631ece122"
|
||||
revision = "b01c7a72566457eb1420261cdafef86638fc3861"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = ["context","context/ctxhttp","http2","http2/hpack","idna","internal/timeseries","lex/httplex","trace"]
|
||||
revision = "a8b9294777976932365dabb6640cf1468d95c70f"
|
||||
packages = ["context","context/ctxhttp","http/httpguts","http2","http2/hpack","idna","internal/timeseries","trace"]
|
||||
revision = "d26f9f9a57f3fab6a695bec0d84433c2c50f8bbf"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/oauth2"
|
||||
packages = [".","google","internal","jws","jwt"]
|
||||
revision = "f95fa95eaa936d9d87489b15d1d18b97c1ba9c28"
|
||||
revision = "99b60b757ec124ebb7d6b7e97f153b19c10ce163"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix","windows"]
|
||||
revision = "13fcbd661c8ececa8807a29b48407d674b1d8ed8"
|
||||
revision = "302c3dd5f1cc82baae8e44d9c3178e89b6e2b345"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/text"
|
||||
packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
|
||||
revision = "75cc3cad82b5f47d3fb229ddda8c5167da14f294"
|
||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
name = "google.golang.org/appengine"
|
||||
packages = [".","internal","internal/app_identity","internal/base","internal/datastore","internal/log","internal/modules","internal/remote_api","internal/urlfetch","urlfetch"]
|
||||
revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a"
|
||||
version = "v1.0.0"
|
||||
revision = "e9657d882bb81064595ca3b56cbe2546bbabf7b1"
|
||||
version = "v1.4.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "google.golang.org/genproto"
|
||||
packages = ["googleapis/rpc/status"]
|
||||
revision = "7f0da29060c682909f650ad8ed4e515bd74fa12a"
|
||||
revision = "8ac453e89fca495c0d17f98932642f392e2a11f3"
|
||||
|
||||
[[projects]]
|
||||
name = "google.golang.org/grpc"
|
||||
packages = [".","balancer","balancer/roundrobin","codes","connectivity","credentials","credentials/oauth","encoding","grpclb/grpc_lb_v1/messages","grpclog","internal","keepalive","metadata","naming","peer","resolver","resolver/dns","resolver/passthrough","stats","status","tap","transport"]
|
||||
revision = "5a9f7b402fe85096d2e1d0383435ee1876e863d0"
|
||||
version = "v1.8.0"
|
||||
packages = [".","balancer","balancer/base","balancer/roundrobin","binarylog/grpc_binarylog_v1","codes","connectivity","credentials","credentials/internal","credentials/oauth","encoding","encoding/proto","grpclog","internal","internal/backoff","internal/binarylog","internal/channelz","internal/envconfig","internal/grpcrand","internal/grpcsync","internal/syscall","internal/transport","keepalive","metadata","naming","peer","resolver","resolver/dns","resolver/passthrough","stats","status","tap"]
|
||||
revision = "a02b0774206b209466313a0b525d2c738fe407eb"
|
||||
version = "v1.18.0"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
[gRPC Go](https://github.com/grpc/grpc-go) Middleware: interceptors, helpers, utilities.
|
||||
|
||||
**Important** The repo recently moved to `github.com/grpc-ecosystem/go-grpc-middleware`, please update your import paths.
|
||||
|
||||
## Middleware
|
||||
|
||||
[gRPC Go](https://github.com/grpc/grpc-go) recently acquired support for
|
||||
|
@ -24,7 +22,7 @@ These are generic building blocks that make it easy to build multiple microservi
|
|||
The purpose of this repository is to act as a go-to point for such reusable functionality. It contains
|
||||
some of them itself, but also will link to useful external repos.
|
||||
|
||||
`grpc_middleware` itself provides support for chaining interceptors. See [Documentation](DOC.md), but here's an example:
|
||||
`grpc_middleware` itself provides support for chaining interceptors, here's an example:
|
||||
|
||||
```go
|
||||
import "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
SHELL="/bin/bash"
|
||||
SHELL=/bin/bash
|
||||
|
||||
GOFILES_NOVENDOR = $(shell go list ./... | grep -v /vendor/)
|
||||
|
||||
all: vet fmt docs test
|
||||
|
||||
docs:
|
||||
./scripts/docs.sh generate
|
||||
|
||||
checkdocs:
|
||||
./scripts/docs.sh check
|
||||
all: vet fmt test
|
||||
|
||||
fmt:
|
||||
go fmt $(GOFILES_NOVENDOR)
|
||||
|
@ -19,4 +13,4 @@ vet:
|
|||
test: vet
|
||||
./scripts/test_all.sh
|
||||
|
||||
.PHONY: all docs validate test
|
||||
.PHONY: all test
|
||||
|
|
|
@ -306,7 +306,7 @@ func (t *table) push(i uint32, depth int) bool {
|
|||
|
||||
// The lists of element names and attribute keys were taken from
|
||||
// https://html.spec.whatwg.org/multipage/indices.html#index
|
||||
// as of the "HTML Living Standard - Last Updated 18 September 2017" version.
|
||||
// as of the "HTML Living Standard - Last Updated 16 April 2018" version.
|
||||
|
||||
// "command", "keygen" and "menuitem" have been removed from the spec,
|
||||
// but are kept here for backwards compatibility.
|
||||
|
@ -665,6 +665,7 @@ var eventHandlers = []string{
|
|||
|
||||
// extra are ad-hoc values not covered by any of the lists above.
|
||||
var extra = []string{
|
||||
"acronym",
|
||||
"align",
|
||||
"annotation",
|
||||
"annotation-xml",
|
||||
|
@ -700,6 +701,8 @@ var extra = []string{
|
|||
"plaintext",
|
||||
"prompt",
|
||||
"public",
|
||||
"rb",
|
||||
"rtc",
|
||||
"spacer",
|
||||
"strike",
|
||||
"svg",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,7 @@
|
|||
|
||||
package html
|
||||
|
||||
// Section 12.2.3.2 of the HTML5 specification says "The following elements
|
||||
// Section 12.2.4.2 of the HTML5 specification says "The following elements
|
||||
// have varying levels of special parsing rules".
|
||||
// https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements
|
||||
var isSpecialElementMap = map[string]bool{
|
||||
|
|
|
@ -67,7 +67,7 @@ func mathMLTextIntegrationPoint(n *Node) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.5.
|
||||
// Section 12.2.6.5.
|
||||
var breakout = map[string]bool{
|
||||
"b": true,
|
||||
"big": true,
|
||||
|
@ -115,7 +115,7 @@ var breakout = map[string]bool{
|
|||
"var": true,
|
||||
}
|
||||
|
||||
// Section 12.2.5.5.
|
||||
// Section 12.2.6.5.
|
||||
var svgTagNameAdjustments = map[string]string{
|
||||
"altglyph": "altGlyph",
|
||||
"altglyphdef": "altGlyphDef",
|
||||
|
@ -155,7 +155,7 @@ var svgTagNameAdjustments = map[string]string{
|
|||
"textpath": "textPath",
|
||||
}
|
||||
|
||||
// Section 12.2.5.1
|
||||
// Section 12.2.6.1
|
||||
var mathMLAttributeAdjustments = map[string]string{
|
||||
"definitionurl": "definitionURL",
|
||||
}
|
||||
|
|
|
@ -21,9 +21,10 @@ const (
|
|||
scopeMarkerNode
|
||||
)
|
||||
|
||||
// Section 12.2.3.3 says "scope markers are inserted when entering applet
|
||||
// elements, buttons, object elements, marquees, table cells, and table
|
||||
// captions, and are used to prevent formatting from 'leaking'".
|
||||
// Section 12.2.4.3 says "The markers are inserted when entering applet,
|
||||
// object, marquee, template, td, th, and caption elements, and are used
|
||||
// to prevent formatting from "leaking" into applet, object, marquee,
|
||||
// template, td, th, and caption elements".
|
||||
var scopeMarker = Node{Type: scopeMarkerNode}
|
||||
|
||||
// A Node consists of a NodeType and some Data (tag name for element nodes,
|
||||
|
@ -173,6 +174,16 @@ func (s *nodeStack) index(n *Node) int {
|
|||
return -1
|
||||
}
|
||||
|
||||
// contains returns whether a is within s.
|
||||
func (s *nodeStack) contains(a atom.Atom) bool {
|
||||
for _, n := range *s {
|
||||
if n.DataAtom == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// insert inserts a node at the given index.
|
||||
func (s *nodeStack) insert(i int, n *Node) {
|
||||
(*s) = append(*s, nil)
|
||||
|
@ -191,3 +202,19 @@ func (s *nodeStack) remove(n *Node) {
|
|||
(*s)[j] = nil
|
||||
*s = (*s)[:j]
|
||||
}
|
||||
|
||||
type insertionModeStack []insertionMode
|
||||
|
||||
func (s *insertionModeStack) pop() (im insertionMode) {
|
||||
i := len(*s)
|
||||
im = (*s)[i-1]
|
||||
*s = (*s)[:i-1]
|
||||
return im
|
||||
}
|
||||
|
||||
func (s *insertionModeStack) top() insertionMode {
|
||||
if i := len(*s); i > 0 {
|
||||
return (*s)[i-1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -25,20 +25,22 @@ type parser struct {
|
|||
hasSelfClosingToken bool
|
||||
// doc is the document root element.
|
||||
doc *Node
|
||||
// The stack of open elements (section 12.2.3.2) and active formatting
|
||||
// elements (section 12.2.3.3).
|
||||
// The stack of open elements (section 12.2.4.2) and active formatting
|
||||
// elements (section 12.2.4.3).
|
||||
oe, afe nodeStack
|
||||
// Element pointers (section 12.2.3.4).
|
||||
// Element pointers (section 12.2.4.4).
|
||||
head, form *Node
|
||||
// Other parsing state flags (section 12.2.3.5).
|
||||
// Other parsing state flags (section 12.2.4.5).
|
||||
scripting, framesetOK bool
|
||||
// The stack of template insertion modes
|
||||
templateStack insertionModeStack
|
||||
// im is the current insertion mode.
|
||||
im insertionMode
|
||||
// originalIM is the insertion mode to go back to after completing a text
|
||||
// or inTableText insertion mode.
|
||||
originalIM insertionMode
|
||||
// fosterParenting is whether new elements should be inserted according to
|
||||
// the foster parenting rules (section 12.2.5.3).
|
||||
// the foster parenting rules (section 12.2.6.1).
|
||||
fosterParenting bool
|
||||
// quirks is whether the parser is operating in "quirks mode."
|
||||
quirks bool
|
||||
|
@ -56,7 +58,7 @@ func (p *parser) top() *Node {
|
|||
return p.doc
|
||||
}
|
||||
|
||||
// Stop tags for use in popUntil. These come from section 12.2.3.2.
|
||||
// Stop tags for use in popUntil. These come from section 12.2.4.2.
|
||||
var (
|
||||
defaultScopeStopTags = map[string][]a.Atom{
|
||||
"": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template},
|
||||
|
@ -79,7 +81,7 @@ const (
|
|||
|
||||
// popUntil pops the stack of open elements at the highest element whose tag
|
||||
// is in matchTags, provided there is no higher element in the scope's stop
|
||||
// tags (as defined in section 12.2.3.2). It returns whether or not there was
|
||||
// tags (as defined in section 12.2.4.2). It returns whether or not there was
|
||||
// such an element. If there was not, popUntil leaves the stack unchanged.
|
||||
//
|
||||
// For example, the set of stop tags for table scope is: "html", "table". If
|
||||
|
@ -126,7 +128,7 @@ func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int {
|
|||
return -1
|
||||
}
|
||||
case tableScope:
|
||||
if tagAtom == a.Html || tagAtom == a.Table {
|
||||
if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template {
|
||||
return -1
|
||||
}
|
||||
case selectScope:
|
||||
|
@ -162,17 +164,17 @@ func (p *parser) clearStackToContext(s scope) {
|
|||
tagAtom := p.oe[i].DataAtom
|
||||
switch s {
|
||||
case tableScope:
|
||||
if tagAtom == a.Html || tagAtom == a.Table {
|
||||
if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template {
|
||||
p.oe = p.oe[:i+1]
|
||||
return
|
||||
}
|
||||
case tableRowScope:
|
||||
if tagAtom == a.Html || tagAtom == a.Tr {
|
||||
if tagAtom == a.Html || tagAtom == a.Tr || tagAtom == a.Template {
|
||||
p.oe = p.oe[:i+1]
|
||||
return
|
||||
}
|
||||
case tableBodyScope:
|
||||
if tagAtom == a.Html || tagAtom == a.Tbody || tagAtom == a.Tfoot || tagAtom == a.Thead {
|
||||
if tagAtom == a.Html || tagAtom == a.Tbody || tagAtom == a.Tfoot || tagAtom == a.Thead || tagAtom == a.Template {
|
||||
p.oe = p.oe[:i+1]
|
||||
return
|
||||
}
|
||||
|
@ -183,7 +185,7 @@ func (p *parser) clearStackToContext(s scope) {
|
|||
}
|
||||
|
||||
// generateImpliedEndTags pops nodes off the stack of open elements as long as
|
||||
// the top node has a tag name of dd, dt, li, option, optgroup, p, rp, or rt.
|
||||
// the top node has a tag name of dd, dt, li, optgroup, option, p, rb, rp, rt or rtc.
|
||||
// If exceptions are specified, nodes with that name will not be popped off.
|
||||
func (p *parser) generateImpliedEndTags(exceptions ...string) {
|
||||
var i int
|
||||
|
@ -192,7 +194,7 @@ loop:
|
|||
n := p.oe[i]
|
||||
if n.Type == ElementNode {
|
||||
switch n.DataAtom {
|
||||
case a.Dd, a.Dt, a.Li, a.Option, a.Optgroup, a.P, a.Rp, a.Rt:
|
||||
case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc:
|
||||
for _, except := range exceptions {
|
||||
if n.Data == except {
|
||||
break loop
|
||||
|
@ -207,6 +209,27 @@ loop:
|
|||
p.oe = p.oe[:i+1]
|
||||
}
|
||||
|
||||
// generateAllImpliedEndTags pops nodes off the stack of open elements as long as
|
||||
// the top node has a tag name of caption, colgroup, dd, div, dt, li, optgroup, option, p, rb,
|
||||
// rp, rt, rtc, span, tbody, td, tfoot, th, thead or tr.
|
||||
func (p *parser) generateAllImpliedEndTags() {
|
||||
var i int
|
||||
for i = len(p.oe) - 1; i >= 0; i-- {
|
||||
n := p.oe[i]
|
||||
if n.Type == ElementNode {
|
||||
switch n.DataAtom {
|
||||
// TODO: remove this divergence from the HTML5 spec
|
||||
case a.Caption, a.Colgroup, a.Dd, a.Div, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb,
|
||||
a.Rp, a.Rt, a.Rtc, a.Span, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
p.oe = p.oe[:i+1]
|
||||
}
|
||||
|
||||
// addChild adds a child node n to the top element, and pushes n onto the stack
|
||||
// of open elements if it is an element node.
|
||||
func (p *parser) addChild(n *Node) {
|
||||
|
@ -234,9 +257,9 @@ func (p *parser) shouldFosterParent() bool {
|
|||
}
|
||||
|
||||
// fosterParent adds a child node according to the foster parenting rules.
|
||||
// Section 12.2.5.3, "foster parenting".
|
||||
// Section 12.2.6.1, "foster parenting".
|
||||
func (p *parser) fosterParent(n *Node) {
|
||||
var table, parent, prev *Node
|
||||
var table, parent, prev, template *Node
|
||||
var i int
|
||||
for i = len(p.oe) - 1; i >= 0; i-- {
|
||||
if p.oe[i].DataAtom == a.Table {
|
||||
|
@ -245,6 +268,19 @@ func (p *parser) fosterParent(n *Node) {
|
|||
}
|
||||
}
|
||||
|
||||
var j int
|
||||
for j = len(p.oe) - 1; j >= 0; j-- {
|
||||
if p.oe[j].DataAtom == a.Template {
|
||||
template = p.oe[j]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if template != nil && (table == nil || j < i) {
|
||||
template.AppendChild(n)
|
||||
return
|
||||
}
|
||||
|
||||
if table == nil {
|
||||
// The foster parent is the html element.
|
||||
parent = p.oe[0]
|
||||
|
@ -304,7 +340,7 @@ func (p *parser) addElement() {
|
|||
})
|
||||
}
|
||||
|
||||
// Section 12.2.3.3.
|
||||
// Section 12.2.4.3.
|
||||
func (p *parser) addFormattingElement() {
|
||||
tagAtom, attr := p.tok.DataAtom, p.tok.Attr
|
||||
p.addElement()
|
||||
|
@ -351,7 +387,7 @@ findIdenticalElements:
|
|||
p.afe = append(p.afe, p.top())
|
||||
}
|
||||
|
||||
// Section 12.2.3.3.
|
||||
// Section 12.2.4.3.
|
||||
func (p *parser) clearActiveFormattingElements() {
|
||||
for {
|
||||
n := p.afe.pop()
|
||||
|
@ -361,7 +397,7 @@ func (p *parser) clearActiveFormattingElements() {
|
|||
}
|
||||
}
|
||||
|
||||
// Section 12.2.3.3.
|
||||
// Section 12.2.4.3.
|
||||
func (p *parser) reconstructActiveFormattingElements() {
|
||||
n := p.afe.top()
|
||||
if n == nil {
|
||||
|
@ -390,12 +426,12 @@ func (p *parser) reconstructActiveFormattingElements() {
|
|||
}
|
||||
}
|
||||
|
||||
// Section 12.2.4.
|
||||
// Section 12.2.5.
|
||||
func (p *parser) acknowledgeSelfClosingTag() {
|
||||
p.hasSelfClosingToken = false
|
||||
}
|
||||
|
||||
// An insertion mode (section 12.2.3.1) is the state transition function from
|
||||
// An insertion mode (section 12.2.4.1) is the state transition function from
|
||||
// a particular state in the HTML5 parser's state machine. It updates the
|
||||
// parser's fields depending on parser.tok (where ErrorToken means EOF).
|
||||
// It returns whether the token was consumed.
|
||||
|
@ -403,7 +439,7 @@ type insertionMode func(*parser) bool
|
|||
|
||||
// setOriginalIM sets the insertion mode to return to after completing a text or
|
||||
// inTableText insertion mode.
|
||||
// Section 12.2.3.1, "using the rules for".
|
||||
// Section 12.2.4.1, "using the rules for".
|
||||
func (p *parser) setOriginalIM() {
|
||||
if p.originalIM != nil {
|
||||
panic("html: bad parser state: originalIM was set twice")
|
||||
|
@ -411,18 +447,38 @@ func (p *parser) setOriginalIM() {
|
|||
p.originalIM = p.im
|
||||
}
|
||||
|
||||
// Section 12.2.3.1, "reset the insertion mode".
|
||||
// Section 12.2.4.1, "reset the insertion mode".
|
||||
func (p *parser) resetInsertionMode() {
|
||||
for i := len(p.oe) - 1; i >= 0; i-- {
|
||||
n := p.oe[i]
|
||||
if i == 0 && p.context != nil {
|
||||
last := i == 0
|
||||
if last && p.context != nil {
|
||||
n = p.context
|
||||
}
|
||||
|
||||
switch n.DataAtom {
|
||||
case a.Select:
|
||||
if !last {
|
||||
for ancestor, first := n, p.oe[0]; ancestor != first; {
|
||||
if ancestor == first {
|
||||
break
|
||||
}
|
||||
ancestor = p.oe[p.oe.index(ancestor)-1]
|
||||
switch ancestor.DataAtom {
|
||||
case a.Template:
|
||||
p.im = inSelectIM
|
||||
return
|
||||
case a.Table:
|
||||
p.im = inSelectInTableIM
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
p.im = inSelectIM
|
||||
case a.Td, a.Th:
|
||||
// TODO: remove this divergence from the HTML5 spec.
|
||||
//
|
||||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
|
||||
p.im = inCellIM
|
||||
case a.Tr:
|
||||
p.im = inRowIM
|
||||
|
@ -434,25 +490,37 @@ func (p *parser) resetInsertionMode() {
|
|||
p.im = inColumnGroupIM
|
||||
case a.Table:
|
||||
p.im = inTableIM
|
||||
case a.Template:
|
||||
p.im = p.templateStack.top()
|
||||
case a.Head:
|
||||
p.im = inBodyIM
|
||||
// TODO: remove this divergence from the HTML5 spec.
|
||||
//
|
||||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
|
||||
p.im = inHeadIM
|
||||
case a.Body:
|
||||
p.im = inBodyIM
|
||||
case a.Frameset:
|
||||
p.im = inFramesetIM
|
||||
case a.Html:
|
||||
if p.head == nil {
|
||||
p.im = beforeHeadIM
|
||||
} else {
|
||||
p.im = afterHeadIM
|
||||
}
|
||||
default:
|
||||
if last {
|
||||
p.im = inBodyIM
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
p.im = inBodyIM
|
||||
}
|
||||
|
||||
const whitespace = " \t\r\n\f"
|
||||
|
||||
// Section 12.2.5.4.1.
|
||||
// Section 12.2.6.4.1.
|
||||
func initialIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
|
@ -479,7 +547,7 @@ func initialIM(p *parser) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.2.
|
||||
// Section 12.2.6.4.2.
|
||||
func beforeHTMLIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case DoctypeToken:
|
||||
|
@ -517,7 +585,7 @@ func beforeHTMLIM(p *parser) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.3.
|
||||
// Section 12.2.6.4.3.
|
||||
func beforeHeadIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
|
@ -560,7 +628,7 @@ func beforeHeadIM(p *parser) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.4.
|
||||
// Section 12.2.6.4.4.
|
||||
func inHeadIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
|
@ -590,19 +658,33 @@ func inHeadIM(p *parser) bool {
|
|||
case a.Head:
|
||||
// Ignore the token.
|
||||
return true
|
||||
case a.Template:
|
||||
p.addElement()
|
||||
p.afe = append(p.afe, &scopeMarker)
|
||||
p.framesetOK = false
|
||||
p.im = inTemplateIM
|
||||
p.templateStack = append(p.templateStack, inTemplateIM)
|
||||
return true
|
||||
}
|
||||
case EndTagToken:
|
||||
switch p.tok.DataAtom {
|
||||
case a.Head:
|
||||
n := p.oe.pop()
|
||||
if n.DataAtom != a.Head {
|
||||
panic("html: bad parser state: <head> element not found, in the in-head insertion mode")
|
||||
}
|
||||
p.oe.pop()
|
||||
p.im = afterHeadIM
|
||||
return true
|
||||
case a.Body, a.Html, a.Br:
|
||||
p.parseImpliedToken(EndTagToken, a.Head, a.Head.String())
|
||||
return false
|
||||
case a.Template:
|
||||
if !p.oe.contains(a.Template) {
|
||||
return true
|
||||
}
|
||||
p.generateAllImpliedEndTags()
|
||||
p.popUntil(defaultScope, a.Template)
|
||||
p.clearActiveFormattingElements()
|
||||
p.templateStack.pop()
|
||||
p.resetInsertionMode()
|
||||
return true
|
||||
default:
|
||||
// Ignore the token.
|
||||
return true
|
||||
|
@ -622,7 +704,7 @@ func inHeadIM(p *parser) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.6.
|
||||
// Section 12.2.6.4.6.
|
||||
func afterHeadIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
|
@ -648,7 +730,7 @@ func afterHeadIM(p *parser) bool {
|
|||
p.addElement()
|
||||
p.im = inFramesetIM
|
||||
return true
|
||||
case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Title:
|
||||
case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
|
||||
p.oe = append(p.oe, p.head)
|
||||
defer p.oe.remove(p.head)
|
||||
return inHeadIM(p)
|
||||
|
@ -660,6 +742,8 @@ func afterHeadIM(p *parser) bool {
|
|||
switch p.tok.DataAtom {
|
||||
case a.Body, a.Html, a.Br:
|
||||
// Drop down to creating an implied <body> tag.
|
||||
case a.Template:
|
||||
return inHeadIM(p)
|
||||
default:
|
||||
// Ignore the token.
|
||||
return true
|
||||
|
@ -697,7 +781,7 @@ func copyAttributes(dst *Node, src Token) {
|
|||
}
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.7.
|
||||
// Section 12.2.6.4.7.
|
||||
func inBodyIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
|
@ -727,10 +811,16 @@ func inBodyIM(p *parser) bool {
|
|||
case StartTagToken:
|
||||
switch p.tok.DataAtom {
|
||||
case a.Html:
|
||||
if p.oe.contains(a.Template) {
|
||||
return true
|
||||
}
|
||||
copyAttributes(p.oe[0], p.tok)
|
||||
case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Title:
|
||||
case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
|
||||
return inHeadIM(p)
|
||||
case a.Body:
|
||||
if p.oe.contains(a.Template) {
|
||||
return true
|
||||
}
|
||||
if len(p.oe) >= 2 {
|
||||
body := p.oe[1]
|
||||
if body.Type == ElementNode && body.DataAtom == a.Body {
|
||||
|
@ -767,9 +857,13 @@ func inBodyIM(p *parser) bool {
|
|||
// The newline, if any, will be dealt with by the TextToken case.
|
||||
p.framesetOK = false
|
||||
case a.Form:
|
||||
if p.form == nil {
|
||||
if p.form != nil && !p.oe.contains(a.Template) {
|
||||
// Ignore the token
|
||||
return true
|
||||
}
|
||||
p.popUntil(buttonScope, a.P)
|
||||
p.addElement()
|
||||
if !p.oe.contains(a.Template) {
|
||||
p.form = p.top()
|
||||
}
|
||||
case a.Li:
|
||||
|
@ -952,11 +1046,16 @@ func inBodyIM(p *parser) bool {
|
|||
}
|
||||
p.reconstructActiveFormattingElements()
|
||||
p.addElement()
|
||||
case a.Rp, a.Rt:
|
||||
case a.Rb, a.Rtc:
|
||||
if p.elementInScope(defaultScope, a.Ruby) {
|
||||
p.generateImpliedEndTags()
|
||||
}
|
||||
p.addElement()
|
||||
case a.Rp, a.Rt:
|
||||
if p.elementInScope(defaultScope, a.Ruby) {
|
||||
p.generateImpliedEndTags("rtc")
|
||||
}
|
||||
p.addElement()
|
||||
case a.Math, a.Svg:
|
||||
p.reconstructActiveFormattingElements()
|
||||
if p.tok.DataAtom == a.Math {
|
||||
|
@ -972,7 +1071,13 @@ func inBodyIM(p *parser) bool {
|
|||
p.acknowledgeSelfClosingTag()
|
||||
}
|
||||
return true
|
||||
case a.Caption, a.Col, a.Colgroup, a.Frame, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
|
||||
case a.Frame:
|
||||
// TODO: remove this divergence from the HTML5 spec.
|
||||
if p.oe.contains(a.Template) {
|
||||
p.addElement()
|
||||
return true
|
||||
}
|
||||
case a.Caption, a.Col, a.Colgroup, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
|
||||
// Ignore the token.
|
||||
default:
|
||||
p.reconstructActiveFormattingElements()
|
||||
|
@ -993,6 +1098,19 @@ func inBodyIM(p *parser) bool {
|
|||
case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Menu, a.Nav, a.Ol, a.Pre, a.Section, a.Summary, a.Ul:
|
||||
p.popUntil(defaultScope, p.tok.DataAtom)
|
||||
case a.Form:
|
||||
if p.oe.contains(a.Template) {
|
||||
i := p.indexOfElementInScope(defaultScope, a.Form)
|
||||
if i == -1 {
|
||||
// Ignore the token.
|
||||
return true
|
||||
}
|
||||
p.generateImpliedEndTags()
|
||||
if p.oe[i].DataAtom != a.Form {
|
||||
// Ignore the token.
|
||||
return true
|
||||
}
|
||||
p.popUntil(defaultScope, a.Form)
|
||||
} else {
|
||||
node := p.form
|
||||
p.form = nil
|
||||
i := p.indexOfElementInScope(defaultScope, a.Form)
|
||||
|
@ -1002,6 +1120,7 @@ func inBodyIM(p *parser) bool {
|
|||
}
|
||||
p.generateImpliedEndTags()
|
||||
p.oe.remove(node)
|
||||
}
|
||||
case a.P:
|
||||
if !p.elementInScope(buttonScope, a.P) {
|
||||
p.parseImpliedToken(StartTagToken, a.P, a.P.String())
|
||||
|
@ -1022,6 +1141,8 @@ func inBodyIM(p *parser) bool {
|
|||
case a.Br:
|
||||
p.tok.Type = StartTagToken
|
||||
return false
|
||||
case a.Template:
|
||||
return inHeadIM(p)
|
||||
default:
|
||||
p.inBodyEndTagOther(p.tok.DataAtom)
|
||||
}
|
||||
|
@ -1030,6 +1151,21 @@ func inBodyIM(p *parser) bool {
|
|||
Type: CommentNode,
|
||||
Data: p.tok.Data,
|
||||
})
|
||||
case ErrorToken:
|
||||
// TODO: remove this divergence from the HTML5 spec.
|
||||
if len(p.templateStack) > 0 {
|
||||
p.im = inTemplateIM
|
||||
return false
|
||||
} else {
|
||||
for _, e := range p.oe {
|
||||
switch e.DataAtom {
|
||||
case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc, a.Tbody, a.Td, a.Tfoot, a.Th,
|
||||
a.Thead, a.Tr, a.Body, a.Html:
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
|
@ -1135,6 +1271,12 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) {
|
|||
switch commonAncestor.DataAtom {
|
||||
case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
|
||||
p.fosterParent(lastNode)
|
||||
case a.Template:
|
||||
// TODO: remove namespace checking
|
||||
if commonAncestor.Namespace == "html" {
|
||||
commonAncestor = commonAncestor.LastChild
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
commonAncestor.AppendChild(lastNode)
|
||||
}
|
||||
|
@ -1160,7 +1302,7 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) {
|
|||
}
|
||||
|
||||
// inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM.
|
||||
// "Any other end tag" handling from 12.2.5.5 The rules for parsing tokens in foreign content
|
||||
// "Any other end tag" handling from 12.2.6.5 The rules for parsing tokens in foreign content
|
||||
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign
|
||||
func (p *parser) inBodyEndTagOther(tagAtom a.Atom) {
|
||||
for i := len(p.oe) - 1; i >= 0; i-- {
|
||||
|
@ -1174,7 +1316,7 @@ func (p *parser) inBodyEndTagOther(tagAtom a.Atom) {
|
|||
}
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.8.
|
||||
// Section 12.2.6.4.8.
|
||||
func textIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case ErrorToken:
|
||||
|
@ -1203,7 +1345,7 @@ func textIM(p *parser) bool {
|
|||
return p.tok.Type == EndTagToken
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.9.
|
||||
// Section 12.2.6.4.9.
|
||||
func inTableIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case ErrorToken:
|
||||
|
@ -1249,7 +1391,7 @@ func inTableIM(p *parser) bool {
|
|||
}
|
||||
// Ignore the token.
|
||||
return true
|
||||
case a.Style, a.Script:
|
||||
case a.Style, a.Script, a.Template:
|
||||
return inHeadIM(p)
|
||||
case a.Input:
|
||||
for _, t := range p.tok.Attr {
|
||||
|
@ -1261,7 +1403,7 @@ func inTableIM(p *parser) bool {
|
|||
}
|
||||
// Otherwise drop down to the default action.
|
||||
case a.Form:
|
||||
if p.form != nil {
|
||||
if p.oe.contains(a.Template) || p.form != nil {
|
||||
// Ignore the token.
|
||||
return true
|
||||
}
|
||||
|
@ -1291,6 +1433,8 @@ func inTableIM(p *parser) bool {
|
|||
case a.Body, a.Caption, a.Col, a.Colgroup, a.Html, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
|
||||
// Ignore the token.
|
||||
return true
|
||||
case a.Template:
|
||||
return inHeadIM(p)
|
||||
}
|
||||
case CommentToken:
|
||||
p.addChild(&Node{
|
||||
|
@ -1309,7 +1453,7 @@ func inTableIM(p *parser) bool {
|
|||
return inBodyIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.11.
|
||||
// Section 12.2.6.4.11.
|
||||
func inCaptionIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case StartTagToken:
|
||||
|
@ -1355,7 +1499,7 @@ func inCaptionIM(p *parser) bool {
|
|||
return inBodyIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.12.
|
||||
// Section 12.2.6.4.12.
|
||||
func inColumnGroupIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
|
@ -1386,11 +1530,13 @@ func inColumnGroupIM(p *parser) bool {
|
|||
p.oe.pop()
|
||||
p.acknowledgeSelfClosingTag()
|
||||
return true
|
||||
case a.Template:
|
||||
return inHeadIM(p)
|
||||
}
|
||||
case EndTagToken:
|
||||
switch p.tok.DataAtom {
|
||||
case a.Colgroup:
|
||||
if p.oe.top().DataAtom != a.Html {
|
||||
if p.oe.top().DataAtom == a.Colgroup {
|
||||
p.oe.pop()
|
||||
p.im = inTableIM
|
||||
}
|
||||
|
@ -1398,17 +1544,19 @@ func inColumnGroupIM(p *parser) bool {
|
|||
case a.Col:
|
||||
// Ignore the token.
|
||||
return true
|
||||
case a.Template:
|
||||
return inHeadIM(p)
|
||||
}
|
||||
}
|
||||
if p.oe.top().DataAtom != a.Html {
|
||||
if p.oe.top().DataAtom != a.Colgroup {
|
||||
return true
|
||||
}
|
||||
p.oe.pop()
|
||||
p.im = inTableIM
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.13.
|
||||
// Section 12.2.6.4.13.
|
||||
func inTableBodyIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case StartTagToken:
|
||||
|
@ -1460,7 +1608,7 @@ func inTableBodyIM(p *parser) bool {
|
|||
return inTableIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.14.
|
||||
// Section 12.2.6.4.14.
|
||||
func inRowIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case StartTagToken:
|
||||
|
@ -1511,7 +1659,7 @@ func inRowIM(p *parser) bool {
|
|||
return inTableIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.15.
|
||||
// Section 12.2.6.4.15.
|
||||
func inCellIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case StartTagToken:
|
||||
|
@ -1560,7 +1708,7 @@ func inCellIM(p *parser) bool {
|
|||
return inBodyIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.16.
|
||||
// Section 12.2.6.4.16.
|
||||
func inSelectIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case ErrorToken:
|
||||
|
@ -1597,7 +1745,7 @@ func inSelectIM(p *parser) bool {
|
|||
p.tokenizer.NextIsNotRawText()
|
||||
// Ignore the token.
|
||||
return true
|
||||
case a.Script:
|
||||
case a.Script, a.Template:
|
||||
return inHeadIM(p)
|
||||
}
|
||||
case EndTagToken:
|
||||
|
@ -1618,6 +1766,8 @@ func inSelectIM(p *parser) bool {
|
|||
if p.popUntil(selectScope, a.Select) {
|
||||
p.resetInsertionMode()
|
||||
}
|
||||
case a.Template:
|
||||
return inHeadIM(p)
|
||||
}
|
||||
case CommentToken:
|
||||
p.addChild(&Node{
|
||||
|
@ -1632,7 +1782,7 @@ func inSelectIM(p *parser) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.17.
|
||||
// Section 12.2.6.4.17.
|
||||
func inSelectInTableIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case StartTagToken, EndTagToken:
|
||||
|
@ -1650,7 +1800,62 @@ func inSelectInTableIM(p *parser) bool {
|
|||
return inSelectIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.18.
|
||||
// Section 12.2.6.4.18.
|
||||
func inTemplateIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken, CommentToken, DoctypeToken:
|
||||
return inBodyIM(p)
|
||||
case StartTagToken:
|
||||
switch p.tok.DataAtom {
|
||||
case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
|
||||
return inHeadIM(p)
|
||||
case a.Caption, a.Colgroup, a.Tbody, a.Tfoot, a.Thead:
|
||||
p.templateStack.pop()
|
||||
p.templateStack = append(p.templateStack, inTableIM)
|
||||
p.im = inTableIM
|
||||
return false
|
||||
case a.Col:
|
||||
p.templateStack.pop()
|
||||
p.templateStack = append(p.templateStack, inColumnGroupIM)
|
||||
p.im = inColumnGroupIM
|
||||
return false
|
||||
case a.Tr:
|
||||
p.templateStack.pop()
|
||||
p.templateStack = append(p.templateStack, inTableBodyIM)
|
||||
p.im = inTableBodyIM
|
||||
return false
|
||||
case a.Td, a.Th:
|
||||
p.templateStack.pop()
|
||||
p.templateStack = append(p.templateStack, inRowIM)
|
||||
p.im = inRowIM
|
||||
return false
|
||||
default:
|
||||
p.templateStack.pop()
|
||||
p.templateStack = append(p.templateStack, inBodyIM)
|
||||
p.im = inBodyIM
|
||||
return false
|
||||
}
|
||||
case EndTagToken:
|
||||
switch p.tok.DataAtom {
|
||||
case a.Template:
|
||||
return inHeadIM(p)
|
||||
default:
|
||||
// Ignore the token.
|
||||
return true
|
||||
}
|
||||
}
|
||||
if !p.oe.contains(a.Template) {
|
||||
// Ignore the token.
|
||||
return true
|
||||
}
|
||||
p.popUntil(defaultScope, a.Template)
|
||||
p.clearActiveFormattingElements()
|
||||
p.templateStack.pop()
|
||||
p.resetInsertionMode()
|
||||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.6.4.19.
|
||||
func afterBodyIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case ErrorToken:
|
||||
|
@ -1688,7 +1893,7 @@ func afterBodyIM(p *parser) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.19.
|
||||
// Section 12.2.6.4.20.
|
||||
func inFramesetIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case CommentToken:
|
||||
|
@ -1720,6 +1925,11 @@ func inFramesetIM(p *parser) bool {
|
|||
p.acknowledgeSelfClosingTag()
|
||||
case a.Noframes:
|
||||
return inHeadIM(p)
|
||||
case a.Template:
|
||||
// TODO: remove this divergence from the HTML5 spec.
|
||||
//
|
||||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
|
||||
return inTemplateIM(p)
|
||||
}
|
||||
case EndTagToken:
|
||||
switch p.tok.DataAtom {
|
||||
|
@ -1738,7 +1948,7 @@ func inFramesetIM(p *parser) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.20.
|
||||
// Section 12.2.6.4.21.
|
||||
func afterFramesetIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case CommentToken:
|
||||
|
@ -1777,7 +1987,7 @@ func afterFramesetIM(p *parser) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.21.
|
||||
// Section 12.2.6.4.22.
|
||||
func afterAfterBodyIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case ErrorToken:
|
||||
|
@ -1806,7 +2016,7 @@ func afterAfterBodyIM(p *parser) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.22.
|
||||
// Section 12.2.6.4.23.
|
||||
func afterAfterFramesetIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case CommentToken:
|
||||
|
@ -1844,7 +2054,7 @@ func afterAfterFramesetIM(p *parser) bool {
|
|||
|
||||
const whitespaceOrNUL = whitespace + "\x00"
|
||||
|
||||
// Section 12.2.5.5.
|
||||
// Section 12.2.6.5
|
||||
func parseForeignContent(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
|
@ -1924,7 +2134,7 @@ func parseForeignContent(p *parser) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Section 12.2.5.
|
||||
// Section 12.2.6.
|
||||
func (p *parser) inForeignContent() bool {
|
||||
if len(p.oe) == 0 {
|
||||
return false
|
||||
|
@ -2064,6 +2274,9 @@ func ParseFragment(r io.Reader, context *Node) ([]*Node, error) {
|
|||
}
|
||||
p.doc.AppendChild(root)
|
||||
p.oe = nodeStack{root}
|
||||
if context != nil && context.DataAtom == a.Template {
|
||||
p.templateStack = append(p.templateStack, inTemplateIM)
|
||||
}
|
||||
p.resetInsertionMode()
|
||||
|
||||
for n := context; n != nil; n = n.Parent {
|
||||
|
|
|
@ -2,9 +2,12 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
|||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["httplex.go"],
|
||||
importmap = "k8s.io/kubernetes/vendor/golang.org/x/net/lex/httplex",
|
||||
importpath = "golang.org/x/net/lex/httplex",
|
||||
srcs = [
|
||||
"guts.go",
|
||||
"httplex.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/golang.org/x/net/http/httpguts",
|
||||
importpath = "golang.org/x/net/http/httpguts",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//vendor/golang.org/x/net/idna:go_default_library"],
|
||||
)
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package httpguts provides functions implementing various details
|
||||
// of the HTTP specification.
|
||||
//
|
||||
// This package is shared by the standard library (which vendors it)
|
||||
// and x/net/http2. It comes with no API stability promise.
|
||||
package httpguts
|
||||
|
||||
import (
|
||||
"net/textproto"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ValidTrailerHeader reports whether name is a valid header field name to appear
|
||||
// in trailers.
|
||||
// See RFC 7230, Section 4.1.2
|
||||
func ValidTrailerHeader(name string) bool {
|
||||
name = textproto.CanonicalMIMEHeaderKey(name)
|
||||
if strings.HasPrefix(name, "If-") || badTrailer[name] {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var badTrailer = map[string]bool{
|
||||
"Authorization": true,
|
||||
"Cache-Control": true,
|
||||
"Connection": true,
|
||||
"Content-Encoding": true,
|
||||
"Content-Length": true,
|
||||
"Content-Range": true,
|
||||
"Content-Type": true,
|
||||
"Expect": true,
|
||||
"Host": true,
|
||||
"Keep-Alive": true,
|
||||
"Max-Forwards": true,
|
||||
"Pragma": true,
|
||||
"Proxy-Authenticate": true,
|
||||
"Proxy-Authorization": true,
|
||||
"Proxy-Connection": true,
|
||||
"Range": true,
|
||||
"Realm": true,
|
||||
"Te": true,
|
||||
"Trailer": true,
|
||||
"Transfer-Encoding": true,
|
||||
"Www-Authenticate": true,
|
||||
}
|
|
@ -2,12 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package httplex contains rules around lexical matters of various
|
||||
// HTTP-related specifications.
|
||||
//
|
||||
// This package is shared by the standard library (which vendors it)
|
||||
// and x/net/http2. It comes with no API stability promise.
|
||||
package httplex
|
||||
package httpguts
|
||||
|
||||
import (
|
||||
"net"
|
|
@ -10,6 +10,7 @@ go_library(
|
|||
"errors.go",
|
||||
"flow.go",
|
||||
"frame.go",
|
||||
"go111.go",
|
||||
"go16.go",
|
||||
"go17.go",
|
||||
"go17_not18.go",
|
||||
|
@ -18,6 +19,7 @@ go_library(
|
|||
"gotrack.go",
|
||||
"headermap.go",
|
||||
"http2.go",
|
||||
"not_go111.go",
|
||||
"not_go16.go",
|
||||
"not_go17.go",
|
||||
"not_go18.go",
|
||||
|
@ -34,9 +36,9 @@ go_library(
|
|||
importpath = "golang.org/x/net/http2",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/golang.org/x/net/http/httpguts:go_default_library",
|
||||
"//vendor/golang.org/x/net/http2/hpack:go_default_library",
|
||||
"//vendor/golang.org/x/net/idna:go_default_library",
|
||||
"//vendor/golang.org/x/net/lex/httplex:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -52,9 +52,31 @@ const (
|
|||
noDialOnMiss = false
|
||||
)
|
||||
|
||||
// shouldTraceGetConn reports whether getClientConn should call any
|
||||
// ClientTrace.GetConn hook associated with the http.Request.
|
||||
//
|
||||
// This complexity is needed to avoid double calls of the GetConn hook
|
||||
// during the back-and-forth between net/http and x/net/http2 (when the
|
||||
// net/http.Transport is upgraded to also speak http2), as well as support
|
||||
// the case where x/net/http2 is being used directly.
|
||||
func (p *clientConnPool) shouldTraceGetConn(st clientConnIdleState) bool {
|
||||
// If our Transport wasn't made via ConfigureTransport, always
|
||||
// trace the GetConn hook if provided, because that means the
|
||||
// http2 package is being used directly and it's the one
|
||||
// dialing, as opposed to net/http.
|
||||
if _, ok := p.t.ConnPool.(noDialClientConnPool); !ok {
|
||||
return true
|
||||
}
|
||||
// Otherwise, only use the GetConn hook if this connection has
|
||||
// been used previously for other requests. For fresh
|
||||
// connections, the net/http package does the dialing.
|
||||
return !st.freshConn
|
||||
}
|
||||
|
||||
func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) {
|
||||
if isConnectionCloseRequest(req) && dialOnMiss {
|
||||
// It gets its own connection.
|
||||
traceGetConn(req, addr)
|
||||
const singleUse = true
|
||||
cc, err := p.t.dialClientConn(addr, singleUse)
|
||||
if err != nil {
|
||||
|
@ -64,7 +86,10 @@ func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMis
|
|||
}
|
||||
p.mu.Lock()
|
||||
for _, cc := range p.conns[addr] {
|
||||
if cc.CanTakeNewRequest() {
|
||||
if st := cc.idleState(); st.canTakeNewRequest {
|
||||
if p.shouldTraceGetConn(st) {
|
||||
traceGetConn(req, addr)
|
||||
}
|
||||
p.mu.Unlock()
|
||||
return cc, nil
|
||||
}
|
||||
|
@ -73,6 +98,7 @@ func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMis
|
|||
p.mu.Unlock()
|
||||
return nil, ErrNoCachedConn
|
||||
}
|
||||
traceGetConn(req, addr)
|
||||
call := p.getStartDialLocked(addr)
|
||||
p.mu.Unlock()
|
||||
<-call.done
|
||||
|
|
|
@ -57,7 +57,7 @@ func configureTransport(t1 *http.Transport) (*Transport, error) {
|
|||
|
||||
// registerHTTPSProtocol calls Transport.RegisterProtocol but
|
||||
// converting panics into errors.
|
||||
func registerHTTPSProtocol(t *http.Transport, rt http.RoundTripper) (err error) {
|
||||
func registerHTTPSProtocol(t *http.Transport, rt noDialH2RoundTripper) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("%v", e)
|
||||
|
@ -69,10 +69,12 @@ func registerHTTPSProtocol(t *http.Transport, rt http.RoundTripper) (err error)
|
|||
|
||||
// noDialH2RoundTripper is a RoundTripper which only tries to complete the request
|
||||
// if there's already has a cached connection to the host.
|
||||
type noDialH2RoundTripper struct{ t *Transport }
|
||||
// (The field is exported so it can be accessed via reflect from net/http; tested
|
||||
// by TestNoDialH2RoundTripperType)
|
||||
type noDialH2RoundTripper struct{ *Transport }
|
||||
|
||||
func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
res, err := rt.t.RoundTrip(req)
|
||||
res, err := rt.Transport.RoundTrip(req)
|
||||
if isNoCachedConnError(err) {
|
||||
return nil, http.ErrSkipAltProtocol
|
||||
}
|
||||
|
|
|
@ -41,10 +41,10 @@ func (f *flow) take(n int32) {
|
|||
// add adds n bytes (positive or negative) to the flow control window.
|
||||
// It returns false if the sum would exceed 2^31-1.
|
||||
func (f *flow) add(n int32) bool {
|
||||
remain := (1<<31 - 1) - f.n
|
||||
if n > remain {
|
||||
return false
|
||||
}
|
||||
f.n += n
|
||||
sum := f.n + n
|
||||
if (sum > n) == (f.n > 0) {
|
||||
f.n = sum
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -14,8 +14,8 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/lex/httplex"
|
||||
)
|
||||
|
||||
const frameHeaderLen = 9
|
||||
|
@ -733,32 +733,67 @@ func (f *SettingsFrame) IsAck() bool {
|
|||
return f.FrameHeader.Flags.Has(FlagSettingsAck)
|
||||
}
|
||||
|
||||
func (f *SettingsFrame) Value(s SettingID) (v uint32, ok bool) {
|
||||
func (f *SettingsFrame) Value(id SettingID) (v uint32, ok bool) {
|
||||
f.checkValid()
|
||||
buf := f.p
|
||||
for len(buf) > 0 {
|
||||
settingID := SettingID(binary.BigEndian.Uint16(buf[:2]))
|
||||
if settingID == s {
|
||||
return binary.BigEndian.Uint32(buf[2:6]), true
|
||||
for i := 0; i < f.NumSettings(); i++ {
|
||||
if s := f.Setting(i); s.ID == id {
|
||||
return s.Val, true
|
||||
}
|
||||
buf = buf[6:]
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Setting returns the setting from the frame at the given 0-based index.
|
||||
// The index must be >= 0 and less than f.NumSettings().
|
||||
func (f *SettingsFrame) Setting(i int) Setting {
|
||||
buf := f.p
|
||||
return Setting{
|
||||
ID: SettingID(binary.BigEndian.Uint16(buf[i*6 : i*6+2])),
|
||||
Val: binary.BigEndian.Uint32(buf[i*6+2 : i*6+6]),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *SettingsFrame) NumSettings() int { return len(f.p) / 6 }
|
||||
|
||||
// HasDuplicates reports whether f contains any duplicate setting IDs.
|
||||
func (f *SettingsFrame) HasDuplicates() bool {
|
||||
num := f.NumSettings()
|
||||
if num == 0 {
|
||||
return false
|
||||
}
|
||||
// If it's small enough (the common case), just do the n^2
|
||||
// thing and avoid a map allocation.
|
||||
if num < 10 {
|
||||
for i := 0; i < num; i++ {
|
||||
idi := f.Setting(i).ID
|
||||
for j := i + 1; j < num; j++ {
|
||||
idj := f.Setting(j).ID
|
||||
if idi == idj {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
seen := map[SettingID]bool{}
|
||||
for i := 0; i < num; i++ {
|
||||
id := f.Setting(i).ID
|
||||
if seen[id] {
|
||||
return true
|
||||
}
|
||||
seen[id] = true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ForeachSetting runs fn for each setting.
|
||||
// It stops and returns the first error.
|
||||
func (f *SettingsFrame) ForeachSetting(fn func(Setting) error) error {
|
||||
f.checkValid()
|
||||
buf := f.p
|
||||
for len(buf) > 0 {
|
||||
if err := fn(Setting{
|
||||
SettingID(binary.BigEndian.Uint16(buf[:2])),
|
||||
binary.BigEndian.Uint32(buf[2:6]),
|
||||
}); err != nil {
|
||||
for i := 0; i < f.NumSettings(); i++ {
|
||||
if err := fn(f.Setting(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
buf = buf[6:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1462,7 +1497,7 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
|
|||
if VerboseLogs && fr.logReads {
|
||||
fr.debugReadLoggerf("http2: decoded hpack field %+v", hf)
|
||||
}
|
||||
if !httplex.ValidHeaderFieldValue(hf.Value) {
|
||||
if !httpguts.ValidHeaderFieldValue(hf.Value) {
|
||||
invalid = headerFieldValueError(hf.Value)
|
||||
}
|
||||
isPseudo := strings.HasPrefix(hf.Name, ":")
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.11
|
||||
|
||||
package http2
|
||||
|
||||
import "net/textproto"
|
||||
|
||||
func traceHasWroteHeaderField(trace *clientTrace) bool {
|
||||
return trace != nil && trace.WroteHeaderField != nil
|
||||
}
|
||||
|
||||
func traceWroteHeaderField(trace *clientTrace, k, v string) {
|
||||
if trace != nil && trace.WroteHeaderField != nil {
|
||||
trace.WroteHeaderField(k, []string{v})
|
||||
}
|
||||
}
|
||||
|
||||
func traceGot1xxResponseFunc(trace *clientTrace) func(int, textproto.MIMEHeader) error {
|
||||
if trace != nil {
|
||||
return trace.Got1xxResponse
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -18,6 +18,8 @@ type contextContext interface {
|
|||
context.Context
|
||||
}
|
||||
|
||||
var errCanceled = context.Canceled
|
||||
|
||||
func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx contextContext, cancel func()) {
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
ctx = context.WithValue(ctx, http.LocalAddrContextKey, c.LocalAddr())
|
||||
|
@ -48,6 +50,14 @@ func (t *Transport) idleConnTimeout() time.Duration {
|
|||
|
||||
func setResponseUncompressed(res *http.Response) { res.Uncompressed = true }
|
||||
|
||||
func traceGetConn(req *http.Request, hostPort string) {
|
||||
trace := httptrace.ContextClientTrace(req.Context())
|
||||
if trace == nil || trace.GetConn == nil {
|
||||
return
|
||||
}
|
||||
trace.GetConn(hostPort)
|
||||
}
|
||||
|
||||
func traceGotConn(req *http.Request, cc *ClientConn) {
|
||||
trace := httptrace.ContextClientTrace(req.Context())
|
||||
if trace == nil || trace.GotConn == nil {
|
||||
|
@ -104,3 +114,8 @@ func requestTrace(req *http.Request) *clientTrace {
|
|||
func (cc *ClientConn) Ping(ctx context.Context) error {
|
||||
return cc.ping(ctx)
|
||||
}
|
||||
|
||||
// Shutdown gracefully closes the client connection, waiting for running streams to complete.
|
||||
func (cc *ClientConn) Shutdown(ctx context.Context) error {
|
||||
return cc.shutdown(ctx)
|
||||
}
|
||||
|
|
|
@ -206,7 +206,7 @@ func appendVarInt(dst []byte, n byte, i uint64) []byte {
|
|||
}
|
||||
|
||||
// appendHpackString appends s, as encoded in "String Literal"
|
||||
// representation, to dst and returns the the extended buffer.
|
||||
// representation, to dst and returns the extended buffer.
|
||||
//
|
||||
// s will be encoded in Huffman codes only when it produces strictly
|
||||
// shorter byte string.
|
||||
|
|
|
@ -389,6 +389,12 @@ func (d *Decoder) callEmit(hf HeaderField) error {
|
|||
|
||||
// (same invariants and behavior as parseHeaderFieldRepr)
|
||||
func (d *Decoder) parseDynamicTableSizeUpdate() error {
|
||||
// RFC 7541, sec 4.2: This dynamic table size update MUST occur at the
|
||||
// beginning of the first header block following the change to the dynamic table size.
|
||||
if d.dynTab.size > 0 {
|
||||
return DecodingError{errors.New("dynamic table size update MUST occur at the beginning of a header block")}
|
||||
}
|
||||
|
||||
buf := d.buf
|
||||
size, buf, err := readVarInt(5, buf)
|
||||
if err != nil {
|
||||
|
|
|
@ -47,6 +47,7 @@ var ErrInvalidHuffman = errors.New("hpack: invalid Huffman-encoded data")
|
|||
// If maxLen is greater than 0, attempts to write more to buf than
|
||||
// maxLen bytes will return ErrStringLength.
|
||||
func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error {
|
||||
rootHuffmanNode := getRootHuffmanNode()
|
||||
n := rootHuffmanNode
|
||||
// cur is the bit buffer that has not been fed into n.
|
||||
// cbits is the number of low order bits in cur that are valid.
|
||||
|
@ -106,7 +107,7 @@ func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error {
|
|||
|
||||
type node struct {
|
||||
// children is non-nil for internal nodes
|
||||
children []*node
|
||||
children *[256]*node
|
||||
|
||||
// The following are only valid if children is nil:
|
||||
codeLen uint8 // number of bits that led to the output of sym
|
||||
|
@ -114,22 +115,31 @@ type node struct {
|
|||
}
|
||||
|
||||
func newInternalNode() *node {
|
||||
return &node{children: make([]*node, 256)}
|
||||
return &node{children: new([256]*node)}
|
||||
}
|
||||
|
||||
var rootHuffmanNode = newInternalNode()
|
||||
var (
|
||||
buildRootOnce sync.Once
|
||||
lazyRootHuffmanNode *node
|
||||
)
|
||||
|
||||
func init() {
|
||||
func getRootHuffmanNode() *node {
|
||||
buildRootOnce.Do(buildRootHuffmanNode)
|
||||
return lazyRootHuffmanNode
|
||||
}
|
||||
|
||||
func buildRootHuffmanNode() {
|
||||
if len(huffmanCodes) != 256 {
|
||||
panic("unexpected size")
|
||||
}
|
||||
lazyRootHuffmanNode = newInternalNode()
|
||||
for i, code := range huffmanCodes {
|
||||
addDecoderNode(byte(i), code, huffmanCodeLen[i])
|
||||
}
|
||||
}
|
||||
|
||||
func addDecoderNode(sym byte, code uint32, codeLen uint8) {
|
||||
cur := rootHuffmanNode
|
||||
cur := lazyRootHuffmanNode
|
||||
for codeLen > 8 {
|
||||
codeLen -= 8
|
||||
i := uint8(code >> codeLen)
|
||||
|
|
|
@ -29,7 +29,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/lex/httplex"
|
||||
"golang.org/x/net/http/httpguts"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -179,7 +179,7 @@ var (
|
|||
)
|
||||
|
||||
// validWireHeaderFieldName reports whether v is a valid header field
|
||||
// name (key). See httplex.ValidHeaderName for the base rules.
|
||||
// name (key). See httpguts.ValidHeaderName for the base rules.
|
||||
//
|
||||
// Further, http2 says:
|
||||
// "Just as in HTTP/1.x, header field names are strings of ASCII
|
||||
|
@ -191,7 +191,7 @@ func validWireHeaderFieldName(v string) bool {
|
|||
return false
|
||||
}
|
||||
for _, r := range v {
|
||||
if !httplex.IsTokenRune(r) {
|
||||
if !httpguts.IsTokenRune(r) {
|
||||
return false
|
||||
}
|
||||
if 'A' <= r && r <= 'Z' {
|
||||
|
@ -312,7 +312,7 @@ func mustUint31(v int32) uint32 {
|
|||
}
|
||||
|
||||
// bodyAllowedForStatus reports whether a given response status code
|
||||
// permits a body. See RFC 2616, section 4.4.
|
||||
// permits a body. See RFC 7230, section 3.3.
|
||||
func bodyAllowedForStatus(status int) bool {
|
||||
switch {
|
||||
case status >= 100 && status <= 199:
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.11
|
||||
|
||||
package http2
|
||||
|
||||
import "net/textproto"
|
||||
|
||||
func traceHasWroteHeaderField(trace *clientTrace) bool { return false }
|
||||
|
||||
func traceWroteHeaderField(trace *clientTrace, k, v string) {}
|
||||
|
||||
func traceGot1xxResponseFunc(trace *clientTrace) func(int, textproto.MIMEHeader) error {
|
||||
return nil
|
||||
}
|
|
@ -8,6 +8,7 @@ package http2
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
@ -18,6 +19,8 @@ type contextContext interface {
|
|||
Err() error
|
||||
}
|
||||
|
||||
var errCanceled = errors.New("canceled")
|
||||
|
||||
type fakeContext struct{}
|
||||
|
||||
func (fakeContext) Done() <-chan struct{} { return nil }
|
||||
|
@ -34,6 +37,7 @@ func setResponseUncompressed(res *http.Response) {
|
|||
type clientTrace struct{}
|
||||
|
||||
func requestTrace(*http.Request) *clientTrace { return nil }
|
||||
func traceGetConn(*http.Request, string) {}
|
||||
func traceGotConn(*http.Request, *ClientConn) {}
|
||||
func traceFirstResponseByte(*clientTrace) {}
|
||||
func traceWroteHeaders(*clientTrace) {}
|
||||
|
@ -84,4 +88,8 @@ func (cc *ClientConn) Ping(ctx contextContext) error {
|
|||
return cc.ping(ctx)
|
||||
}
|
||||
|
||||
func (cc *ClientConn) Shutdown(ctx contextContext) error {
|
||||
return cc.shutdown(ctx)
|
||||
}
|
||||
|
||||
func (t *Transport) idleConnTimeout() time.Duration { return 0 }
|
||||
|
|
|
@ -46,6 +46,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
)
|
||||
|
||||
|
@ -406,7 +407,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
|||
// addresses during development.
|
||||
//
|
||||
// TODO: optionally enforce? Or enforce at the time we receive
|
||||
// a new request, and verify the the ServerName matches the :authority?
|
||||
// a new request, and verify the ServerName matches the :authority?
|
||||
// But that precludes proxy situations, perhaps.
|
||||
//
|
||||
// So for now, do nothing here again.
|
||||
|
@ -1486,6 +1487,12 @@ func (sc *serverConn) processSettings(f *SettingsFrame) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
if f.NumSettings() > 100 || f.HasDuplicates() {
|
||||
// This isn't actually in the spec, but hang up on
|
||||
// suspiciously large settings frames or those with
|
||||
// duplicate entries.
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
if err := f.ForeachSetting(sc.processSetting); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1574,6 +1581,12 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
|||
// type PROTOCOL_ERROR."
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
// RFC 7540, sec 6.1: If a DATA frame is received whose stream is not in
|
||||
// "open" or "half-closed (local)" state, the recipient MUST respond with a
|
||||
// stream error (Section 5.4.2) of type STREAM_CLOSED.
|
||||
if state == stateClosed {
|
||||
return streamError(id, ErrCodeStreamClosed)
|
||||
}
|
||||
if st == nil || state != stateOpen || st.gotTrailerHeader || st.resetQueued {
|
||||
// This includes sending a RST_STREAM if the stream is
|
||||
// in stateHalfClosedLocal (which currently means that
|
||||
|
@ -1607,7 +1620,10 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
|||
// Sender sending more than they'd declared?
|
||||
if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes {
|
||||
st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes))
|
||||
return streamError(id, ErrCodeStreamClosed)
|
||||
// RFC 7540, sec 8.1.2.6: A request or response is also malformed if the
|
||||
// value of a content-length header field does not equal the sum of the
|
||||
// DATA frame payload lengths that form the body.
|
||||
return streamError(id, ErrCodeProtocol)
|
||||
}
|
||||
if f.Length > 0 {
|
||||
// Check whether the client has flow control quota.
|
||||
|
@ -1717,6 +1733,13 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
|||
// processing this frame.
|
||||
return nil
|
||||
}
|
||||
// RFC 7540, sec 5.1: If an endpoint receives additional frames, other than
|
||||
// WINDOW_UPDATE, PRIORITY, or RST_STREAM, for a stream that is in
|
||||
// this state, it MUST respond with a stream error (Section 5.4.2) of
|
||||
// type STREAM_CLOSED.
|
||||
if st.state == stateHalfClosedRemote {
|
||||
return streamError(id, ErrCodeStreamClosed)
|
||||
}
|
||||
return st.processTrailerHeaders(f)
|
||||
}
|
||||
|
||||
|
@ -1817,7 +1840,7 @@ func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
|
|||
if st.trailer != nil {
|
||||
for _, hf := range f.RegularFields() {
|
||||
key := sc.canonicalHeader(hf.Name)
|
||||
if !ValidTrailerHeader(key) {
|
||||
if !httpguts.ValidTrailerHeader(key) {
|
||||
// TODO: send more details to the peer somehow. But http2 has
|
||||
// no way to send debug data at a stream level. Discuss with
|
||||
// HTTP folk.
|
||||
|
@ -2284,8 +2307,8 @@ func (rws *responseWriterState) hasTrailers() bool { return len(rws.trailers) !=
|
|||
// written in the trailers at the end of the response.
|
||||
func (rws *responseWriterState) declareTrailer(k string) {
|
||||
k = http.CanonicalHeaderKey(k)
|
||||
if !ValidTrailerHeader(k) {
|
||||
// Forbidden by RFC 2616 14.40.
|
||||
if !httpguts.ValidTrailerHeader(k) {
|
||||
// Forbidden by RFC 7230, section 4.1.2.
|
||||
rws.conn.logf("ignoring invalid trailer %q", k)
|
||||
return
|
||||
}
|
||||
|
@ -2335,6 +2358,19 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
|||
foreachHeaderElement(v, rws.declareTrailer)
|
||||
}
|
||||
|
||||
// "Connection" headers aren't allowed in HTTP/2 (RFC 7540, 8.1.2.2),
|
||||
// but respect "Connection" == "close" to mean sending a GOAWAY and tearing
|
||||
// down the TCP connection when idle, like we do for HTTP/1.
|
||||
// TODO: remove more Connection-specific header fields here, in addition
|
||||
// to "Connection".
|
||||
if _, ok := rws.snapHeader["Connection"]; ok {
|
||||
v := rws.snapHeader.Get("Connection")
|
||||
delete(rws.snapHeader, "Connection")
|
||||
if v == "close" {
|
||||
rws.conn.startGracefulShutdown()
|
||||
}
|
||||
}
|
||||
|
||||
endStream := (rws.handlerDone && !rws.hasTrailers() && len(p) == 0) || isHeadResp
|
||||
err = rws.conn.writeHeaders(rws.stream, &writeResHeaders{
|
||||
streamID: rws.stream.id,
|
||||
|
@ -2406,7 +2442,7 @@ const TrailerPrefix = "Trailer:"
|
|||
// after the header has already been flushed. Because the Go
|
||||
// ResponseWriter interface has no way to set Trailers (only the
|
||||
// Header), and because we didn't want to expand the ResponseWriter
|
||||
// interface, and because nobody used trailers, and because RFC 2616
|
||||
// interface, and because nobody used trailers, and because RFC 7230
|
||||
// says you SHOULD (but not must) predeclare any trailers in the
|
||||
// header, the official ResponseWriter rules said trailers in Go must
|
||||
// be predeclared, and then we reuse the same ResponseWriter.Header()
|
||||
|
@ -2790,7 +2826,7 @@ func (sc *serverConn) startPush(msg *startPushRequest) {
|
|||
}
|
||||
|
||||
// foreachHeaderElement splits v according to the "#rule" construction
|
||||
// in RFC 2616 section 2.1 and calls fn for each non-empty element.
|
||||
// in RFC 7230 section 7 and calls fn for each non-empty element.
|
||||
func foreachHeaderElement(v string, fn func(string)) {
|
||||
v = textproto.TrimString(v)
|
||||
if v == "" {
|
||||
|
@ -2838,41 +2874,6 @@ func new400Handler(err error) http.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// ValidTrailerHeader reports whether name is a valid header field name to appear
|
||||
// in trailers.
|
||||
// See: http://tools.ietf.org/html/rfc7230#section-4.1.2
|
||||
func ValidTrailerHeader(name string) bool {
|
||||
name = http.CanonicalHeaderKey(name)
|
||||
if strings.HasPrefix(name, "If-") || badTrailer[name] {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var badTrailer = map[string]bool{
|
||||
"Authorization": true,
|
||||
"Cache-Control": true,
|
||||
"Connection": true,
|
||||
"Content-Encoding": true,
|
||||
"Content-Length": true,
|
||||
"Content-Range": true,
|
||||
"Content-Type": true,
|
||||
"Expect": true,
|
||||
"Host": true,
|
||||
"Keep-Alive": true,
|
||||
"Max-Forwards": true,
|
||||
"Pragma": true,
|
||||
"Proxy-Authenticate": true,
|
||||
"Proxy-Authorization": true,
|
||||
"Proxy-Connection": true,
|
||||
"Range": true,
|
||||
"Realm": true,
|
||||
"Te": true,
|
||||
"Trailer": true,
|
||||
"Transfer-Encoding": true,
|
||||
"Www-Authenticate": true,
|
||||
}
|
||||
|
||||
// h1ServerKeepAlivesDisabled reports whether hs has its keep-alives
|
||||
// disabled. See comments on h1ServerShutdownChan above for why
|
||||
// the code is written this way.
|
||||
|
|
|
@ -21,15 +21,16 @@ import (
|
|||
mathrand "math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/idna"
|
||||
"golang.org/x/net/lex/httplex"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -159,6 +160,7 @@ type ClientConn struct {
|
|||
cond *sync.Cond // hold mu; broadcast on flow/closed changes
|
||||
flow flow // our conn-level flow control quota (cs.flow is per stream)
|
||||
inflow flow // peer's conn-level flow control
|
||||
closing bool
|
||||
closed bool
|
||||
wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back
|
||||
goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received
|
||||
|
@ -214,6 +216,7 @@ type clientStream struct {
|
|||
firstByte bool // got the first response byte
|
||||
pastHeaders bool // got first MetaHeadersFrame (actual headers)
|
||||
pastTrailers bool // got optional second MetaHeadersFrame (trailers)
|
||||
num1xx uint8 // number of 1xx responses seen
|
||||
|
||||
trailer http.Header // accumulated trailers
|
||||
resTrailer *http.Header // client's Response.Trailer
|
||||
|
@ -237,6 +240,17 @@ func awaitRequestCancel(req *http.Request, done <-chan struct{}) error {
|
|||
}
|
||||
}
|
||||
|
||||
var got1xxFuncForTests func(int, textproto.MIMEHeader) error
|
||||
|
||||
// get1xxTraceFunc returns the value of request's httptrace.ClientTrace.Got1xxResponse func,
|
||||
// if any. It returns nil if not set or if the Go version is too old.
|
||||
func (cs *clientStream) get1xxTraceFunc() func(int, textproto.MIMEHeader) error {
|
||||
if fn := got1xxFuncForTests; fn != nil {
|
||||
return fn
|
||||
}
|
||||
return traceGot1xxResponseFunc(cs.trace)
|
||||
}
|
||||
|
||||
// awaitRequestCancel waits for the user to cancel a request, its context to
|
||||
// expire, or for the request to be done (any way it might be removed from the
|
||||
// cc.streams map: peer reset, successful completion, TCP connection breakage,
|
||||
|
@ -423,20 +437,17 @@ func shouldRetryRequest(req *http.Request, err error, afterBodyWrite bool) (*htt
|
|||
if !canRetryError(err) {
|
||||
return nil, err
|
||||
}
|
||||
if !afterBodyWrite {
|
||||
return req, nil
|
||||
}
|
||||
// If the Body is nil (or http.NoBody), it's safe to reuse
|
||||
// this request and its Body.
|
||||
if req.Body == nil || reqBodyIsNoBody(req.Body) {
|
||||
return req, nil
|
||||
}
|
||||
// Otherwise we depend on the Request having its GetBody
|
||||
// func defined.
|
||||
|
||||
// If the request body can be reset back to its original
|
||||
// state via the optional req.GetBody, do that.
|
||||
getBody := reqGetBody(req) // Go 1.8: getBody = req.GetBody
|
||||
if getBody == nil {
|
||||
return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err)
|
||||
}
|
||||
if getBody != nil {
|
||||
// TODO: consider a req.Body.Close here? or audit that all caller paths do?
|
||||
body, err := getBody()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -444,6 +455,18 @@ func shouldRetryRequest(req *http.Request, err error, afterBodyWrite bool) (*htt
|
|||
newReq := *req
|
||||
newReq.Body = body
|
||||
return &newReq, nil
|
||||
}
|
||||
|
||||
// The Request.Body can't reset back to the beginning, but we
|
||||
// don't seem to have started to read from it yet, so reuse
|
||||
// the request directly. The "afterBodyWrite" means the
|
||||
// bodyWrite process has started, which becomes true before
|
||||
// the first Read.
|
||||
if !afterBodyWrite {
|
||||
return req, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err)
|
||||
}
|
||||
|
||||
func canRetryError(err error) bool {
|
||||
|
@ -567,6 +590,10 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
|
|||
// henc in response to SETTINGS frames?
|
||||
cc.henc = hpack.NewEncoder(&cc.hbuf)
|
||||
|
||||
if t.AllowHTTP {
|
||||
cc.nextStreamID = 3
|
||||
}
|
||||
|
||||
if cs, ok := c.(connectionStater); ok {
|
||||
state := cs.ConnectionState()
|
||||
cc.tlsState = &state
|
||||
|
@ -626,12 +653,32 @@ func (cc *ClientConn) CanTakeNewRequest() bool {
|
|||
return cc.canTakeNewRequestLocked()
|
||||
}
|
||||
|
||||
func (cc *ClientConn) canTakeNewRequestLocked() bool {
|
||||
// clientConnIdleState describes the suitability of a client
|
||||
// connection to initiate a new RoundTrip request.
|
||||
type clientConnIdleState struct {
|
||||
canTakeNewRequest bool
|
||||
freshConn bool // whether it's unused by any previous request
|
||||
}
|
||||
|
||||
func (cc *ClientConn) idleState() clientConnIdleState {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
return cc.idleStateLocked()
|
||||
}
|
||||
|
||||
func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) {
|
||||
if cc.singleUse && cc.nextStreamID > 1 {
|
||||
return false
|
||||
return
|
||||
}
|
||||
return cc.goAway == nil && !cc.closed &&
|
||||
st.canTakeNewRequest = cc.goAway == nil && !cc.closed && !cc.closing &&
|
||||
int64(cc.nextStreamID)+int64(cc.pendingRequests) < math.MaxInt32
|
||||
st.freshConn = cc.nextStreamID == 1 && st.canTakeNewRequest
|
||||
return
|
||||
}
|
||||
|
||||
func (cc *ClientConn) canTakeNewRequestLocked() bool {
|
||||
st := cc.idleStateLocked()
|
||||
return st.canTakeNewRequest
|
||||
}
|
||||
|
||||
// onIdleTimeout is called from a time.AfterFunc goroutine. It will
|
||||
|
@ -661,6 +708,88 @@ func (cc *ClientConn) closeIfIdle() {
|
|||
cc.tconn.Close()
|
||||
}
|
||||
|
||||
var shutdownEnterWaitStateHook = func() {}
|
||||
|
||||
// Shutdown gracefully close the client connection, waiting for running streams to complete.
|
||||
// Public implementation is in go17.go and not_go17.go
|
||||
func (cc *ClientConn) shutdown(ctx contextContext) error {
|
||||
if err := cc.sendGoAway(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Wait for all in-flight streams to complete or connection to close
|
||||
done := make(chan error, 1)
|
||||
cancelled := false // guarded by cc.mu
|
||||
go func() {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
for {
|
||||
if len(cc.streams) == 0 || cc.closed {
|
||||
cc.closed = true
|
||||
done <- cc.tconn.Close()
|
||||
break
|
||||
}
|
||||
if cancelled {
|
||||
break
|
||||
}
|
||||
cc.cond.Wait()
|
||||
}
|
||||
}()
|
||||
shutdownEnterWaitStateHook()
|
||||
select {
|
||||
case err := <-done:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
cc.mu.Lock()
|
||||
// Free the goroutine above
|
||||
cancelled = true
|
||||
cc.cond.Broadcast()
|
||||
cc.mu.Unlock()
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
func (cc *ClientConn) sendGoAway() error {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
cc.wmu.Lock()
|
||||
defer cc.wmu.Unlock()
|
||||
if cc.closing {
|
||||
// GOAWAY sent already
|
||||
return nil
|
||||
}
|
||||
// Send a graceful shutdown frame to server
|
||||
maxStreamID := cc.nextStreamID
|
||||
if err := cc.fr.WriteGoAway(maxStreamID, ErrCodeNo, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cc.bw.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Prevent new requests
|
||||
cc.closing = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the client connection immediately.
|
||||
//
|
||||
// In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead.
|
||||
func (cc *ClientConn) Close() error {
|
||||
cc.mu.Lock()
|
||||
defer cc.cond.Broadcast()
|
||||
defer cc.mu.Unlock()
|
||||
err := errors.New("http2: client connection force closed via ClientConn.Close")
|
||||
for id, cs := range cc.streams {
|
||||
select {
|
||||
case cs.resc <- resAndError{err: err}:
|
||||
default:
|
||||
}
|
||||
cs.bufPipe.CloseWithError(err)
|
||||
delete(cc.streams, id)
|
||||
}
|
||||
cc.closed = true
|
||||
return cc.tconn.Close()
|
||||
}
|
||||
|
||||
const maxAllocFrameSize = 512 << 10
|
||||
|
||||
// frameBuffer returns a scratch buffer suitable for writing DATA frames.
|
||||
|
@ -743,7 +872,7 @@ func checkConnHeaders(req *http.Request) error {
|
|||
if vv := req.Header["Transfer-Encoding"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "chunked") {
|
||||
return fmt.Errorf("http2: invalid Transfer-Encoding request header: %q", vv)
|
||||
}
|
||||
if vv := req.Header["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "close" && vv[0] != "keep-alive") {
|
||||
if vv := req.Header["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && !strings.EqualFold(vv[0], "close") && !strings.EqualFold(vv[0], "keep-alive")) {
|
||||
return fmt.Errorf("http2: invalid Connection request header: %q", vv)
|
||||
}
|
||||
return nil
|
||||
|
@ -931,6 +1060,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
|||
default:
|
||||
}
|
||||
if err != nil {
|
||||
cc.forgetStreamID(cs.ID)
|
||||
return nil, cs.getStartedWrite(), err
|
||||
}
|
||||
bodyWritten = true
|
||||
|
@ -951,6 +1081,9 @@ func (cc *ClientConn) awaitOpenSlotForRequest(req *http.Request) error {
|
|||
for {
|
||||
cc.lastActive = time.Now()
|
||||
if cc.closed || !cc.canTakeNewRequestLocked() {
|
||||
if waitingForConn != nil {
|
||||
close(waitingForConn)
|
||||
}
|
||||
return errClientConnUnusable
|
||||
}
|
||||
if int64(len(cc.streams))+1 <= int64(cc.maxConcurrentStreams) {
|
||||
|
@ -1049,6 +1182,7 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (
|
|||
sawEOF = true
|
||||
err = nil
|
||||
} else if err != nil {
|
||||
cc.writeStreamReset(cs.ID, ErrCodeCancel, err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -1174,7 +1308,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
|
|||
if host == "" {
|
||||
host = req.URL.Host
|
||||
}
|
||||
host, err := httplex.PunycodeHostPort(host)
|
||||
host, err := httpguts.PunycodeHostPort(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1199,11 +1333,11 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
|
|||
// potentially pollute our hpack state. (We want to be able to
|
||||
// continue to reuse the hpack encoder for future requests)
|
||||
for k, vv := range req.Header {
|
||||
if !httplex.ValidHeaderFieldName(k) {
|
||||
if !httpguts.ValidHeaderFieldName(k) {
|
||||
return nil, fmt.Errorf("invalid HTTP header name %q", k)
|
||||
}
|
||||
for _, v := range vv {
|
||||
if !httplex.ValidHeaderFieldValue(v) {
|
||||
if !httpguts.ValidHeaderFieldValue(v) {
|
||||
return nil, fmt.Errorf("invalid HTTP header value %q for header %q", v, k)
|
||||
}
|
||||
}
|
||||
|
@ -1284,9 +1418,16 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
|
|||
return nil, errRequestHeaderListSize
|
||||
}
|
||||
|
||||
trace := requestTrace(req)
|
||||
traceHeaders := traceHasWroteHeaderField(trace)
|
||||
|
||||
// Header list size is ok. Write the headers.
|
||||
enumerateHeaders(func(name, value string) {
|
||||
cc.writeHeader(strings.ToLower(name), value)
|
||||
name = strings.ToLower(name)
|
||||
cc.writeHeader(name, value)
|
||||
if traceHeaders {
|
||||
traceWroteHeaderField(trace, name, value)
|
||||
}
|
||||
})
|
||||
|
||||
return cc.hbuf.Bytes(), nil
|
||||
|
@ -1608,8 +1749,7 @@ func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
|
|||
// is the detail.
|
||||
//
|
||||
// As a special case, handleResponse may return (nil, nil) to skip the
|
||||
// frame (currently only used for 100 expect continue). This special
|
||||
// case is going away after Issue 13851 is fixed.
|
||||
// frame (currently only used for 1xx responses).
|
||||
func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFrame) (*http.Response, error) {
|
||||
if f.Truncated {
|
||||
return nil, errResponseHeaderListSize
|
||||
|
@ -1624,15 +1764,6 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
|
|||
return nil, errors.New("malformed response from server: malformed non-numeric status pseudo header")
|
||||
}
|
||||
|
||||
if statusCode == 100 {
|
||||
traceGot100Continue(cs.trace)
|
||||
if cs.on100 != nil {
|
||||
cs.on100() // forces any write delay timer to fire
|
||||
}
|
||||
cs.pastHeaders = false // do it all again
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
header := make(http.Header)
|
||||
res := &http.Response{
|
||||
Proto: "HTTP/2.0",
|
||||
|
@ -1657,6 +1788,27 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
|
|||
}
|
||||
}
|
||||
|
||||
if statusCode >= 100 && statusCode <= 199 {
|
||||
cs.num1xx++
|
||||
const max1xxResponses = 5 // arbitrary bound on number of informational responses, same as net/http
|
||||
if cs.num1xx > max1xxResponses {
|
||||
return nil, errors.New("http2: too many 1xx informational responses")
|
||||
}
|
||||
if fn := cs.get1xxTraceFunc(); fn != nil {
|
||||
if err := fn(statusCode, textproto.MIMEHeader(header)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if statusCode == 100 {
|
||||
traceGot100Continue(cs.trace)
|
||||
if cs.on100 != nil {
|
||||
cs.on100() // forces any write delay timer to fire
|
||||
}
|
||||
}
|
||||
cs.pastHeaders = false // do it all again
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
streamEnded := f.StreamEnded()
|
||||
isHead := cs.req.Method == "HEAD"
|
||||
if !streamEnded || isHead {
|
||||
|
@ -2244,7 +2396,7 @@ func (t *Transport) getBodyWriterState(cs *clientStream, body io.Reader) (s body
|
|||
}
|
||||
s.delay = t.expectContinueTimeout()
|
||||
if s.delay == 0 ||
|
||||
!httplex.HeaderValuesContainsToken(
|
||||
!httpguts.HeaderValuesContainsToken(
|
||||
cs.req.Header["Expect"],
|
||||
"100-continue") {
|
||||
return
|
||||
|
@ -2299,5 +2451,5 @@ func (s bodyWriterState) scheduleBodyWrite() {
|
|||
// isConnectionCloseRequest reports whether req should use its own
|
||||
// connection for a single request and then close the connection.
|
||||
func isConnectionCloseRequest(req *http.Request) bool {
|
||||
return req.Close || httplex.HeaderValuesContainsToken(req.Header["Connection"], "close")
|
||||
return req.Close || httpguts.HeaderValuesContainsToken(req.Header["Connection"], "close")
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/lex/httplex"
|
||||
)
|
||||
|
||||
// writeFramer is implemented by any type that is used to write frames.
|
||||
|
@ -350,7 +350,7 @@ func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) {
|
|||
}
|
||||
isTE := k == "transfer-encoding"
|
||||
for _, v := range vv {
|
||||
if !httplex.ValidHeaderFieldValue(v) {
|
||||
if !httpguts.ValidHeaderFieldValue(v) {
|
||||
// TODO: return an error? golang.org/issue/14048
|
||||
// For now just omit it.
|
||||
continue
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"helper_bsd.go",
|
||||
"helper_nobsd.go",
|
||||
"helper_posix.go",
|
||||
"helper_stub.go",
|
||||
"helper_unix.go",
|
||||
"helper_windows.go",
|
||||
"interface.go",
|
||||
"rlimit.go",
|
||||
"stack.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/golang.org/x/net/internal/nettest",
|
||||
importpath = "golang.org/x/net/internal/nettest",
|
||||
visibility = ["//vendor/golang.org/x/net:__subpackages__"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
package nettest
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var darwinVersion int
|
||||
|
||||
func init() {
|
||||
if runtime.GOOS == "darwin" {
|
||||
// See http://support.apple.com/kb/HT1633.
|
||||
s, err := syscall.Sysctl("kern.osrelease")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ss := strings.Split(s, ".")
|
||||
if len(ss) == 0 {
|
||||
return
|
||||
}
|
||||
darwinVersion, _ = strconv.Atoi(ss[0])
|
||||
}
|
||||
}
|
||||
|
||||
func supportsIPv6MulticastDeliveryOnLoopback() bool {
|
||||
switch runtime.GOOS {
|
||||
case "freebsd":
|
||||
// See http://www.freebsd.org/cgi/query-pr.cgi?pr=180065.
|
||||
// Even after the fix, it looks like the latest
|
||||
// kernels don't deliver link-local scoped multicast
|
||||
// packets correctly.
|
||||
return false
|
||||
case "darwin":
|
||||
return !causesIPv6Crash()
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func causesIPv6Crash() bool {
|
||||
// We see some kernel crash when running IPv6 with IP-level
|
||||
// options on Darwin kernel version 12 or below.
|
||||
// See golang.org/issues/17015.
|
||||
return darwinVersion < 13
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux solaris
|
||||
|
||||
package nettest
|
||||
|
||||
func supportsIPv6MulticastDeliveryOnLoopback() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func causesIPv6Crash() bool {
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
|
||||
|
||||
package nettest
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func protocolNotSupported(err error) bool {
|
||||
switch err := err.(type) {
|
||||
case syscall.Errno:
|
||||
switch err {
|
||||
case syscall.EPROTONOSUPPORT, syscall.ENOPROTOOPT:
|
||||
return true
|
||||
}
|
||||
case *os.SyscallError:
|
||||
switch err := err.Err.(type) {
|
||||
case syscall.Errno:
|
||||
switch err {
|
||||
case syscall.EPROTONOSUPPORT, syscall.ENOPROTOOPT:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build js nacl plan9
|
||||
|
||||
package nettest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func maxOpenFiles() int {
|
||||
return defaultMaxOpenFiles
|
||||
}
|
||||
|
||||
func supportsRawIPSocket() (string, bool) {
|
||||
return fmt.Sprintf("not supported on %s", runtime.GOOS), false
|
||||
}
|
||||
|
||||
func supportsIPv6MulticastDeliveryOnLoopback() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func causesIPv6Crash() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func protocolNotSupported(err error) bool {
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package nettest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func maxOpenFiles() int {
|
||||
var rlim syscall.Rlimit
|
||||
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlim); err != nil {
|
||||
return defaultMaxOpenFiles
|
||||
}
|
||||
return int(rlim.Cur)
|
||||
}
|
||||
|
||||
func supportsRawIPSocket() (string, bool) {
|
||||
if os.Getuid() != 0 {
|
||||
return fmt.Sprintf("must be root on %s", runtime.GOOS), false
|
||||
}
|
||||
return "", true
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package nettest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func maxOpenFiles() int {
|
||||
return 4 * defaultMaxOpenFiles /* actually it's 16581375 */
|
||||
}
|
||||
|
||||
func supportsRawIPSocket() (string, bool) {
|
||||
// From http://msdn.microsoft.com/en-us/library/windows/desktop/ms740548.aspx:
|
||||
// Note: To use a socket of type SOCK_RAW requires administrative privileges.
|
||||
// Users running Winsock applications that use raw sockets must be a member of
|
||||
// the Administrators group on the local computer, otherwise raw socket calls
|
||||
// will fail with an error code of WSAEACCES. On Windows Vista and later, access
|
||||
// for raw sockets is enforced at socket creation. In earlier versions of Windows,
|
||||
// access for raw sockets is enforced during other socket operations.
|
||||
s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, 0)
|
||||
if err == syscall.WSAEACCES {
|
||||
return fmt.Sprintf("no access to raw socket allowed on %s", runtime.GOOS), false
|
||||
}
|
||||
if err != nil {
|
||||
return err.Error(), false
|
||||
}
|
||||
syscall.Closesocket(s)
|
||||
return "", true
|
||||
}
|
||||
|
||||
func supportsIPv6MulticastDeliveryOnLoopback() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func causesIPv6Crash() bool {
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package nettest
|
||||
|
||||
import "net"
|
||||
|
||||
// IsMulticastCapable reports whether ifi is an IP multicast-capable
|
||||
// network interface. Network must be "ip", "ip4" or "ip6".
|
||||
func IsMulticastCapable(network string, ifi *net.Interface) (net.IP, bool) {
|
||||
switch network {
|
||||
case "ip", "ip4", "ip6":
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
if ifi == nil || ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 {
|
||||
return nil, false
|
||||
}
|
||||
return hasRoutableIP(network, ifi)
|
||||
}
|
||||
|
||||
// RoutedInterface returns a network interface that can route IP
|
||||
// traffic and satisfies flags. It returns nil when an appropriate
|
||||
// network interface is not found. Network must be "ip", "ip4" or
|
||||
// "ip6".
|
||||
func RoutedInterface(network string, flags net.Flags) *net.Interface {
|
||||
switch network {
|
||||
case "ip", "ip4", "ip6":
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
ift, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, ifi := range ift {
|
||||
if ifi.Flags&flags != flags {
|
||||
continue
|
||||
}
|
||||
if _, ok := hasRoutableIP(network, &ifi); !ok {
|
||||
continue
|
||||
}
|
||||
return &ifi
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func hasRoutableIP(network string, ifi *net.Interface) (net.IP, bool) {
|
||||
ifat, err := ifi.Addrs()
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
for _, ifa := range ifat {
|
||||
switch ifa := ifa.(type) {
|
||||
case *net.IPAddr:
|
||||
if ip := routableIP(network, ifa.IP); ip != nil {
|
||||
return ip, true
|
||||
}
|
||||
case *net.IPNet:
|
||||
if ip := routableIP(network, ifa.IP); ip != nil {
|
||||
return ip, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func routableIP(network string, ip net.IP) net.IP {
|
||||
if !ip.IsLoopback() && !ip.IsLinkLocalUnicast() && !ip.IsGlobalUnicast() {
|
||||
return nil
|
||||
}
|
||||
switch network {
|
||||
case "ip4":
|
||||
if ip := ip.To4(); ip != nil {
|
||||
return ip
|
||||
}
|
||||
case "ip6":
|
||||
if ip.IsLoopback() { // addressing scope of the loopback address depends on each implementation
|
||||
return nil
|
||||
}
|
||||
if ip := ip.To16(); ip != nil && ip.To4() == nil {
|
||||
return ip
|
||||
}
|
||||
default:
|
||||
if ip := ip.To4(); ip != nil {
|
||||
return ip
|
||||
}
|
||||
if ip := ip.To16(); ip != nil {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package nettest
|
||||
|
||||
const defaultMaxOpenFiles = 256
|
||||
|
||||
// MaxOpenFiles returns the maximum number of open files for the
|
||||
// caller's process.
|
||||
func MaxOpenFiles() int { return maxOpenFiles() }
|
|
@ -0,0 +1,152 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package nettest provides utilities for network testing.
|
||||
package nettest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
supportsIPv4 bool
|
||||
supportsIPv6 bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil {
|
||||
ln.Close()
|
||||
supportsIPv4 = true
|
||||
}
|
||||
if ln, err := net.Listen("tcp6", "[::1]:0"); err == nil {
|
||||
ln.Close()
|
||||
supportsIPv6 = true
|
||||
}
|
||||
}
|
||||
|
||||
// SupportsIPv4 reports whether the platform supports IPv4 networking
|
||||
// functionality.
|
||||
func SupportsIPv4() bool { return supportsIPv4 }
|
||||
|
||||
// SupportsIPv6 reports whether the platform supports IPv6 networking
|
||||
// functionality.
|
||||
func SupportsIPv6() bool { return supportsIPv6 }
|
||||
|
||||
// SupportsRawIPSocket reports whether the platform supports raw IP
|
||||
// sockets.
|
||||
func SupportsRawIPSocket() (string, bool) {
|
||||
return supportsRawIPSocket()
|
||||
}
|
||||
|
||||
// SupportsIPv6MulticastDeliveryOnLoopback reports whether the
|
||||
// platform supports IPv6 multicast packet delivery on software
|
||||
// loopback interface.
|
||||
func SupportsIPv6MulticastDeliveryOnLoopback() bool {
|
||||
return supportsIPv6MulticastDeliveryOnLoopback()
|
||||
}
|
||||
|
||||
// ProtocolNotSupported reports whether err is a protocol not
|
||||
// supported error.
|
||||
func ProtocolNotSupported(err error) bool {
|
||||
return protocolNotSupported(err)
|
||||
}
|
||||
|
||||
// TestableNetwork reports whether network is testable on the current
|
||||
// platform configuration.
|
||||
func TestableNetwork(network string) bool {
|
||||
// This is based on logic from standard library's
|
||||
// net/platform_test.go.
|
||||
switch network {
|
||||
case "unix", "unixgram":
|
||||
switch runtime.GOOS {
|
||||
case "android", "js", "nacl", "plan9", "windows":
|
||||
return false
|
||||
}
|
||||
if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
|
||||
return false
|
||||
}
|
||||
case "unixpacket":
|
||||
switch runtime.GOOS {
|
||||
case "android", "darwin", "freebsd", "js", "nacl", "plan9", "windows":
|
||||
return false
|
||||
case "netbsd":
|
||||
// It passes on amd64 at least. 386 fails (Issue 22927). arm is unknown.
|
||||
if runtime.GOARCH == "386" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// NewLocalListener returns a listener which listens to a loopback IP
|
||||
// address or local file system path.
|
||||
// Network must be "tcp", "tcp4", "tcp6", "unix" or "unixpacket".
|
||||
func NewLocalListener(network string) (net.Listener, error) {
|
||||
switch network {
|
||||
case "tcp":
|
||||
if supportsIPv4 {
|
||||
if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil {
|
||||
return ln, nil
|
||||
}
|
||||
}
|
||||
if supportsIPv6 {
|
||||
return net.Listen("tcp6", "[::1]:0")
|
||||
}
|
||||
case "tcp4":
|
||||
if supportsIPv4 {
|
||||
return net.Listen("tcp4", "127.0.0.1:0")
|
||||
}
|
||||
case "tcp6":
|
||||
if supportsIPv6 {
|
||||
return net.Listen("tcp6", "[::1]:0")
|
||||
}
|
||||
case "unix", "unixpacket":
|
||||
return net.Listen(network, localPath())
|
||||
}
|
||||
return nil, fmt.Errorf("%s is not supported", network)
|
||||
}
|
||||
|
||||
// NewLocalPacketListener returns a packet listener which listens to a
|
||||
// loopback IP address or local file system path.
|
||||
// Network must be "udp", "udp4", "udp6" or "unixgram".
|
||||
func NewLocalPacketListener(network string) (net.PacketConn, error) {
|
||||
switch network {
|
||||
case "udp":
|
||||
if supportsIPv4 {
|
||||
if c, err := net.ListenPacket("udp4", "127.0.0.1:0"); err == nil {
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
if supportsIPv6 {
|
||||
return net.ListenPacket("udp6", "[::1]:0")
|
||||
}
|
||||
case "udp4":
|
||||
if supportsIPv4 {
|
||||
return net.ListenPacket("udp4", "127.0.0.1:0")
|
||||
}
|
||||
case "udp6":
|
||||
if supportsIPv6 {
|
||||
return net.ListenPacket("udp6", "[::1]:0")
|
||||
}
|
||||
case "unixgram":
|
||||
return net.ListenPacket(network, localPath())
|
||||
}
|
||||
return nil, fmt.Errorf("%s is not supported", network)
|
||||
}
|
||||
|
||||
func localPath() string {
|
||||
f, err := ioutil.TempFile("", "nettest")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
path := f.Name()
|
||||
f.Close()
|
||||
os.Remove(path)
|
||||
return path
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"client.go",
|
||||
"socks.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/golang.org/x/net/internal/socks",
|
||||
importpath = "golang.org/x/net/internal/socks",
|
||||
visibility = ["//vendor/golang.org/x/net:__subpackages__"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
|
@ -0,0 +1,168 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package socks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
noDeadline = time.Time{}
|
||||
aLongTimeAgo = time.Unix(1, 0)
|
||||
)
|
||||
|
||||
func (d *Dialer) connect(ctx context.Context, c net.Conn, address string) (_ net.Addr, ctxErr error) {
|
||||
host, port, err := splitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
|
||||
c.SetDeadline(deadline)
|
||||
defer c.SetDeadline(noDeadline)
|
||||
}
|
||||
if ctx != context.Background() {
|
||||
errCh := make(chan error, 1)
|
||||
done := make(chan struct{})
|
||||
defer func() {
|
||||
close(done)
|
||||
if ctxErr == nil {
|
||||
ctxErr = <-errCh
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
c.SetDeadline(aLongTimeAgo)
|
||||
errCh <- ctx.Err()
|
||||
case <-done:
|
||||
errCh <- nil
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
b := make([]byte, 0, 6+len(host)) // the size here is just an estimate
|
||||
b = append(b, Version5)
|
||||
if len(d.AuthMethods) == 0 || d.Authenticate == nil {
|
||||
b = append(b, 1, byte(AuthMethodNotRequired))
|
||||
} else {
|
||||
ams := d.AuthMethods
|
||||
if len(ams) > 255 {
|
||||
return nil, errors.New("too many authentication methods")
|
||||
}
|
||||
b = append(b, byte(len(ams)))
|
||||
for _, am := range ams {
|
||||
b = append(b, byte(am))
|
||||
}
|
||||
}
|
||||
if _, ctxErr = c.Write(b); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, ctxErr = io.ReadFull(c, b[:2]); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
if b[0] != Version5 {
|
||||
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
|
||||
}
|
||||
am := AuthMethod(b[1])
|
||||
if am == AuthMethodNoAcceptableMethods {
|
||||
return nil, errors.New("no acceptable authentication methods")
|
||||
}
|
||||
if d.Authenticate != nil {
|
||||
if ctxErr = d.Authenticate(ctx, c, am); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
b = b[:0]
|
||||
b = append(b, Version5, byte(d.cmd), 0)
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
b = append(b, AddrTypeIPv4)
|
||||
b = append(b, ip4...)
|
||||
} else if ip6 := ip.To16(); ip6 != nil {
|
||||
b = append(b, AddrTypeIPv6)
|
||||
b = append(b, ip6...)
|
||||
} else {
|
||||
return nil, errors.New("unknown address type")
|
||||
}
|
||||
} else {
|
||||
if len(host) > 255 {
|
||||
return nil, errors.New("FQDN too long")
|
||||
}
|
||||
b = append(b, AddrTypeFQDN)
|
||||
b = append(b, byte(len(host)))
|
||||
b = append(b, host...)
|
||||
}
|
||||
b = append(b, byte(port>>8), byte(port))
|
||||
if _, ctxErr = c.Write(b); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, ctxErr = io.ReadFull(c, b[:4]); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
if b[0] != Version5 {
|
||||
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
|
||||
}
|
||||
if cmdErr := Reply(b[1]); cmdErr != StatusSucceeded {
|
||||
return nil, errors.New("unknown error " + cmdErr.String())
|
||||
}
|
||||
if b[2] != 0 {
|
||||
return nil, errors.New("non-zero reserved field")
|
||||
}
|
||||
l := 2
|
||||
var a Addr
|
||||
switch b[3] {
|
||||
case AddrTypeIPv4:
|
||||
l += net.IPv4len
|
||||
a.IP = make(net.IP, net.IPv4len)
|
||||
case AddrTypeIPv6:
|
||||
l += net.IPv6len
|
||||
a.IP = make(net.IP, net.IPv6len)
|
||||
case AddrTypeFQDN:
|
||||
if _, err := io.ReadFull(c, b[:1]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l += int(b[0])
|
||||
default:
|
||||
return nil, errors.New("unknown address type " + strconv.Itoa(int(b[3])))
|
||||
}
|
||||
if cap(b) < l {
|
||||
b = make([]byte, l)
|
||||
} else {
|
||||
b = b[:l]
|
||||
}
|
||||
if _, ctxErr = io.ReadFull(c, b); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
if a.IP != nil {
|
||||
copy(a.IP, b)
|
||||
} else {
|
||||
a.Name = string(b[:len(b)-2])
|
||||
}
|
||||
a.Port = int(b[len(b)-2])<<8 | int(b[len(b)-1])
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
func splitHostPort(address string) (string, int, error) {
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
portnum, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
if 1 > portnum || portnum > 0xffff {
|
||||
return "", 0, errors.New("port number out of range " + port)
|
||||
}
|
||||
return host, portnum, nil
|
||||
}
|
|
@ -0,0 +1,316 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package socks provides a SOCKS version 5 client implementation.
|
||||
//
|
||||
// SOCKS protocol version 5 is defined in RFC 1928.
|
||||
// Username/Password authentication for SOCKS version 5 is defined in
|
||||
// RFC 1929.
|
||||
package socks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A Command represents a SOCKS command.
|
||||
type Command int
|
||||
|
||||
func (cmd Command) String() string {
|
||||
switch cmd {
|
||||
case CmdConnect:
|
||||
return "socks connect"
|
||||
case cmdBind:
|
||||
return "socks bind"
|
||||
default:
|
||||
return "socks " + strconv.Itoa(int(cmd))
|
||||
}
|
||||
}
|
||||
|
||||
// An AuthMethod represents a SOCKS authentication method.
|
||||
type AuthMethod int
|
||||
|
||||
// A Reply represents a SOCKS command reply code.
|
||||
type Reply int
|
||||
|
||||
func (code Reply) String() string {
|
||||
switch code {
|
||||
case StatusSucceeded:
|
||||
return "succeeded"
|
||||
case 0x01:
|
||||
return "general SOCKS server failure"
|
||||
case 0x02:
|
||||
return "connection not allowed by ruleset"
|
||||
case 0x03:
|
||||
return "network unreachable"
|
||||
case 0x04:
|
||||
return "host unreachable"
|
||||
case 0x05:
|
||||
return "connection refused"
|
||||
case 0x06:
|
||||
return "TTL expired"
|
||||
case 0x07:
|
||||
return "command not supported"
|
||||
case 0x08:
|
||||
return "address type not supported"
|
||||
default:
|
||||
return "unknown code: " + strconv.Itoa(int(code))
|
||||
}
|
||||
}
|
||||
|
||||
// Wire protocol constants.
|
||||
const (
|
||||
Version5 = 0x05
|
||||
|
||||
AddrTypeIPv4 = 0x01
|
||||
AddrTypeFQDN = 0x03
|
||||
AddrTypeIPv6 = 0x04
|
||||
|
||||
CmdConnect Command = 0x01 // establishes an active-open forward proxy connection
|
||||
cmdBind Command = 0x02 // establishes a passive-open forward proxy connection
|
||||
|
||||
AuthMethodNotRequired AuthMethod = 0x00 // no authentication required
|
||||
AuthMethodUsernamePassword AuthMethod = 0x02 // use username/password
|
||||
AuthMethodNoAcceptableMethods AuthMethod = 0xff // no acceptable authentication methods
|
||||
|
||||
StatusSucceeded Reply = 0x00
|
||||
)
|
||||
|
||||
// An Addr represents a SOCKS-specific address.
|
||||
// Either Name or IP is used exclusively.
|
||||
type Addr struct {
|
||||
Name string // fully-qualified domain name
|
||||
IP net.IP
|
||||
Port int
|
||||
}
|
||||
|
||||
func (a *Addr) Network() string { return "socks" }
|
||||
|
||||
func (a *Addr) String() string {
|
||||
if a == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
port := strconv.Itoa(a.Port)
|
||||
if a.IP == nil {
|
||||
return net.JoinHostPort(a.Name, port)
|
||||
}
|
||||
return net.JoinHostPort(a.IP.String(), port)
|
||||
}
|
||||
|
||||
// A Conn represents a forward proxy connection.
|
||||
type Conn struct {
|
||||
net.Conn
|
||||
|
||||
boundAddr net.Addr
|
||||
}
|
||||
|
||||
// BoundAddr returns the address assigned by the proxy server for
|
||||
// connecting to the command target address from the proxy server.
|
||||
func (c *Conn) BoundAddr() net.Addr {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.boundAddr
|
||||
}
|
||||
|
||||
// A Dialer holds SOCKS-specific options.
|
||||
type Dialer struct {
|
||||
cmd Command // either CmdConnect or cmdBind
|
||||
proxyNetwork string // network between a proxy server and a client
|
||||
proxyAddress string // proxy server address
|
||||
|
||||
// ProxyDial specifies the optional dial function for
|
||||
// establishing the transport connection.
|
||||
ProxyDial func(context.Context, string, string) (net.Conn, error)
|
||||
|
||||
// AuthMethods specifies the list of request authention
|
||||
// methods.
|
||||
// If empty, SOCKS client requests only AuthMethodNotRequired.
|
||||
AuthMethods []AuthMethod
|
||||
|
||||
// Authenticate specifies the optional authentication
|
||||
// function. It must be non-nil when AuthMethods is not empty.
|
||||
// It must return an error when the authentication is failed.
|
||||
Authenticate func(context.Context, io.ReadWriter, AuthMethod) error
|
||||
}
|
||||
|
||||
// DialContext connects to the provided address on the provided
|
||||
// network.
|
||||
//
|
||||
// The returned error value may be a net.OpError. When the Op field of
|
||||
// net.OpError contains "socks", the Source field contains a proxy
|
||||
// server address and the Addr field contains a command target
|
||||
// address.
|
||||
//
|
||||
// See func Dial of the net package of standard library for a
|
||||
// description of the network and address parameters.
|
||||
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
if err := d.validateTarget(network, address); err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
if ctx == nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
|
||||
}
|
||||
var err error
|
||||
var c net.Conn
|
||||
if d.ProxyDial != nil {
|
||||
c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress)
|
||||
} else {
|
||||
var dd net.Dialer
|
||||
c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress)
|
||||
}
|
||||
if err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
a, err := d.connect(ctx, c, address)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
return &Conn{Conn: c, boundAddr: a}, nil
|
||||
}
|
||||
|
||||
// DialWithConn initiates a connection from SOCKS server to the target
|
||||
// network and address using the connection c that is already
|
||||
// connected to the SOCKS server.
|
||||
//
|
||||
// It returns the connection's local address assigned by the SOCKS
|
||||
// server.
|
||||
func (d *Dialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) {
|
||||
if err := d.validateTarget(network, address); err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
if ctx == nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
|
||||
}
|
||||
a, err := d.connect(ctx, c, address)
|
||||
if err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Dial connects to the provided address on the provided network.
|
||||
//
|
||||
// Unlike DialContext, it returns a raw transport connection instead
|
||||
// of a forward proxy connection.
|
||||
//
|
||||
// Deprecated: Use DialContext or DialWithConn instead.
|
||||
func (d *Dialer) Dial(network, address string) (net.Conn, error) {
|
||||
if err := d.validateTarget(network, address); err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
var err error
|
||||
var c net.Conn
|
||||
if d.ProxyDial != nil {
|
||||
c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress)
|
||||
} else {
|
||||
c, err = net.Dial(d.proxyNetwork, d.proxyAddress)
|
||||
}
|
||||
if err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (d *Dialer) validateTarget(network, address string) error {
|
||||
switch network {
|
||||
case "tcp", "tcp6", "tcp4":
|
||||
default:
|
||||
return errors.New("network not implemented")
|
||||
}
|
||||
switch d.cmd {
|
||||
case CmdConnect, cmdBind:
|
||||
default:
|
||||
return errors.New("command not implemented")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Dialer) pathAddrs(address string) (proxy, dst net.Addr, err error) {
|
||||
for i, s := range []string{d.proxyAddress, address} {
|
||||
host, port, err := splitHostPort(s)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
a := &Addr{Port: port}
|
||||
a.IP = net.ParseIP(host)
|
||||
if a.IP == nil {
|
||||
a.Name = host
|
||||
}
|
||||
if i == 0 {
|
||||
proxy = a
|
||||
} else {
|
||||
dst = a
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewDialer returns a new Dialer that dials through the provided
|
||||
// proxy server's network and address.
|
||||
func NewDialer(network, address string) *Dialer {
|
||||
return &Dialer{proxyNetwork: network, proxyAddress: address, cmd: CmdConnect}
|
||||
}
|
||||
|
||||
const (
|
||||
authUsernamePasswordVersion = 0x01
|
||||
authStatusSucceeded = 0x00
|
||||
)
|
||||
|
||||
// UsernamePassword are the credentials for the username/password
|
||||
// authentication method.
|
||||
type UsernamePassword struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// Authenticate authenticates a pair of username and password with the
|
||||
// proxy server.
|
||||
func (up *UsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth AuthMethod) error {
|
||||
switch auth {
|
||||
case AuthMethodNotRequired:
|
||||
return nil
|
||||
case AuthMethodUsernamePassword:
|
||||
if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) == 0 || len(up.Password) > 255 {
|
||||
return errors.New("invalid username/password")
|
||||
}
|
||||
b := []byte{authUsernamePasswordVersion}
|
||||
b = append(b, byte(len(up.Username)))
|
||||
b = append(b, up.Username...)
|
||||
b = append(b, byte(len(up.Password)))
|
||||
b = append(b, up.Password...)
|
||||
// TODO(mikio): handle IO deadlines and cancelation if
|
||||
// necessary
|
||||
if _, err := rw.Write(b); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.ReadFull(rw, b[:2]); err != nil {
|
||||
return err
|
||||
}
|
||||
if b[0] != authUsernamePasswordVersion {
|
||||
return errors.New("invalid username/password version")
|
||||
}
|
||||
if b[1] != authStatusSucceeded {
|
||||
return errors.New("username/password authentication failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return errors.New("unsupported authentication method " + strconv.Itoa(int(auth)))
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["server.go"],
|
||||
importmap = "k8s.io/kubernetes/vendor/golang.org/x/net/internal/sockstest",
|
||||
importpath = "golang.org/x/net/internal/sockstest",
|
||||
visibility = ["//vendor/golang.org/x/net:__subpackages__"],
|
||||
deps = [
|
||||
"//vendor/golang.org/x/net/internal/nettest:go_default_library",
|
||||
"//vendor/golang.org/x/net/internal/socks:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
|
@ -0,0 +1,241 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package sockstest provides utilities for SOCKS testing.
|
||||
package sockstest
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"golang.org/x/net/internal/nettest"
|
||||
"golang.org/x/net/internal/socks"
|
||||
)
|
||||
|
||||
// An AuthRequest represents an authentication request.
|
||||
type AuthRequest struct {
|
||||
Version int
|
||||
Methods []socks.AuthMethod
|
||||
}
|
||||
|
||||
// ParseAuthRequest parses an authentication request.
|
||||
func ParseAuthRequest(b []byte) (*AuthRequest, error) {
|
||||
if len(b) < 2 {
|
||||
return nil, errors.New("short auth request")
|
||||
}
|
||||
if b[0] != socks.Version5 {
|
||||
return nil, errors.New("unexpected protocol version")
|
||||
}
|
||||
if len(b)-2 < int(b[1]) {
|
||||
return nil, errors.New("short auth request")
|
||||
}
|
||||
req := &AuthRequest{Version: int(b[0])}
|
||||
if b[1] > 0 {
|
||||
req.Methods = make([]socks.AuthMethod, b[1])
|
||||
for i, m := range b[2 : 2+b[1]] {
|
||||
req.Methods[i] = socks.AuthMethod(m)
|
||||
}
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// MarshalAuthReply returns an authentication reply in wire format.
|
||||
func MarshalAuthReply(ver int, m socks.AuthMethod) ([]byte, error) {
|
||||
return []byte{byte(ver), byte(m)}, nil
|
||||
}
|
||||
|
||||
// A CmdRequest repesents a command request.
|
||||
type CmdRequest struct {
|
||||
Version int
|
||||
Cmd socks.Command
|
||||
Addr socks.Addr
|
||||
}
|
||||
|
||||
// ParseCmdRequest parses a command request.
|
||||
func ParseCmdRequest(b []byte) (*CmdRequest, error) {
|
||||
if len(b) < 7 {
|
||||
return nil, errors.New("short cmd request")
|
||||
}
|
||||
if b[0] != socks.Version5 {
|
||||
return nil, errors.New("unexpected protocol version")
|
||||
}
|
||||
if socks.Command(b[1]) != socks.CmdConnect {
|
||||
return nil, errors.New("unexpected command")
|
||||
}
|
||||
if b[2] != 0 {
|
||||
return nil, errors.New("non-zero reserved field")
|
||||
}
|
||||
req := &CmdRequest{Version: int(b[0]), Cmd: socks.Command(b[1])}
|
||||
l := 2
|
||||
off := 4
|
||||
switch b[3] {
|
||||
case socks.AddrTypeIPv4:
|
||||
l += net.IPv4len
|
||||
req.Addr.IP = make(net.IP, net.IPv4len)
|
||||
case socks.AddrTypeIPv6:
|
||||
l += net.IPv6len
|
||||
req.Addr.IP = make(net.IP, net.IPv6len)
|
||||
case socks.AddrTypeFQDN:
|
||||
l += int(b[4])
|
||||
off = 5
|
||||
default:
|
||||
return nil, errors.New("unknown address type")
|
||||
}
|
||||
if len(b[off:]) < l {
|
||||
return nil, errors.New("short cmd request")
|
||||
}
|
||||
if req.Addr.IP != nil {
|
||||
copy(req.Addr.IP, b[off:])
|
||||
} else {
|
||||
req.Addr.Name = string(b[off : off+l-2])
|
||||
}
|
||||
req.Addr.Port = int(b[off+l-2])<<8 | int(b[off+l-1])
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// MarshalCmdReply returns a command reply in wire format.
|
||||
func MarshalCmdReply(ver int, reply socks.Reply, a *socks.Addr) ([]byte, error) {
|
||||
b := make([]byte, 4)
|
||||
b[0] = byte(ver)
|
||||
b[1] = byte(reply)
|
||||
if a.Name != "" {
|
||||
if len(a.Name) > 255 {
|
||||
return nil, errors.New("fqdn too long")
|
||||
}
|
||||
b[3] = socks.AddrTypeFQDN
|
||||
b = append(b, byte(len(a.Name)))
|
||||
b = append(b, a.Name...)
|
||||
} else if ip4 := a.IP.To4(); ip4 != nil {
|
||||
b[3] = socks.AddrTypeIPv4
|
||||
b = append(b, ip4...)
|
||||
} else if ip6 := a.IP.To16(); ip6 != nil {
|
||||
b[3] = socks.AddrTypeIPv6
|
||||
b = append(b, ip6...)
|
||||
} else {
|
||||
return nil, errors.New("unknown address type")
|
||||
}
|
||||
b = append(b, byte(a.Port>>8), byte(a.Port))
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// A Server repesents a server for handshake testing.
|
||||
type Server struct {
|
||||
ln net.Listener
|
||||
}
|
||||
|
||||
// Addr rerurns a server address.
|
||||
func (s *Server) Addr() net.Addr {
|
||||
return s.ln.Addr()
|
||||
}
|
||||
|
||||
// TargetAddr returns a fake final destination address.
|
||||
//
|
||||
// The returned address is only valid for testing with Server.
|
||||
func (s *Server) TargetAddr() net.Addr {
|
||||
a := s.ln.Addr()
|
||||
switch a := a.(type) {
|
||||
case *net.TCPAddr:
|
||||
if a.IP.To4() != nil {
|
||||
return &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 5963}
|
||||
}
|
||||
if a.IP.To16() != nil && a.IP.To4() == nil {
|
||||
return &net.TCPAddr{IP: net.IPv6loopback, Port: 5963}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the server.
|
||||
func (s *Server) Close() error {
|
||||
return s.ln.Close()
|
||||
}
|
||||
|
||||
func (s *Server) serve(authFunc, cmdFunc func(io.ReadWriter, []byte) error) {
|
||||
c, err := s.ln.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
go s.serve(authFunc, cmdFunc)
|
||||
b := make([]byte, 512)
|
||||
n, err := c.Read(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err := authFunc(c, b[:n]); err != nil {
|
||||
return
|
||||
}
|
||||
n, err = c.Read(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err := cmdFunc(c, b[:n]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// NewServer returns a new server.
|
||||
//
|
||||
// The provided authFunc and cmdFunc must parse requests and return
|
||||
// appropriate replies to clients.
|
||||
func NewServer(authFunc, cmdFunc func(io.ReadWriter, []byte) error) (*Server, error) {
|
||||
var err error
|
||||
s := new(Server)
|
||||
s.ln, err = nettest.NewLocalListener("tcp")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go s.serve(authFunc, cmdFunc)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// NoAuthRequired handles a no-authentication-required signaling.
|
||||
func NoAuthRequired(rw io.ReadWriter, b []byte) error {
|
||||
req, err := ParseAuthRequest(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err = MarshalAuthReply(req.Version, socks.AuthMethodNotRequired)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, err := rw.Write(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != len(b) {
|
||||
return errors.New("short write")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NoProxyRequired handles a command signaling without constructing a
|
||||
// proxy connection to the final destination.
|
||||
func NoProxyRequired(rw io.ReadWriter, b []byte) error {
|
||||
req, err := ParseCmdRequest(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Addr.Port += 1
|
||||
if req.Addr.Name != "" {
|
||||
req.Addr.Name = "boundaddr.doesnotexist"
|
||||
} else if req.Addr.IP.To4() != nil {
|
||||
req.Addr.IP = net.IPv4(127, 0, 0, 1)
|
||||
} else {
|
||||
req.Addr.IP = net.IPv6loopback
|
||||
}
|
||||
b, err = MarshalCmdReply(socks.Version5, socks.StatusSucceeded, &req.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, err := rw.Write(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != len(b) {
|
||||
return errors.New("short write")
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -11,6 +11,7 @@ go_library(
|
|||
importmap = "k8s.io/kubernetes/vendor/golang.org/x/net/proxy",
|
||||
importpath = "golang.org/x/net/proxy",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//vendor/golang.org/x/net/internal/socks:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
|
|
|
@ -5,210 +5,32 @@
|
|||
package proxy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"context"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/net/internal/socks"
|
||||
)
|
||||
|
||||
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
|
||||
// with an optional username and password. See RFC 1928 and RFC 1929.
|
||||
func SOCKS5(network, addr string, auth *Auth, forward Dialer) (Dialer, error) {
|
||||
s := &socks5{
|
||||
network: network,
|
||||
addr: addr,
|
||||
forward: forward,
|
||||
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given
|
||||
// address with an optional username and password.
|
||||
// See RFC 1928 and RFC 1929.
|
||||
func SOCKS5(network, address string, auth *Auth, forward Dialer) (Dialer, error) {
|
||||
d := socks.NewDialer(network, address)
|
||||
if forward != nil {
|
||||
d.ProxyDial = func(_ context.Context, network string, address string) (net.Conn, error) {
|
||||
return forward.Dial(network, address)
|
||||
}
|
||||
}
|
||||
if auth != nil {
|
||||
s.user = auth.User
|
||||
s.password = auth.Password
|
||||
up := socks.UsernamePassword{
|
||||
Username: auth.User,
|
||||
Password: auth.Password,
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
type socks5 struct {
|
||||
user, password string
|
||||
network, addr string
|
||||
forward Dialer
|
||||
}
|
||||
|
||||
const socks5Version = 5
|
||||
|
||||
const (
|
||||
socks5AuthNone = 0
|
||||
socks5AuthPassword = 2
|
||||
)
|
||||
|
||||
const socks5Connect = 1
|
||||
|
||||
const (
|
||||
socks5IP4 = 1
|
||||
socks5Domain = 3
|
||||
socks5IP6 = 4
|
||||
)
|
||||
|
||||
var socks5Errors = []string{
|
||||
"",
|
||||
"general failure",
|
||||
"connection forbidden",
|
||||
"network unreachable",
|
||||
"host unreachable",
|
||||
"connection refused",
|
||||
"TTL expired",
|
||||
"command not supported",
|
||||
"address type not supported",
|
||||
}
|
||||
|
||||
// Dial connects to the address addr on the given network via the SOCKS5 proxy.
|
||||
func (s *socks5) Dial(network, addr string) (net.Conn, error) {
|
||||
switch network {
|
||||
case "tcp", "tcp6", "tcp4":
|
||||
default:
|
||||
return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
|
||||
}
|
||||
|
||||
conn, err := s.forward.Dial(s.network, s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.connect(conn, addr); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// connect takes an existing connection to a socks5 proxy server,
|
||||
// and commands the server to extend that connection to target,
|
||||
// which must be a canonical address with a host and port.
|
||||
func (s *socks5) connect(conn net.Conn, target string) error {
|
||||
host, portStr, err := net.SplitHostPort(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to parse port number: " + portStr)
|
||||
}
|
||||
if port < 1 || port > 0xffff {
|
||||
return errors.New("proxy: port number out of range: " + portStr)
|
||||
}
|
||||
|
||||
// the size here is just an estimate
|
||||
buf := make([]byte, 0, 6+len(host))
|
||||
|
||||
buf = append(buf, socks5Version)
|
||||
if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
|
||||
buf = append(buf, 2 /* num auth methods */, socks5AuthNone, socks5AuthPassword)
|
||||
} else {
|
||||
buf = append(buf, 1 /* num auth methods */, socks5AuthNone)
|
||||
}
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
if buf[0] != 5 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
if buf[1] == 0xff {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
|
||||
}
|
||||
|
||||
// See RFC 1929
|
||||
if buf[1] == socks5AuthPassword {
|
||||
buf = buf[:0]
|
||||
buf = append(buf, 1 /* password protocol version */)
|
||||
buf = append(buf, uint8(len(s.user)))
|
||||
buf = append(buf, s.user...)
|
||||
buf = append(buf, uint8(len(s.password)))
|
||||
buf = append(buf, s.password...)
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if buf[1] != 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
|
||||
}
|
||||
}
|
||||
|
||||
buf = buf[:0]
|
||||
buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */)
|
||||
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
buf = append(buf, socks5IP4)
|
||||
ip = ip4
|
||||
} else {
|
||||
buf = append(buf, socks5IP6)
|
||||
}
|
||||
buf = append(buf, ip...)
|
||||
} else {
|
||||
if len(host) > 255 {
|
||||
return errors.New("proxy: destination host name too long: " + host)
|
||||
}
|
||||
buf = append(buf, socks5Domain)
|
||||
buf = append(buf, byte(len(host)))
|
||||
buf = append(buf, host...)
|
||||
}
|
||||
buf = append(buf, byte(port>>8), byte(port))
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:4]); err != nil {
|
||||
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
failure := "unknown error"
|
||||
if int(buf[1]) < len(socks5Errors) {
|
||||
failure = socks5Errors[buf[1]]
|
||||
}
|
||||
|
||||
if len(failure) > 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
|
||||
}
|
||||
|
||||
bytesToDiscard := 0
|
||||
switch buf[3] {
|
||||
case socks5IP4:
|
||||
bytesToDiscard = net.IPv4len
|
||||
case socks5IP6:
|
||||
bytesToDiscard = net.IPv6len
|
||||
case socks5Domain:
|
||||
_, err := io.ReadFull(conn, buf[:1])
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
bytesToDiscard = int(buf[0])
|
||||
default:
|
||||
return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
|
||||
}
|
||||
|
||||
if cap(buf) < bytesToDiscard {
|
||||
buf = make([]byte, bytesToDiscard)
|
||||
} else {
|
||||
buf = buf[:bytesToDiscard]
|
||||
}
|
||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
||||
return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
// Also need to discard the port number
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
d.AuthMethods = []socks.AuthMethod{
|
||||
socks.AuthMethodNotRequired,
|
||||
socks.AuthMethodUsernamePassword,
|
||||
}
|
||||
d.Authenticate = up.Authenticate
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ import (
|
|||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
@ -110,6 +111,13 @@ var AuthRequest = func(req *http.Request) (any, sensitive bool) {
|
|||
}
|
||||
|
||||
func init() {
|
||||
_, pat := http.DefaultServeMux.Handler(&http.Request{URL: &url.URL{Path: "/debug/requests"}})
|
||||
if pat != "" {
|
||||
panic("/debug/requests is already registered. You may have two independent copies of " +
|
||||
"golang.org/x/net/trace in your binary, trying to maintain separate state. This may " +
|
||||
"involve a vendored copy of golang.org/x/net/trace.")
|
||||
}
|
||||
|
||||
// TODO(jbd): Serve Traces from /debug/traces in the future?
|
||||
// There is no requirement for a request to be present to have traces.
|
||||
http.HandleFunc("/debug/requests", Traces)
|
||||
|
@ -368,7 +376,11 @@ func New(family, title string) Trace {
|
|||
}
|
||||
|
||||
func (tr *trace) Finish() {
|
||||
tr.Elapsed = time.Now().Sub(tr.Start)
|
||||
elapsed := time.Now().Sub(tr.Start)
|
||||
tr.mu.Lock()
|
||||
tr.Elapsed = elapsed
|
||||
tr.mu.Unlock()
|
||||
|
||||
if DebugUseAfterFinish {
|
||||
buf := make([]byte, 4<<10) // 4 KB should be enough
|
||||
n := runtime.Stack(buf, false)
|
||||
|
@ -381,14 +393,17 @@ func (tr *trace) Finish() {
|
|||
m.Remove(tr)
|
||||
|
||||
f := getFamily(tr.Family, true)
|
||||
tr.mu.RLock() // protects tr fields in Cond.match calls
|
||||
for _, b := range f.Buckets {
|
||||
if b.Cond.match(tr) {
|
||||
b.Add(tr)
|
||||
}
|
||||
}
|
||||
tr.mu.RUnlock()
|
||||
|
||||
// Add a sample of elapsed time as microseconds to the family's timeseries
|
||||
h := new(histogram)
|
||||
h.addMeasurement(tr.Elapsed.Nanoseconds() / 1e3)
|
||||
h.addMeasurement(elapsed.Nanoseconds() / 1e3)
|
||||
f.LatencyMu.Lock()
|
||||
f.Latency.Add(h)
|
||||
f.LatencyMu.Unlock()
|
||||
|
@ -684,24 +699,19 @@ type trace struct {
|
|||
// Title is the title of this trace.
|
||||
Title string
|
||||
|
||||
// Timing information.
|
||||
// Start time of the this trace.
|
||||
Start time.Time
|
||||
Elapsed time.Duration // zero while active
|
||||
|
||||
// Trace information if non-zero.
|
||||
traceID uint64
|
||||
mu sync.RWMutex
|
||||
events []event // Append-only sequence of events (modulo discards).
|
||||
maxEvents int
|
||||
recycler func(interface{})
|
||||
IsError bool // Whether this trace resulted in an error.
|
||||
Elapsed time.Duration // Elapsed time for this trace, zero while active.
|
||||
traceID uint64 // Trace information if non-zero.
|
||||
spanID uint64
|
||||
|
||||
// Whether this trace resulted in an error.
|
||||
IsError bool
|
||||
|
||||
// Append-only sequence of events (modulo discards).
|
||||
mu sync.RWMutex
|
||||
events []event
|
||||
maxEvents int
|
||||
|
||||
refs int32 // how many buckets this is in
|
||||
recycler func(interface{})
|
||||
disc discarded // scratch space to avoid allocation
|
||||
|
||||
finishStack []byte // where finish was called, if DebugUseAfterFinish is set
|
||||
|
@ -714,14 +724,18 @@ func (tr *trace) reset() {
|
|||
tr.Family = ""
|
||||
tr.Title = ""
|
||||
tr.Start = time.Time{}
|
||||
|
||||
tr.mu.Lock()
|
||||
tr.Elapsed = 0
|
||||
tr.traceID = 0
|
||||
tr.spanID = 0
|
||||
tr.IsError = false
|
||||
tr.maxEvents = 0
|
||||
tr.events = nil
|
||||
tr.refs = 0
|
||||
tr.recycler = nil
|
||||
tr.mu.Unlock()
|
||||
|
||||
tr.refs = 0
|
||||
tr.disc = 0
|
||||
tr.finishStack = nil
|
||||
for i := range tr.eventsBuf {
|
||||
|
@ -801,21 +815,31 @@ func (tr *trace) LazyPrintf(format string, a ...interface{}) {
|
|||
tr.addEvent(&lazySprintf{format, a}, false, false)
|
||||
}
|
||||
|
||||
func (tr *trace) SetError() { tr.IsError = true }
|
||||
func (tr *trace) SetError() {
|
||||
tr.mu.Lock()
|
||||
tr.IsError = true
|
||||
tr.mu.Unlock()
|
||||
}
|
||||
|
||||
func (tr *trace) SetRecycler(f func(interface{})) {
|
||||
tr.mu.Lock()
|
||||
tr.recycler = f
|
||||
tr.mu.Unlock()
|
||||
}
|
||||
|
||||
func (tr *trace) SetTraceInfo(traceID, spanID uint64) {
|
||||
tr.mu.Lock()
|
||||
tr.traceID, tr.spanID = traceID, spanID
|
||||
tr.mu.Unlock()
|
||||
}
|
||||
|
||||
func (tr *trace) SetMaxEvents(m int) {
|
||||
tr.mu.Lock()
|
||||
// Always keep at least three events: first, discarded count, last.
|
||||
if len(tr.events) == 0 && m > 3 {
|
||||
tr.maxEvents = m
|
||||
}
|
||||
tr.mu.Unlock()
|
||||
}
|
||||
|
||||
func (tr *trace) ref() {
|
||||
|
@ -824,6 +848,7 @@ func (tr *trace) ref() {
|
|||
|
||||
func (tr *trace) unref() {
|
||||
if atomic.AddInt32(&tr.refs, -1) == 0 {
|
||||
tr.mu.RLock()
|
||||
if tr.recycler != nil {
|
||||
// freeTrace clears tr, so we hold tr.recycler and tr.events here.
|
||||
go func(f func(interface{}), es []event) {
|
||||
|
@ -834,6 +859,7 @@ func (tr *trace) unref() {
|
|||
}
|
||||
}(tr.recycler, tr.events)
|
||||
}
|
||||
tr.mu.RUnlock()
|
||||
|
||||
freeTrace(tr)
|
||||
}
|
||||
|
@ -844,7 +870,10 @@ func (tr *trace) When() string {
|
|||
}
|
||||
|
||||
func (tr *trace) ElapsedTime() string {
|
||||
tr.mu.RLock()
|
||||
t := tr.Elapsed
|
||||
tr.mu.RUnlock()
|
||||
|
||||
if t == 0 {
|
||||
// Active trace.
|
||||
t = time.Since(tr.Start)
|
||||
|
|
|
@ -241,7 +241,10 @@ func (ws *Conn) Close() error {
|
|||
return err1
|
||||
}
|
||||
|
||||
// IsClientConn reports whether ws is a client-side connection.
|
||||
func (ws *Conn) IsClientConn() bool { return ws.request == nil }
|
||||
|
||||
// IsServerConn reports whether ws is a server-side connection.
|
||||
func (ws *Conn) IsServerConn() bool { return ws.request != nil }
|
||||
|
||||
// LocalAddr returns the WebSocket Origin for the connection for client, or
|
||||
|
|
Loading…
Reference in New Issue