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",
|
"GoVersion": "go1.12",
|
||||||
"GodepVersion": "v80-k8s-r1",
|
"GodepVersion": "v80-k8s-r1",
|
||||||
"Packages": [
|
"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/bazel-gazelle/cmd/gazelle",
|
||||||
"github.com/bazelbuild/buildtools/buildozer",
|
"github.com/bazelbuild/buildtools/buildozer",
|
||||||
"github.com/cespare/prettybench",
|
"github.com/cespare/prettybench",
|
||||||
|
@ -2385,8 +2388,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/grpc-ecosystem/go-grpc-middleware",
|
"ImportPath": "github.com/grpc-ecosystem/go-grpc-middleware",
|
||||||
"Comment": "v1.0.0-4-g498ae206fc3cfe",
|
"Comment": "v1.0.0-8-gcfaf5686ec79ff",
|
||||||
"Rev": "498ae206fc3cfe81cd82e48c1d4354026fa5f9ec"
|
"Rev": "cfaf5686ec79ff8344257723b6f5ba1ae0ffeb4d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/grpc-ecosystem/go-grpc-prometheus",
|
"ImportPath": "github.com/grpc-ecosystem/go-grpc-prometheus",
|
||||||
|
@ -3622,19 +3625,23 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context",
|
"ImportPath": "golang.org/x/net/context",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context/ctxhttp",
|
"ImportPath": "golang.org/x/net/context/ctxhttp",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/html",
|
"ImportPath": "golang.org/x/net/html",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/html/atom",
|
"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",
|
"ImportPath": "golang.org/x/net/html/charset",
|
||||||
|
@ -3642,35 +3649,43 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"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",
|
"ImportPath": "golang.org/x/net/internal/timeseries",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/proxy",
|
"ImportPath": "golang.org/x/net/proxy",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/trace",
|
"ImportPath": "golang.org/x/net/trace",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/websocket",
|
"ImportPath": "golang.org/x/net/websocket",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/oauth2",
|
"ImportPath": "golang.org/x/oauth2",
|
||||||
|
|
|
@ -54,21 +54,21 @@
|
||||||
"ImportPath": "github.com/stretchr/testify/require",
|
"ImportPath": "github.com/stretchr/testify/require",
|
||||||
"Rev": "c679ae2cc0cb27ec3293fea7e254e47386f05d69"
|
"Rev": "c679ae2cc0cb27ec3293fea7e254e47386f05d69"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||||
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||||
|
|
|
@ -488,7 +488,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/grpc-ecosystem/go-grpc-middleware",
|
"ImportPath": "github.com/grpc-ecosystem/go-grpc-middleware",
|
||||||
"Rev": "498ae206fc3cfe81cd82e48c1d4354026fa5f9ec"
|
"Rev": "cfaf5686ec79ff8344257723b6f5ba1ae0ffeb4d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/grpc-ecosystem/go-grpc-prometheus",
|
"ImportPath": "github.com/grpc-ecosystem/go-grpc-prometheus",
|
||||||
|
@ -688,35 +688,35 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context",
|
"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",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/internal/timeseries",
|
"ImportPath": "golang.org/x/net/internal/timeseries",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/trace",
|
"ImportPath": "golang.org/x/net/trace",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/websocket",
|
"ImportPath": "golang.org/x/net/websocket",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/oauth2",
|
"ImportPath": "golang.org/x/oauth2",
|
||||||
|
|
|
@ -120,31 +120,31 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/html",
|
"ImportPath": "golang.org/x/net/html",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/html/atom",
|
"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",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/websocket",
|
"ImportPath": "golang.org/x/net/websocket",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||||
|
|
|
@ -468,7 +468,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/grpc-ecosystem/go-grpc-middleware",
|
"ImportPath": "github.com/grpc-ecosystem/go-grpc-middleware",
|
||||||
"Rev": "498ae206fc3cfe81cd82e48c1d4354026fa5f9ec"
|
"Rev": "cfaf5686ec79ff8344257723b6f5ba1ae0ffeb4d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/grpc-ecosystem/go-grpc-prometheus",
|
"ImportPath": "github.com/grpc-ecosystem/go-grpc-prometheus",
|
||||||
|
@ -696,35 +696,35 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context",
|
"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",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/internal/timeseries",
|
"ImportPath": "golang.org/x/net/internal/timeseries",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/trace",
|
"ImportPath": "golang.org/x/net/trace",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/websocket",
|
"ImportPath": "golang.org/x/net/websocket",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/oauth2",
|
"ImportPath": "golang.org/x/oauth2",
|
||||||
|
|
|
@ -168,23 +168,23 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context",
|
"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",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/oauth2",
|
"ImportPath": "golang.org/x/oauth2",
|
||||||
|
|
|
@ -192,27 +192,27 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context",
|
"ImportPath": "golang.org/x/net/context",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context/ctxhttp",
|
"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",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/oauth2",
|
"ImportPath": "golang.org/x/oauth2",
|
||||||
|
|
|
@ -84,23 +84,23 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context",
|
"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",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/oauth2",
|
"ImportPath": "golang.org/x/oauth2",
|
||||||
|
|
|
@ -18,21 +18,21 @@
|
||||||
"ImportPath": "github.com/google/gofuzz",
|
"ImportPath": "github.com/google/gofuzz",
|
||||||
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||||
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||||
|
|
|
@ -22,21 +22,21 @@
|
||||||
"ImportPath": "github.com/spf13/pflag",
|
"ImportPath": "github.com/spf13/pflag",
|
||||||
"Rev": "583c0c0531f06d5278b7d917446061adc344b5cd"
|
"Rev": "583c0c0531f06d5278b7d917446061adc344b5cd"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||||
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||||
|
|
|
@ -84,23 +84,23 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context",
|
"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",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/oauth2",
|
"ImportPath": "golang.org/x/oauth2",
|
||||||
|
|
|
@ -18,21 +18,21 @@
|
||||||
"ImportPath": "github.com/google/gofuzz",
|
"ImportPath": "github.com/google/gofuzz",
|
||||||
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||||
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||||
|
|
|
@ -268,43 +268,43 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context",
|
"ImportPath": "golang.org/x/net/context",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/html",
|
"ImportPath": "golang.org/x/net/html",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/html/atom",
|
"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",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/internal/timeseries",
|
"ImportPath": "golang.org/x/net/internal/timeseries",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/trace",
|
"ImportPath": "golang.org/x/net/trace",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/websocket",
|
"ImportPath": "golang.org/x/net/websocket",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/oauth2",
|
"ImportPath": "golang.org/x/oauth2",
|
||||||
|
|
|
@ -18,21 +18,21 @@
|
||||||
"ImportPath": "github.com/google/gofuzz",
|
"ImportPath": "github.com/google/gofuzz",
|
||||||
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||||
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||||
|
|
|
@ -18,21 +18,21 @@
|
||||||
"ImportPath": "github.com/google/gofuzz",
|
"ImportPath": "github.com/google/gofuzz",
|
||||||
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||||
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||||
|
|
|
@ -18,21 +18,21 @@
|
||||||
"ImportPath": "github.com/google/gofuzz",
|
"ImportPath": "github.com/google/gofuzz",
|
||||||
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||||
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||||
|
|
|
@ -18,21 +18,21 @@
|
||||||
"ImportPath": "github.com/google/gofuzz",
|
"ImportPath": "github.com/google/gofuzz",
|
||||||
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
"Rev": "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/net/http/httpguts",
|
||||||
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/text/secure/bidirule",
|
"ImportPath": "golang.org/x/text/secure/bidirule",
|
||||||
|
|
|
@ -88,23 +88,23 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context",
|
"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",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/oauth2",
|
"ImportPath": "golang.org/x/oauth2",
|
||||||
|
|
|
@ -84,23 +84,23 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context",
|
"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",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/oauth2",
|
"ImportPath": "golang.org/x/oauth2",
|
||||||
|
|
|
@ -248,35 +248,35 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context",
|
"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",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/internal/timeseries",
|
"ImportPath": "golang.org/x/net/internal/timeseries",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/trace",
|
"ImportPath": "golang.org/x/net/trace",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/websocket",
|
"ImportPath": "golang.org/x/net/websocket",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/oauth2",
|
"ImportPath": "golang.org/x/oauth2",
|
||||||
|
|
|
@ -160,23 +160,23 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context",
|
"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",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/oauth2",
|
"ImportPath": "golang.org/x/oauth2",
|
||||||
|
|
|
@ -96,23 +96,23 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context",
|
"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",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/idna",
|
"ImportPath": "golang.org/x/net/idna",
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
"Rev": "a4630153038d3cb8c57f83d95200aea356145cf5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/net/lex/httplex",
|
|
||||||
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/oauth2",
|
"ImportPath": "golang.org/x/oauth2",
|
||||||
|
|
|
@ -69,7 +69,6 @@ var (
|
||||||
func init() {
|
func init() {
|
||||||
cmdSave.Flag.BoolVar(&saveR, "r", false, "rewrite import paths")
|
cmdSave.Flag.BoolVar(&saveR, "r", false, "rewrite import paths")
|
||||||
cmdSave.Flag.BoolVar(&saveT, "t", false, "save test files")
|
cmdSave.Flag.BoolVar(&saveT, "t", false, "save test files")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runSave(cmd *Command, args []string) {
|
func runSave(cmd *Command, args []string) {
|
||||||
|
|
|
@ -400,10 +400,13 @@ filegroup(
|
||||||
"//vendor/golang.org/x/lint:all-srcs",
|
"//vendor/golang.org/x/lint:all-srcs",
|
||||||
"//vendor/golang.org/x/net/context:all-srcs",
|
"//vendor/golang.org/x/net/context:all-srcs",
|
||||||
"//vendor/golang.org/x/net/html: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/http2:all-srcs",
|
||||||
"//vendor/golang.org/x/net/idna: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/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/proxy:all-srcs",
|
||||||
"//vendor/golang.org/x/net/trace:all-srcs",
|
"//vendor/golang.org/x/net/trace:all-srcs",
|
||||||
"//vendor/golang.org/x/net/websocket:all-srcs",
|
"//vendor/golang.org/x/net/websocket:all-srcs",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
sudo: false
|
sudo: false
|
||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.8.x
|
- 1.11.x
|
||||||
env:
|
env:
|
||||||
- DEP_VERSION="0.3.2"
|
- DEP_VERSION="0.3.2"
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ install:
|
||||||
- dep ensure
|
- dep ensure
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- make checkdocs
|
|
||||||
- make test
|
- make test
|
||||||
|
|
||||||
after_success:
|
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]]
|
[[projects]]
|
||||||
name = "cloud.google.com/go"
|
name = "cloud.google.com/go"
|
||||||
packages = ["compute/metadata"]
|
packages = ["compute/metadata"]
|
||||||
revision = "2d3a6656c17a60b0815b7e06ab0be04eacb6e613"
|
revision = "28a4bc8c44b3acbcc482cff0cdf7de29a4688b61"
|
||||||
version = "v0.16.0"
|
version = "v0.35.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/davecgh/go-spew"
|
name = "github.com/davecgh/go-spew"
|
||||||
packages = ["spew"]
|
packages = ["spew"]
|
||||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
|
||||||
version = "v1.1.0"
|
version = "v1.1.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/gogo/protobuf"
|
name = "github.com/gogo/protobuf"
|
||||||
|
@ -23,7 +23,13 @@
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/golang/protobuf"
|
name = "github.com/golang/protobuf"
|
||||||
packages = ["jsonpb","proto","ptypes","ptypes/any","ptypes/duration","ptypes/struct","ptypes/timestamp"]
|
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]]
|
[[projects]]
|
||||||
name = "github.com/opentracing/opentracing-go"
|
name = "github.com/opentracing/opentracing-go"
|
||||||
|
@ -40,20 +46,20 @@
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/sirupsen/logrus"
|
name = "github.com/sirupsen/logrus"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "f006c2ac4710855cf0f916dd6b77acf6b048dc6e"
|
revision = "e1e72e9de974bd926e5c56f83753fba2df402ce5"
|
||||||
version = "v1.0.3"
|
version = "v1.3.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/stretchr/testify"
|
name = "github.com/stretchr/testify"
|
||||||
packages = ["assert","require","suite"]
|
packages = ["assert","require","suite"]
|
||||||
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
|
revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053"
|
||||||
version = "v1.1.4"
|
version = "v1.3.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "go.uber.org/atomic"
|
name = "go.uber.org/atomic"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "8474b86a5a6f79c443ce4b2992817ff32cf208b8"
|
revision = "1ea20fb1cbb1cc08cbd0d913a96dead89aa18289"
|
||||||
version = "v1.3.1"
|
version = "v1.3.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "go.uber.org/multierr"
|
name = "go.uber.org/multierr"
|
||||||
|
@ -64,56 +70,56 @@
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "go.uber.org/zap"
|
name = "go.uber.org/zap"
|
||||||
packages = [".","buffer","internal/bufferpool","internal/color","internal/exit","zapcore"]
|
packages = [".","buffer","internal/bufferpool","internal/color","internal/exit","zapcore"]
|
||||||
revision = "35aad584952c3e7020db7b839f6b102de6271f89"
|
revision = "ff33455a0e382e8a81d14dd7c922020b6b5e7982"
|
||||||
version = "v1.7.1"
|
version = "v1.9.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
packages = ["ssh/terminal"]
|
packages = ["ssh/terminal"]
|
||||||
revision = "94eea52f7b742c7cbe0b03b22f0c4c8631ece122"
|
revision = "b01c7a72566457eb1420261cdafef86638fc3861"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/net"
|
name = "golang.org/x/net"
|
||||||
packages = ["context","context/ctxhttp","http2","http2/hpack","idna","internal/timeseries","lex/httplex","trace"]
|
packages = ["context","context/ctxhttp","http/httpguts","http2","http2/hpack","idna","internal/timeseries","trace"]
|
||||||
revision = "a8b9294777976932365dabb6640cf1468d95c70f"
|
revision = "d26f9f9a57f3fab6a695bec0d84433c2c50f8bbf"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/oauth2"
|
name = "golang.org/x/oauth2"
|
||||||
packages = [".","google","internal","jws","jwt"]
|
packages = [".","google","internal","jws","jwt"]
|
||||||
revision = "f95fa95eaa936d9d87489b15d1d18b97c1ba9c28"
|
revision = "99b60b757ec124ebb7d6b7e97f153b19c10ce163"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/sys"
|
name = "golang.org/x/sys"
|
||||||
packages = ["unix","windows"]
|
packages = ["unix","windows"]
|
||||||
revision = "13fcbd661c8ececa8807a29b48407d674b1d8ed8"
|
revision = "302c3dd5f1cc82baae8e44d9c3178e89b6e2b345"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
|
||||||
name = "golang.org/x/text"
|
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"]
|
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]]
|
[[projects]]
|
||||||
name = "google.golang.org/appengine"
|
name = "google.golang.org/appengine"
|
||||||
packages = [".","internal","internal/app_identity","internal/base","internal/datastore","internal/log","internal/modules","internal/remote_api","internal/urlfetch","urlfetch"]
|
packages = [".","internal","internal/app_identity","internal/base","internal/datastore","internal/log","internal/modules","internal/remote_api","internal/urlfetch","urlfetch"]
|
||||||
revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a"
|
revision = "e9657d882bb81064595ca3b56cbe2546bbabf7b1"
|
||||||
version = "v1.0.0"
|
version = "v1.4.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "google.golang.org/genproto"
|
name = "google.golang.org/genproto"
|
||||||
packages = ["googleapis/rpc/status"]
|
packages = ["googleapis/rpc/status"]
|
||||||
revision = "7f0da29060c682909f650ad8ed4e515bd74fa12a"
|
revision = "8ac453e89fca495c0d17f98932642f392e2a11f3"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "google.golang.org/grpc"
|
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"]
|
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 = "5a9f7b402fe85096d2e1d0383435ee1876e863d0"
|
revision = "a02b0774206b209466313a0b525d2c738fe407eb"
|
||||||
version = "v1.8.0"
|
version = "v1.18.0"
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
|
|
||||||
[gRPC Go](https://github.com/grpc/grpc-go) Middleware: interceptors, helpers, utilities.
|
[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
|
## Middleware
|
||||||
|
|
||||||
[gRPC Go](https://github.com/grpc/grpc-go) recently acquired support for
|
[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
|
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.
|
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
|
```go
|
||||||
import "github.com/grpc-ecosystem/go-grpc-middleware"
|
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/)
|
GOFILES_NOVENDOR = $(shell go list ./... | grep -v /vendor/)
|
||||||
|
|
||||||
all: vet fmt docs test
|
all: vet fmt test
|
||||||
|
|
||||||
docs:
|
|
||||||
./scripts/docs.sh generate
|
|
||||||
|
|
||||||
checkdocs:
|
|
||||||
./scripts/docs.sh check
|
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
go fmt $(GOFILES_NOVENDOR)
|
go fmt $(GOFILES_NOVENDOR)
|
||||||
|
@ -19,4 +13,4 @@ vet:
|
||||||
test: vet
|
test: vet
|
||||||
./scripts/test_all.sh
|
./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
|
// The lists of element names and attribute keys were taken from
|
||||||
// https://html.spec.whatwg.org/multipage/indices.html#index
|
// 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,
|
// "command", "keygen" and "menuitem" have been removed from the spec,
|
||||||
// but are kept here for backwards compatibility.
|
// 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.
|
// extra are ad-hoc values not covered by any of the lists above.
|
||||||
var extra = []string{
|
var extra = []string{
|
||||||
|
"acronym",
|
||||||
"align",
|
"align",
|
||||||
"annotation",
|
"annotation",
|
||||||
"annotation-xml",
|
"annotation-xml",
|
||||||
|
@ -700,6 +701,8 @@ var extra = []string{
|
||||||
"plaintext",
|
"plaintext",
|
||||||
"prompt",
|
"prompt",
|
||||||
"public",
|
"public",
|
||||||
|
"rb",
|
||||||
|
"rtc",
|
||||||
"spacer",
|
"spacer",
|
||||||
"strike",
|
"strike",
|
||||||
"svg",
|
"svg",
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
package html
|
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".
|
// have varying levels of special parsing rules".
|
||||||
// https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements
|
// https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements
|
||||||
var isSpecialElementMap = map[string]bool{
|
var isSpecialElementMap = map[string]bool{
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -67,7 +67,7 @@ func mathMLTextIntegrationPoint(n *Node) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.5.
|
// Section 12.2.6.5.
|
||||||
var breakout = map[string]bool{
|
var breakout = map[string]bool{
|
||||||
"b": true,
|
"b": true,
|
||||||
"big": true,
|
"big": true,
|
||||||
|
@ -115,7 +115,7 @@ var breakout = map[string]bool{
|
||||||
"var": true,
|
"var": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.5.
|
// Section 12.2.6.5.
|
||||||
var svgTagNameAdjustments = map[string]string{
|
var svgTagNameAdjustments = map[string]string{
|
||||||
"altglyph": "altGlyph",
|
"altglyph": "altGlyph",
|
||||||
"altglyphdef": "altGlyphDef",
|
"altglyphdef": "altGlyphDef",
|
||||||
|
@ -155,7 +155,7 @@ var svgTagNameAdjustments = map[string]string{
|
||||||
"textpath": "textPath",
|
"textpath": "textPath",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.1
|
// Section 12.2.6.1
|
||||||
var mathMLAttributeAdjustments = map[string]string{
|
var mathMLAttributeAdjustments = map[string]string{
|
||||||
"definitionurl": "definitionURL",
|
"definitionurl": "definitionURL",
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,10 @@ const (
|
||||||
scopeMarkerNode
|
scopeMarkerNode
|
||||||
)
|
)
|
||||||
|
|
||||||
// Section 12.2.3.3 says "scope markers are inserted when entering applet
|
// Section 12.2.4.3 says "The markers are inserted when entering applet,
|
||||||
// elements, buttons, object elements, marquees, table cells, and table
|
// object, marquee, template, td, th, and caption elements, and are used
|
||||||
// captions, and are used to prevent formatting from 'leaking'".
|
// to prevent formatting from "leaking" into applet, object, marquee,
|
||||||
|
// template, td, th, and caption elements".
|
||||||
var scopeMarker = Node{Type: scopeMarkerNode}
|
var scopeMarker = Node{Type: scopeMarkerNode}
|
||||||
|
|
||||||
// A Node consists of a NodeType and some Data (tag name for element nodes,
|
// 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
|
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.
|
// insert inserts a node at the given index.
|
||||||
func (s *nodeStack) insert(i int, n *Node) {
|
func (s *nodeStack) insert(i int, n *Node) {
|
||||||
(*s) = append(*s, nil)
|
(*s) = append(*s, nil)
|
||||||
|
@ -191,3 +202,19 @@ func (s *nodeStack) remove(n *Node) {
|
||||||
(*s)[j] = nil
|
(*s)[j] = nil
|
||||||
*s = (*s)[:j]
|
*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
|
hasSelfClosingToken bool
|
||||||
// doc is the document root element.
|
// doc is the document root element.
|
||||||
doc *Node
|
doc *Node
|
||||||
// The stack of open elements (section 12.2.3.2) and active formatting
|
// The stack of open elements (section 12.2.4.2) and active formatting
|
||||||
// elements (section 12.2.3.3).
|
// elements (section 12.2.4.3).
|
||||||
oe, afe nodeStack
|
oe, afe nodeStack
|
||||||
// Element pointers (section 12.2.3.4).
|
// Element pointers (section 12.2.4.4).
|
||||||
head, form *Node
|
head, form *Node
|
||||||
// Other parsing state flags (section 12.2.3.5).
|
// Other parsing state flags (section 12.2.4.5).
|
||||||
scripting, framesetOK bool
|
scripting, framesetOK bool
|
||||||
|
// The stack of template insertion modes
|
||||||
|
templateStack insertionModeStack
|
||||||
// im is the current insertion mode.
|
// im is the current insertion mode.
|
||||||
im insertionMode
|
im insertionMode
|
||||||
// originalIM is the insertion mode to go back to after completing a text
|
// originalIM is the insertion mode to go back to after completing a text
|
||||||
// or inTableText insertion mode.
|
// or inTableText insertion mode.
|
||||||
originalIM insertionMode
|
originalIM insertionMode
|
||||||
// fosterParenting is whether new elements should be inserted according to
|
// 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
|
fosterParenting bool
|
||||||
// quirks is whether the parser is operating in "quirks mode."
|
// quirks is whether the parser is operating in "quirks mode."
|
||||||
quirks bool
|
quirks bool
|
||||||
|
@ -56,7 +58,7 @@ func (p *parser) top() *Node {
|
||||||
return p.doc
|
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 (
|
var (
|
||||||
defaultScopeStopTags = map[string][]a.Atom{
|
defaultScopeStopTags = map[string][]a.Atom{
|
||||||
"": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template},
|
"": {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
|
// 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
|
// 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.
|
// 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
|
// 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
|
return -1
|
||||||
}
|
}
|
||||||
case tableScope:
|
case tableScope:
|
||||||
if tagAtom == a.Html || tagAtom == a.Table {
|
if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
case selectScope:
|
case selectScope:
|
||||||
|
@ -162,17 +164,17 @@ func (p *parser) clearStackToContext(s scope) {
|
||||||
tagAtom := p.oe[i].DataAtom
|
tagAtom := p.oe[i].DataAtom
|
||||||
switch s {
|
switch s {
|
||||||
case tableScope:
|
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]
|
p.oe = p.oe[:i+1]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case tableRowScope:
|
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]
|
p.oe = p.oe[:i+1]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case tableBodyScope:
|
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]
|
p.oe = p.oe[:i+1]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -183,7 +185,7 @@ func (p *parser) clearStackToContext(s scope) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateImpliedEndTags pops nodes off the stack of open elements as long as
|
// 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.
|
// If exceptions are specified, nodes with that name will not be popped off.
|
||||||
func (p *parser) generateImpliedEndTags(exceptions ...string) {
|
func (p *parser) generateImpliedEndTags(exceptions ...string) {
|
||||||
var i int
|
var i int
|
||||||
|
@ -192,7 +194,7 @@ loop:
|
||||||
n := p.oe[i]
|
n := p.oe[i]
|
||||||
if n.Type == ElementNode {
|
if n.Type == ElementNode {
|
||||||
switch n.DataAtom {
|
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 {
|
for _, except := range exceptions {
|
||||||
if n.Data == except {
|
if n.Data == except {
|
||||||
break loop
|
break loop
|
||||||
|
@ -207,6 +209,27 @@ loop:
|
||||||
p.oe = p.oe[:i+1]
|
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
|
// 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.
|
// of open elements if it is an element node.
|
||||||
func (p *parser) addChild(n *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.
|
// 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) {
|
func (p *parser) fosterParent(n *Node) {
|
||||||
var table, parent, prev *Node
|
var table, parent, prev, template *Node
|
||||||
var i int
|
var i int
|
||||||
for i = len(p.oe) - 1; i >= 0; i-- {
|
for i = len(p.oe) - 1; i >= 0; i-- {
|
||||||
if p.oe[i].DataAtom == a.Table {
|
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 {
|
if table == nil {
|
||||||
// The foster parent is the html element.
|
// The foster parent is the html element.
|
||||||
parent = p.oe[0]
|
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() {
|
func (p *parser) addFormattingElement() {
|
||||||
tagAtom, attr := p.tok.DataAtom, p.tok.Attr
|
tagAtom, attr := p.tok.DataAtom, p.tok.Attr
|
||||||
p.addElement()
|
p.addElement()
|
||||||
|
@ -351,7 +387,7 @@ findIdenticalElements:
|
||||||
p.afe = append(p.afe, p.top())
|
p.afe = append(p.afe, p.top())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.3.3.
|
// Section 12.2.4.3.
|
||||||
func (p *parser) clearActiveFormattingElements() {
|
func (p *parser) clearActiveFormattingElements() {
|
||||||
for {
|
for {
|
||||||
n := p.afe.pop()
|
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() {
|
func (p *parser) reconstructActiveFormattingElements() {
|
||||||
n := p.afe.top()
|
n := p.afe.top()
|
||||||
if n == nil {
|
if n == nil {
|
||||||
|
@ -390,12 +426,12 @@ func (p *parser) reconstructActiveFormattingElements() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.4.
|
// Section 12.2.5.
|
||||||
func (p *parser) acknowledgeSelfClosingTag() {
|
func (p *parser) acknowledgeSelfClosingTag() {
|
||||||
p.hasSelfClosingToken = false
|
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
|
// a particular state in the HTML5 parser's state machine. It updates the
|
||||||
// parser's fields depending on parser.tok (where ErrorToken means EOF).
|
// parser's fields depending on parser.tok (where ErrorToken means EOF).
|
||||||
// It returns whether the token was consumed.
|
// 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
|
// setOriginalIM sets the insertion mode to return to after completing a text or
|
||||||
// inTableText insertion mode.
|
// 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() {
|
func (p *parser) setOriginalIM() {
|
||||||
if p.originalIM != nil {
|
if p.originalIM != nil {
|
||||||
panic("html: bad parser state: originalIM was set twice")
|
panic("html: bad parser state: originalIM was set twice")
|
||||||
|
@ -411,18 +447,38 @@ func (p *parser) setOriginalIM() {
|
||||||
p.originalIM = p.im
|
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() {
|
func (p *parser) resetInsertionMode() {
|
||||||
for i := len(p.oe) - 1; i >= 0; i-- {
|
for i := len(p.oe) - 1; i >= 0; i-- {
|
||||||
n := p.oe[i]
|
n := p.oe[i]
|
||||||
if i == 0 && p.context != nil {
|
last := i == 0
|
||||||
|
if last && p.context != nil {
|
||||||
n = p.context
|
n = p.context
|
||||||
}
|
}
|
||||||
|
|
||||||
switch n.DataAtom {
|
switch n.DataAtom {
|
||||||
case a.Select:
|
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
|
p.im = inSelectIM
|
||||||
case a.Td, a.Th:
|
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
|
p.im = inCellIM
|
||||||
case a.Tr:
|
case a.Tr:
|
||||||
p.im = inRowIM
|
p.im = inRowIM
|
||||||
|
@ -434,25 +490,37 @@ func (p *parser) resetInsertionMode() {
|
||||||
p.im = inColumnGroupIM
|
p.im = inColumnGroupIM
|
||||||
case a.Table:
|
case a.Table:
|
||||||
p.im = inTableIM
|
p.im = inTableIM
|
||||||
|
case a.Template:
|
||||||
|
p.im = p.templateStack.top()
|
||||||
case a.Head:
|
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:
|
case a.Body:
|
||||||
p.im = inBodyIM
|
p.im = inBodyIM
|
||||||
case a.Frameset:
|
case a.Frameset:
|
||||||
p.im = inFramesetIM
|
p.im = inFramesetIM
|
||||||
case a.Html:
|
case a.Html:
|
||||||
p.im = beforeHeadIM
|
if p.head == nil {
|
||||||
|
p.im = beforeHeadIM
|
||||||
|
} else {
|
||||||
|
p.im = afterHeadIM
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
|
if last {
|
||||||
|
p.im = inBodyIM
|
||||||
|
return
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.im = inBodyIM
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const whitespace = " \t\r\n\f"
|
const whitespace = " \t\r\n\f"
|
||||||
|
|
||||||
// Section 12.2.5.4.1.
|
// Section 12.2.6.4.1.
|
||||||
func initialIM(p *parser) bool {
|
func initialIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case TextToken:
|
case TextToken:
|
||||||
|
@ -479,7 +547,7 @@ func initialIM(p *parser) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.4.2.
|
// Section 12.2.6.4.2.
|
||||||
func beforeHTMLIM(p *parser) bool {
|
func beforeHTMLIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case DoctypeToken:
|
case DoctypeToken:
|
||||||
|
@ -517,7 +585,7 @@ func beforeHTMLIM(p *parser) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.4.3.
|
// Section 12.2.6.4.3.
|
||||||
func beforeHeadIM(p *parser) bool {
|
func beforeHeadIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case TextToken:
|
case TextToken:
|
||||||
|
@ -560,7 +628,7 @@ func beforeHeadIM(p *parser) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.4.4.
|
// Section 12.2.6.4.4.
|
||||||
func inHeadIM(p *parser) bool {
|
func inHeadIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case TextToken:
|
case TextToken:
|
||||||
|
@ -590,19 +658,33 @@ func inHeadIM(p *parser) bool {
|
||||||
case a.Head:
|
case a.Head:
|
||||||
// Ignore the token.
|
// Ignore the token.
|
||||||
return true
|
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:
|
case EndTagToken:
|
||||||
switch p.tok.DataAtom {
|
switch p.tok.DataAtom {
|
||||||
case a.Head:
|
case a.Head:
|
||||||
n := p.oe.pop()
|
p.oe.pop()
|
||||||
if n.DataAtom != a.Head {
|
|
||||||
panic("html: bad parser state: <head> element not found, in the in-head insertion mode")
|
|
||||||
}
|
|
||||||
p.im = afterHeadIM
|
p.im = afterHeadIM
|
||||||
return true
|
return true
|
||||||
case a.Body, a.Html, a.Br:
|
case a.Body, a.Html, a.Br:
|
||||||
p.parseImpliedToken(EndTagToken, a.Head, a.Head.String())
|
p.parseImpliedToken(EndTagToken, a.Head, a.Head.String())
|
||||||
return false
|
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:
|
default:
|
||||||
// Ignore the token.
|
// Ignore the token.
|
||||||
return true
|
return true
|
||||||
|
@ -622,7 +704,7 @@ func inHeadIM(p *parser) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.4.6.
|
// Section 12.2.6.4.6.
|
||||||
func afterHeadIM(p *parser) bool {
|
func afterHeadIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case TextToken:
|
case TextToken:
|
||||||
|
@ -648,7 +730,7 @@ func afterHeadIM(p *parser) bool {
|
||||||
p.addElement()
|
p.addElement()
|
||||||
p.im = inFramesetIM
|
p.im = inFramesetIM
|
||||||
return true
|
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)
|
p.oe = append(p.oe, p.head)
|
||||||
defer p.oe.remove(p.head)
|
defer p.oe.remove(p.head)
|
||||||
return inHeadIM(p)
|
return inHeadIM(p)
|
||||||
|
@ -660,6 +742,8 @@ func afterHeadIM(p *parser) bool {
|
||||||
switch p.tok.DataAtom {
|
switch p.tok.DataAtom {
|
||||||
case a.Body, a.Html, a.Br:
|
case a.Body, a.Html, a.Br:
|
||||||
// Drop down to creating an implied <body> tag.
|
// Drop down to creating an implied <body> tag.
|
||||||
|
case a.Template:
|
||||||
|
return inHeadIM(p)
|
||||||
default:
|
default:
|
||||||
// Ignore the token.
|
// Ignore the token.
|
||||||
return true
|
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 {
|
func inBodyIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case TextToken:
|
case TextToken:
|
||||||
|
@ -727,10 +811,16 @@ func inBodyIM(p *parser) bool {
|
||||||
case StartTagToken:
|
case StartTagToken:
|
||||||
switch p.tok.DataAtom {
|
switch p.tok.DataAtom {
|
||||||
case a.Html:
|
case a.Html:
|
||||||
|
if p.oe.contains(a.Template) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
copyAttributes(p.oe[0], p.tok)
|
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)
|
return inHeadIM(p)
|
||||||
case a.Body:
|
case a.Body:
|
||||||
|
if p.oe.contains(a.Template) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
if len(p.oe) >= 2 {
|
if len(p.oe) >= 2 {
|
||||||
body := p.oe[1]
|
body := p.oe[1]
|
||||||
if body.Type == ElementNode && body.DataAtom == a.Body {
|
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.
|
// The newline, if any, will be dealt with by the TextToken case.
|
||||||
p.framesetOK = false
|
p.framesetOK = false
|
||||||
case a.Form:
|
case a.Form:
|
||||||
if p.form == nil {
|
if p.form != nil && !p.oe.contains(a.Template) {
|
||||||
p.popUntil(buttonScope, a.P)
|
// Ignore the token
|
||||||
p.addElement()
|
return true
|
||||||
|
}
|
||||||
|
p.popUntil(buttonScope, a.P)
|
||||||
|
p.addElement()
|
||||||
|
if !p.oe.contains(a.Template) {
|
||||||
p.form = p.top()
|
p.form = p.top()
|
||||||
}
|
}
|
||||||
case a.Li:
|
case a.Li:
|
||||||
|
@ -952,11 +1046,16 @@ func inBodyIM(p *parser) bool {
|
||||||
}
|
}
|
||||||
p.reconstructActiveFormattingElements()
|
p.reconstructActiveFormattingElements()
|
||||||
p.addElement()
|
p.addElement()
|
||||||
case a.Rp, a.Rt:
|
case a.Rb, a.Rtc:
|
||||||
if p.elementInScope(defaultScope, a.Ruby) {
|
if p.elementInScope(defaultScope, a.Ruby) {
|
||||||
p.generateImpliedEndTags()
|
p.generateImpliedEndTags()
|
||||||
}
|
}
|
||||||
p.addElement()
|
p.addElement()
|
||||||
|
case a.Rp, a.Rt:
|
||||||
|
if p.elementInScope(defaultScope, a.Ruby) {
|
||||||
|
p.generateImpliedEndTags("rtc")
|
||||||
|
}
|
||||||
|
p.addElement()
|
||||||
case a.Math, a.Svg:
|
case a.Math, a.Svg:
|
||||||
p.reconstructActiveFormattingElements()
|
p.reconstructActiveFormattingElements()
|
||||||
if p.tok.DataAtom == a.Math {
|
if p.tok.DataAtom == a.Math {
|
||||||
|
@ -972,7 +1071,13 @@ func inBodyIM(p *parser) bool {
|
||||||
p.acknowledgeSelfClosingTag()
|
p.acknowledgeSelfClosingTag()
|
||||||
}
|
}
|
||||||
return true
|
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.
|
// Ignore the token.
|
||||||
default:
|
default:
|
||||||
p.reconstructActiveFormattingElements()
|
p.reconstructActiveFormattingElements()
|
||||||
|
@ -993,15 +1098,29 @@ 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:
|
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)
|
p.popUntil(defaultScope, p.tok.DataAtom)
|
||||||
case a.Form:
|
case a.Form:
|
||||||
node := p.form
|
if p.oe.contains(a.Template) {
|
||||||
p.form = nil
|
i := p.indexOfElementInScope(defaultScope, a.Form)
|
||||||
i := p.indexOfElementInScope(defaultScope, a.Form)
|
if i == -1 {
|
||||||
if node == nil || i == -1 || p.oe[i] != node {
|
// Ignore the token.
|
||||||
// Ignore the token.
|
return true
|
||||||
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)
|
||||||
|
if node == nil || i == -1 || p.oe[i] != node {
|
||||||
|
// Ignore the token.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
p.generateImpliedEndTags()
|
||||||
|
p.oe.remove(node)
|
||||||
}
|
}
|
||||||
p.generateImpliedEndTags()
|
|
||||||
p.oe.remove(node)
|
|
||||||
case a.P:
|
case a.P:
|
||||||
if !p.elementInScope(buttonScope, a.P) {
|
if !p.elementInScope(buttonScope, a.P) {
|
||||||
p.parseImpliedToken(StartTagToken, a.P, a.P.String())
|
p.parseImpliedToken(StartTagToken, a.P, a.P.String())
|
||||||
|
@ -1022,6 +1141,8 @@ func inBodyIM(p *parser) bool {
|
||||||
case a.Br:
|
case a.Br:
|
||||||
p.tok.Type = StartTagToken
|
p.tok.Type = StartTagToken
|
||||||
return false
|
return false
|
||||||
|
case a.Template:
|
||||||
|
return inHeadIM(p)
|
||||||
default:
|
default:
|
||||||
p.inBodyEndTagOther(p.tok.DataAtom)
|
p.inBodyEndTagOther(p.tok.DataAtom)
|
||||||
}
|
}
|
||||||
|
@ -1030,6 +1151,21 @@ func inBodyIM(p *parser) bool {
|
||||||
Type: CommentNode,
|
Type: CommentNode,
|
||||||
Data: p.tok.Data,
|
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
|
return true
|
||||||
|
@ -1135,6 +1271,12 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) {
|
||||||
switch commonAncestor.DataAtom {
|
switch commonAncestor.DataAtom {
|
||||||
case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
|
case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
|
||||||
p.fosterParent(lastNode)
|
p.fosterParent(lastNode)
|
||||||
|
case a.Template:
|
||||||
|
// TODO: remove namespace checking
|
||||||
|
if commonAncestor.Namespace == "html" {
|
||||||
|
commonAncestor = commonAncestor.LastChild
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
default:
|
default:
|
||||||
commonAncestor.AppendChild(lastNode)
|
commonAncestor.AppendChild(lastNode)
|
||||||
}
|
}
|
||||||
|
@ -1160,7 +1302,7 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM.
|
// 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
|
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign
|
||||||
func (p *parser) inBodyEndTagOther(tagAtom a.Atom) {
|
func (p *parser) inBodyEndTagOther(tagAtom a.Atom) {
|
||||||
for i := len(p.oe) - 1; i >= 0; i-- {
|
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 {
|
func textIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case ErrorToken:
|
case ErrorToken:
|
||||||
|
@ -1203,7 +1345,7 @@ func textIM(p *parser) bool {
|
||||||
return p.tok.Type == EndTagToken
|
return p.tok.Type == EndTagToken
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.4.9.
|
// Section 12.2.6.4.9.
|
||||||
func inTableIM(p *parser) bool {
|
func inTableIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case ErrorToken:
|
case ErrorToken:
|
||||||
|
@ -1249,7 +1391,7 @@ func inTableIM(p *parser) bool {
|
||||||
}
|
}
|
||||||
// Ignore the token.
|
// Ignore the token.
|
||||||
return true
|
return true
|
||||||
case a.Style, a.Script:
|
case a.Style, a.Script, a.Template:
|
||||||
return inHeadIM(p)
|
return inHeadIM(p)
|
||||||
case a.Input:
|
case a.Input:
|
||||||
for _, t := range p.tok.Attr {
|
for _, t := range p.tok.Attr {
|
||||||
|
@ -1261,7 +1403,7 @@ func inTableIM(p *parser) bool {
|
||||||
}
|
}
|
||||||
// Otherwise drop down to the default action.
|
// Otherwise drop down to the default action.
|
||||||
case a.Form:
|
case a.Form:
|
||||||
if p.form != nil {
|
if p.oe.contains(a.Template) || p.form != nil {
|
||||||
// Ignore the token.
|
// Ignore the token.
|
||||||
return true
|
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:
|
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.
|
// Ignore the token.
|
||||||
return true
|
return true
|
||||||
|
case a.Template:
|
||||||
|
return inHeadIM(p)
|
||||||
}
|
}
|
||||||
case CommentToken:
|
case CommentToken:
|
||||||
p.addChild(&Node{
|
p.addChild(&Node{
|
||||||
|
@ -1309,7 +1453,7 @@ func inTableIM(p *parser) bool {
|
||||||
return inBodyIM(p)
|
return inBodyIM(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.4.11.
|
// Section 12.2.6.4.11.
|
||||||
func inCaptionIM(p *parser) bool {
|
func inCaptionIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case StartTagToken:
|
case StartTagToken:
|
||||||
|
@ -1355,7 +1499,7 @@ func inCaptionIM(p *parser) bool {
|
||||||
return inBodyIM(p)
|
return inBodyIM(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.4.12.
|
// Section 12.2.6.4.12.
|
||||||
func inColumnGroupIM(p *parser) bool {
|
func inColumnGroupIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case TextToken:
|
case TextToken:
|
||||||
|
@ -1386,11 +1530,13 @@ func inColumnGroupIM(p *parser) bool {
|
||||||
p.oe.pop()
|
p.oe.pop()
|
||||||
p.acknowledgeSelfClosingTag()
|
p.acknowledgeSelfClosingTag()
|
||||||
return true
|
return true
|
||||||
|
case a.Template:
|
||||||
|
return inHeadIM(p)
|
||||||
}
|
}
|
||||||
case EndTagToken:
|
case EndTagToken:
|
||||||
switch p.tok.DataAtom {
|
switch p.tok.DataAtom {
|
||||||
case a.Colgroup:
|
case a.Colgroup:
|
||||||
if p.oe.top().DataAtom != a.Html {
|
if p.oe.top().DataAtom == a.Colgroup {
|
||||||
p.oe.pop()
|
p.oe.pop()
|
||||||
p.im = inTableIM
|
p.im = inTableIM
|
||||||
}
|
}
|
||||||
|
@ -1398,17 +1544,19 @@ func inColumnGroupIM(p *parser) bool {
|
||||||
case a.Col:
|
case a.Col:
|
||||||
// Ignore the token.
|
// Ignore the token.
|
||||||
return true
|
return true
|
||||||
|
case a.Template:
|
||||||
|
return inHeadIM(p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if p.oe.top().DataAtom != a.Html {
|
if p.oe.top().DataAtom != a.Colgroup {
|
||||||
p.oe.pop()
|
return true
|
||||||
p.im = inTableIM
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
return true
|
p.oe.pop()
|
||||||
|
p.im = inTableIM
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.4.13.
|
// Section 12.2.6.4.13.
|
||||||
func inTableBodyIM(p *parser) bool {
|
func inTableBodyIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case StartTagToken:
|
case StartTagToken:
|
||||||
|
@ -1460,7 +1608,7 @@ func inTableBodyIM(p *parser) bool {
|
||||||
return inTableIM(p)
|
return inTableIM(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.4.14.
|
// Section 12.2.6.4.14.
|
||||||
func inRowIM(p *parser) bool {
|
func inRowIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case StartTagToken:
|
case StartTagToken:
|
||||||
|
@ -1511,7 +1659,7 @@ func inRowIM(p *parser) bool {
|
||||||
return inTableIM(p)
|
return inTableIM(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.4.15.
|
// Section 12.2.6.4.15.
|
||||||
func inCellIM(p *parser) bool {
|
func inCellIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case StartTagToken:
|
case StartTagToken:
|
||||||
|
@ -1560,7 +1708,7 @@ func inCellIM(p *parser) bool {
|
||||||
return inBodyIM(p)
|
return inBodyIM(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.4.16.
|
// Section 12.2.6.4.16.
|
||||||
func inSelectIM(p *parser) bool {
|
func inSelectIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case ErrorToken:
|
case ErrorToken:
|
||||||
|
@ -1597,7 +1745,7 @@ func inSelectIM(p *parser) bool {
|
||||||
p.tokenizer.NextIsNotRawText()
|
p.tokenizer.NextIsNotRawText()
|
||||||
// Ignore the token.
|
// Ignore the token.
|
||||||
return true
|
return true
|
||||||
case a.Script:
|
case a.Script, a.Template:
|
||||||
return inHeadIM(p)
|
return inHeadIM(p)
|
||||||
}
|
}
|
||||||
case EndTagToken:
|
case EndTagToken:
|
||||||
|
@ -1618,6 +1766,8 @@ func inSelectIM(p *parser) bool {
|
||||||
if p.popUntil(selectScope, a.Select) {
|
if p.popUntil(selectScope, a.Select) {
|
||||||
p.resetInsertionMode()
|
p.resetInsertionMode()
|
||||||
}
|
}
|
||||||
|
case a.Template:
|
||||||
|
return inHeadIM(p)
|
||||||
}
|
}
|
||||||
case CommentToken:
|
case CommentToken:
|
||||||
p.addChild(&Node{
|
p.addChild(&Node{
|
||||||
|
@ -1632,7 +1782,7 @@ func inSelectIM(p *parser) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.4.17.
|
// Section 12.2.6.4.17.
|
||||||
func inSelectInTableIM(p *parser) bool {
|
func inSelectInTableIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case StartTagToken, EndTagToken:
|
case StartTagToken, EndTagToken:
|
||||||
|
@ -1650,7 +1800,62 @@ func inSelectInTableIM(p *parser) bool {
|
||||||
return inSelectIM(p)
|
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 {
|
func afterBodyIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case ErrorToken:
|
case ErrorToken:
|
||||||
|
@ -1688,7 +1893,7 @@ func afterBodyIM(p *parser) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.4.19.
|
// Section 12.2.6.4.20.
|
||||||
func inFramesetIM(p *parser) bool {
|
func inFramesetIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case CommentToken:
|
case CommentToken:
|
||||||
|
@ -1720,6 +1925,11 @@ func inFramesetIM(p *parser) bool {
|
||||||
p.acknowledgeSelfClosingTag()
|
p.acknowledgeSelfClosingTag()
|
||||||
case a.Noframes:
|
case a.Noframes:
|
||||||
return inHeadIM(p)
|
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:
|
case EndTagToken:
|
||||||
switch p.tok.DataAtom {
|
switch p.tok.DataAtom {
|
||||||
|
@ -1738,7 +1948,7 @@ func inFramesetIM(p *parser) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.4.20.
|
// Section 12.2.6.4.21.
|
||||||
func afterFramesetIM(p *parser) bool {
|
func afterFramesetIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case CommentToken:
|
case CommentToken:
|
||||||
|
@ -1777,7 +1987,7 @@ func afterFramesetIM(p *parser) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.4.21.
|
// Section 12.2.6.4.22.
|
||||||
func afterAfterBodyIM(p *parser) bool {
|
func afterAfterBodyIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case ErrorToken:
|
case ErrorToken:
|
||||||
|
@ -1806,7 +2016,7 @@ func afterAfterBodyIM(p *parser) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.4.22.
|
// Section 12.2.6.4.23.
|
||||||
func afterAfterFramesetIM(p *parser) bool {
|
func afterAfterFramesetIM(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case CommentToken:
|
case CommentToken:
|
||||||
|
@ -1844,7 +2054,7 @@ func afterAfterFramesetIM(p *parser) bool {
|
||||||
|
|
||||||
const whitespaceOrNUL = whitespace + "\x00"
|
const whitespaceOrNUL = whitespace + "\x00"
|
||||||
|
|
||||||
// Section 12.2.5.5.
|
// Section 12.2.6.5
|
||||||
func parseForeignContent(p *parser) bool {
|
func parseForeignContent(p *parser) bool {
|
||||||
switch p.tok.Type {
|
switch p.tok.Type {
|
||||||
case TextToken:
|
case TextToken:
|
||||||
|
@ -1924,7 +2134,7 @@ func parseForeignContent(p *parser) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section 12.2.5.
|
// Section 12.2.6.
|
||||||
func (p *parser) inForeignContent() bool {
|
func (p *parser) inForeignContent() bool {
|
||||||
if len(p.oe) == 0 {
|
if len(p.oe) == 0 {
|
||||||
return false
|
return false
|
||||||
|
@ -2064,6 +2274,9 @@ func ParseFragment(r io.Reader, context *Node) ([]*Node, error) {
|
||||||
}
|
}
|
||||||
p.doc.AppendChild(root)
|
p.doc.AppendChild(root)
|
||||||
p.oe = nodeStack{root}
|
p.oe = nodeStack{root}
|
||||||
|
if context != nil && context.DataAtom == a.Template {
|
||||||
|
p.templateStack = append(p.templateStack, inTemplateIM)
|
||||||
|
}
|
||||||
p.resetInsertionMode()
|
p.resetInsertionMode()
|
||||||
|
|
||||||
for n := context; n != nil; n = n.Parent {
|
for n := context; n != nil; n = n.Parent {
|
||||||
|
|
|
@ -2,9 +2,12 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = ["httplex.go"],
|
srcs = [
|
||||||
importmap = "k8s.io/kubernetes/vendor/golang.org/x/net/lex/httplex",
|
"guts.go",
|
||||||
importpath = "golang.org/x/net/lex/httplex",
|
"httplex.go",
|
||||||
|
],
|
||||||
|
importmap = "k8s.io/kubernetes/vendor/golang.org/x/net/http/httpguts",
|
||||||
|
importpath = "golang.org/x/net/http/httpguts",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = ["//vendor/golang.org/x/net/idna:go_default_library"],
|
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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Package httplex contains rules around lexical matters of various
|
package httpguts
|
||||||
// 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
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
|
@ -10,6 +10,7 @@ go_library(
|
||||||
"errors.go",
|
"errors.go",
|
||||||
"flow.go",
|
"flow.go",
|
||||||
"frame.go",
|
"frame.go",
|
||||||
|
"go111.go",
|
||||||
"go16.go",
|
"go16.go",
|
||||||
"go17.go",
|
"go17.go",
|
||||||
"go17_not18.go",
|
"go17_not18.go",
|
||||||
|
@ -18,6 +19,7 @@ go_library(
|
||||||
"gotrack.go",
|
"gotrack.go",
|
||||||
"headermap.go",
|
"headermap.go",
|
||||||
"http2.go",
|
"http2.go",
|
||||||
|
"not_go111.go",
|
||||||
"not_go16.go",
|
"not_go16.go",
|
||||||
"not_go17.go",
|
"not_go17.go",
|
||||||
"not_go18.go",
|
"not_go18.go",
|
||||||
|
@ -34,9 +36,9 @@ go_library(
|
||||||
importpath = "golang.org/x/net/http2",
|
importpath = "golang.org/x/net/http2",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
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/http2/hpack:go_default_library",
|
||||||
"//vendor/golang.org/x/net/idna: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
|
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) {
|
func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) {
|
||||||
if isConnectionCloseRequest(req) && dialOnMiss {
|
if isConnectionCloseRequest(req) && dialOnMiss {
|
||||||
// It gets its own connection.
|
// It gets its own connection.
|
||||||
|
traceGetConn(req, addr)
|
||||||
const singleUse = true
|
const singleUse = true
|
||||||
cc, err := p.t.dialClientConn(addr, singleUse)
|
cc, err := p.t.dialClientConn(addr, singleUse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -64,7 +86,10 @@ func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMis
|
||||||
}
|
}
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
for _, cc := range p.conns[addr] {
|
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()
|
p.mu.Unlock()
|
||||||
return cc, nil
|
return cc, nil
|
||||||
}
|
}
|
||||||
|
@ -73,6 +98,7 @@ func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMis
|
||||||
p.mu.Unlock()
|
p.mu.Unlock()
|
||||||
return nil, ErrNoCachedConn
|
return nil, ErrNoCachedConn
|
||||||
}
|
}
|
||||||
|
traceGetConn(req, addr)
|
||||||
call := p.getStartDialLocked(addr)
|
call := p.getStartDialLocked(addr)
|
||||||
p.mu.Unlock()
|
p.mu.Unlock()
|
||||||
<-call.done
|
<-call.done
|
||||||
|
|
|
@ -57,7 +57,7 @@ func configureTransport(t1 *http.Transport) (*Transport, error) {
|
||||||
|
|
||||||
// registerHTTPSProtocol calls Transport.RegisterProtocol but
|
// registerHTTPSProtocol calls Transport.RegisterProtocol but
|
||||||
// converting panics into errors.
|
// 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() {
|
defer func() {
|
||||||
if e := recover(); e != nil {
|
if e := recover(); e != nil {
|
||||||
err = fmt.Errorf("%v", e)
|
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
|
// noDialH2RoundTripper is a RoundTripper which only tries to complete the request
|
||||||
// if there's already has a cached connection to the host.
|
// 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) {
|
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) {
|
if isNoCachedConnError(err) {
|
||||||
return nil, http.ErrSkipAltProtocol
|
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.
|
// add adds n bytes (positive or negative) to the flow control window.
|
||||||
// It returns false if the sum would exceed 2^31-1.
|
// It returns false if the sum would exceed 2^31-1.
|
||||||
func (f *flow) add(n int32) bool {
|
func (f *flow) add(n int32) bool {
|
||||||
remain := (1<<31 - 1) - f.n
|
sum := f.n + n
|
||||||
if n > remain {
|
if (sum > n) == (f.n > 0) {
|
||||||
return false
|
f.n = sum
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
f.n += n
|
return false
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/net/http/httpguts"
|
||||||
"golang.org/x/net/http2/hpack"
|
"golang.org/x/net/http2/hpack"
|
||||||
"golang.org/x/net/lex/httplex"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const frameHeaderLen = 9
|
const frameHeaderLen = 9
|
||||||
|
@ -733,32 +733,67 @@ func (f *SettingsFrame) IsAck() bool {
|
||||||
return f.FrameHeader.Flags.Has(FlagSettingsAck)
|
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()
|
f.checkValid()
|
||||||
buf := f.p
|
for i := 0; i < f.NumSettings(); i++ {
|
||||||
for len(buf) > 0 {
|
if s := f.Setting(i); s.ID == id {
|
||||||
settingID := SettingID(binary.BigEndian.Uint16(buf[:2]))
|
return s.Val, true
|
||||||
if settingID == s {
|
|
||||||
return binary.BigEndian.Uint32(buf[2:6]), true
|
|
||||||
}
|
}
|
||||||
buf = buf[6:]
|
|
||||||
}
|
}
|
||||||
return 0, false
|
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.
|
// ForeachSetting runs fn for each setting.
|
||||||
// It stops and returns the first error.
|
// It stops and returns the first error.
|
||||||
func (f *SettingsFrame) ForeachSetting(fn func(Setting) error) error {
|
func (f *SettingsFrame) ForeachSetting(fn func(Setting) error) error {
|
||||||
f.checkValid()
|
f.checkValid()
|
||||||
buf := f.p
|
for i := 0; i < f.NumSettings(); i++ {
|
||||||
for len(buf) > 0 {
|
if err := fn(f.Setting(i)); err != nil {
|
||||||
if err := fn(Setting{
|
|
||||||
SettingID(binary.BigEndian.Uint16(buf[:2])),
|
|
||||||
binary.BigEndian.Uint32(buf[2:6]),
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
buf = buf[6:]
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1462,7 +1497,7 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
|
||||||
if VerboseLogs && fr.logReads {
|
if VerboseLogs && fr.logReads {
|
||||||
fr.debugReadLoggerf("http2: decoded hpack field %+v", hf)
|
fr.debugReadLoggerf("http2: decoded hpack field %+v", hf)
|
||||||
}
|
}
|
||||||
if !httplex.ValidHeaderFieldValue(hf.Value) {
|
if !httpguts.ValidHeaderFieldValue(hf.Value) {
|
||||||
invalid = headerFieldValueError(hf.Value)
|
invalid = headerFieldValueError(hf.Value)
|
||||||
}
|
}
|
||||||
isPseudo := strings.HasPrefix(hf.Name, ":")
|
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
|
context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errCanceled = context.Canceled
|
||||||
|
|
||||||
func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx contextContext, cancel func()) {
|
func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx contextContext, cancel func()) {
|
||||||
ctx, cancel = context.WithCancel(context.Background())
|
ctx, cancel = context.WithCancel(context.Background())
|
||||||
ctx = context.WithValue(ctx, http.LocalAddrContextKey, c.LocalAddr())
|
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 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) {
|
func traceGotConn(req *http.Request, cc *ClientConn) {
|
||||||
trace := httptrace.ContextClientTrace(req.Context())
|
trace := httptrace.ContextClientTrace(req.Context())
|
||||||
if trace == nil || trace.GotConn == nil {
|
if trace == nil || trace.GotConn == nil {
|
||||||
|
@ -104,3 +114,8 @@ func requestTrace(req *http.Request) *clientTrace {
|
||||||
func (cc *ClientConn) Ping(ctx context.Context) error {
|
func (cc *ClientConn) Ping(ctx context.Context) error {
|
||||||
return cc.ping(ctx)
|
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"
|
// 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
|
// s will be encoded in Huffman codes only when it produces strictly
|
||||||
// shorter byte string.
|
// shorter byte string.
|
||||||
|
|
|
@ -389,6 +389,12 @@ func (d *Decoder) callEmit(hf HeaderField) error {
|
||||||
|
|
||||||
// (same invariants and behavior as parseHeaderFieldRepr)
|
// (same invariants and behavior as parseHeaderFieldRepr)
|
||||||
func (d *Decoder) parseDynamicTableSizeUpdate() error {
|
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
|
buf := d.buf
|
||||||
size, buf, err := readVarInt(5, buf)
|
size, buf, err := readVarInt(5, buf)
|
||||||
if err != nil {
|
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
|
// If maxLen is greater than 0, attempts to write more to buf than
|
||||||
// maxLen bytes will return ErrStringLength.
|
// maxLen bytes will return ErrStringLength.
|
||||||
func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error {
|
func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error {
|
||||||
|
rootHuffmanNode := getRootHuffmanNode()
|
||||||
n := rootHuffmanNode
|
n := rootHuffmanNode
|
||||||
// cur is the bit buffer that has not been fed into n.
|
// 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.
|
// 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 {
|
type node struct {
|
||||||
// children is non-nil for internal nodes
|
// children is non-nil for internal nodes
|
||||||
children []*node
|
children *[256]*node
|
||||||
|
|
||||||
// The following are only valid if children is nil:
|
// The following are only valid if children is nil:
|
||||||
codeLen uint8 // number of bits that led to the output of sym
|
codeLen uint8 // number of bits that led to the output of sym
|
||||||
|
@ -114,22 +115,31 @@ type node struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newInternalNode() *node {
|
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 {
|
if len(huffmanCodes) != 256 {
|
||||||
panic("unexpected size")
|
panic("unexpected size")
|
||||||
}
|
}
|
||||||
|
lazyRootHuffmanNode = newInternalNode()
|
||||||
for i, code := range huffmanCodes {
|
for i, code := range huffmanCodes {
|
||||||
addDecoderNode(byte(i), code, huffmanCodeLen[i])
|
addDecoderNode(byte(i), code, huffmanCodeLen[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addDecoderNode(sym byte, code uint32, codeLen uint8) {
|
func addDecoderNode(sym byte, code uint32, codeLen uint8) {
|
||||||
cur := rootHuffmanNode
|
cur := lazyRootHuffmanNode
|
||||||
for codeLen > 8 {
|
for codeLen > 8 {
|
||||||
codeLen -= 8
|
codeLen -= 8
|
||||||
i := uint8(code >> codeLen)
|
i := uint8(code >> codeLen)
|
||||||
|
|
|
@ -29,7 +29,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/net/lex/httplex"
|
"golang.org/x/net/http/httpguts"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -179,7 +179,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// validWireHeaderFieldName reports whether v is a valid header field
|
// 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:
|
// Further, http2 says:
|
||||||
// "Just as in HTTP/1.x, header field names are strings of ASCII
|
// "Just as in HTTP/1.x, header field names are strings of ASCII
|
||||||
|
@ -191,7 +191,7 @@ func validWireHeaderFieldName(v string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, r := range v {
|
for _, r := range v {
|
||||||
if !httplex.IsTokenRune(r) {
|
if !httpguts.IsTokenRune(r) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if 'A' <= r && r <= 'Z' {
|
if 'A' <= r && r <= 'Z' {
|
||||||
|
@ -312,7 +312,7 @@ func mustUint31(v int32) uint32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// bodyAllowedForStatus reports whether a given response status code
|
// 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 {
|
func bodyAllowedForStatus(status int) bool {
|
||||||
switch {
|
switch {
|
||||||
case status >= 100 && status <= 199:
|
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 (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
@ -18,6 +19,8 @@ type contextContext interface {
|
||||||
Err() error
|
Err() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errCanceled = errors.New("canceled")
|
||||||
|
|
||||||
type fakeContext struct{}
|
type fakeContext struct{}
|
||||||
|
|
||||||
func (fakeContext) Done() <-chan struct{} { return nil }
|
func (fakeContext) Done() <-chan struct{} { return nil }
|
||||||
|
@ -34,6 +37,7 @@ func setResponseUncompressed(res *http.Response) {
|
||||||
type clientTrace struct{}
|
type clientTrace struct{}
|
||||||
|
|
||||||
func requestTrace(*http.Request) *clientTrace { return nil }
|
func requestTrace(*http.Request) *clientTrace { return nil }
|
||||||
|
func traceGetConn(*http.Request, string) {}
|
||||||
func traceGotConn(*http.Request, *ClientConn) {}
|
func traceGotConn(*http.Request, *ClientConn) {}
|
||||||
func traceFirstResponseByte(*clientTrace) {}
|
func traceFirstResponseByte(*clientTrace) {}
|
||||||
func traceWroteHeaders(*clientTrace) {}
|
func traceWroteHeaders(*clientTrace) {}
|
||||||
|
@ -84,4 +88,8 @@ func (cc *ClientConn) Ping(ctx contextContext) error {
|
||||||
return cc.ping(ctx)
|
return cc.ping(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cc *ClientConn) Shutdown(ctx contextContext) error {
|
||||||
|
return cc.shutdown(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Transport) idleConnTimeout() time.Duration { return 0 }
|
func (t *Transport) idleConnTimeout() time.Duration { return 0 }
|
||||||
|
|
|
@ -46,6 +46,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/http/httpguts"
|
||||||
"golang.org/x/net/http2/hpack"
|
"golang.org/x/net/http2/hpack"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -406,7 +407,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
||||||
// addresses during development.
|
// addresses during development.
|
||||||
//
|
//
|
||||||
// TODO: optionally enforce? Or enforce at the time we receive
|
// 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.
|
// But that precludes proxy situations, perhaps.
|
||||||
//
|
//
|
||||||
// So for now, do nothing here again.
|
// So for now, do nothing here again.
|
||||||
|
@ -1486,6 +1487,12 @@ func (sc *serverConn) processSettings(f *SettingsFrame) error {
|
||||||
}
|
}
|
||||||
return nil
|
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 {
|
if err := f.ForeachSetting(sc.processSetting); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1574,6 +1581,12 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
||||||
// type PROTOCOL_ERROR."
|
// type PROTOCOL_ERROR."
|
||||||
return ConnectionError(ErrCodeProtocol)
|
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 {
|
if st == nil || state != stateOpen || st.gotTrailerHeader || st.resetQueued {
|
||||||
// This includes sending a RST_STREAM if the stream is
|
// This includes sending a RST_STREAM if the stream is
|
||||||
// in stateHalfClosedLocal (which currently means that
|
// in stateHalfClosedLocal (which currently means that
|
||||||
|
@ -1607,7 +1620,10 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
||||||
// Sender sending more than they'd declared?
|
// Sender sending more than they'd declared?
|
||||||
if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes {
|
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))
|
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 {
|
if f.Length > 0 {
|
||||||
// Check whether the client has flow control quota.
|
// Check whether the client has flow control quota.
|
||||||
|
@ -1717,6 +1733,13 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
||||||
// processing this frame.
|
// processing this frame.
|
||||||
return nil
|
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)
|
return st.processTrailerHeaders(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1817,7 +1840,7 @@ func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
|
||||||
if st.trailer != nil {
|
if st.trailer != nil {
|
||||||
for _, hf := range f.RegularFields() {
|
for _, hf := range f.RegularFields() {
|
||||||
key := sc.canonicalHeader(hf.Name)
|
key := sc.canonicalHeader(hf.Name)
|
||||||
if !ValidTrailerHeader(key) {
|
if !httpguts.ValidTrailerHeader(key) {
|
||||||
// TODO: send more details to the peer somehow. But http2 has
|
// TODO: send more details to the peer somehow. But http2 has
|
||||||
// no way to send debug data at a stream level. Discuss with
|
// no way to send debug data at a stream level. Discuss with
|
||||||
// HTTP folk.
|
// 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.
|
// written in the trailers at the end of the response.
|
||||||
func (rws *responseWriterState) declareTrailer(k string) {
|
func (rws *responseWriterState) declareTrailer(k string) {
|
||||||
k = http.CanonicalHeaderKey(k)
|
k = http.CanonicalHeaderKey(k)
|
||||||
if !ValidTrailerHeader(k) {
|
if !httpguts.ValidTrailerHeader(k) {
|
||||||
// Forbidden by RFC 2616 14.40.
|
// Forbidden by RFC 7230, section 4.1.2.
|
||||||
rws.conn.logf("ignoring invalid trailer %q", k)
|
rws.conn.logf("ignoring invalid trailer %q", k)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -2335,6 +2358,19 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
||||||
foreachHeaderElement(v, rws.declareTrailer)
|
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
|
endStream := (rws.handlerDone && !rws.hasTrailers() && len(p) == 0) || isHeadResp
|
||||||
err = rws.conn.writeHeaders(rws.stream, &writeResHeaders{
|
err = rws.conn.writeHeaders(rws.stream, &writeResHeaders{
|
||||||
streamID: rws.stream.id,
|
streamID: rws.stream.id,
|
||||||
|
@ -2406,7 +2442,7 @@ const TrailerPrefix = "Trailer:"
|
||||||
// after the header has already been flushed. Because the Go
|
// after the header has already been flushed. Because the Go
|
||||||
// ResponseWriter interface has no way to set Trailers (only the
|
// ResponseWriter interface has no way to set Trailers (only the
|
||||||
// Header), and because we didn't want to expand the ResponseWriter
|
// 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
|
// says you SHOULD (but not must) predeclare any trailers in the
|
||||||
// header, the official ResponseWriter rules said trailers in Go must
|
// header, the official ResponseWriter rules said trailers in Go must
|
||||||
// be predeclared, and then we reuse the same ResponseWriter.Header()
|
// 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
|
// 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)) {
|
func foreachHeaderElement(v string, fn func(string)) {
|
||||||
v = textproto.TrimString(v)
|
v = textproto.TrimString(v)
|
||||||
if 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
|
// h1ServerKeepAlivesDisabled reports whether hs has its keep-alives
|
||||||
// disabled. See comments on h1ServerShutdownChan above for why
|
// disabled. See comments on h1ServerShutdownChan above for why
|
||||||
// the code is written this way.
|
// the code is written this way.
|
||||||
|
|
|
@ -21,15 +21,16 @@ import (
|
||||||
mathrand "math/rand"
|
mathrand "math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/textproto"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/http/httpguts"
|
||||||
"golang.org/x/net/http2/hpack"
|
"golang.org/x/net/http2/hpack"
|
||||||
"golang.org/x/net/idna"
|
"golang.org/x/net/idna"
|
||||||
"golang.org/x/net/lex/httplex"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -159,6 +160,7 @@ type ClientConn struct {
|
||||||
cond *sync.Cond // hold mu; broadcast on flow/closed changes
|
cond *sync.Cond // hold mu; broadcast on flow/closed changes
|
||||||
flow flow // our conn-level flow control quota (cs.flow is per stream)
|
flow flow // our conn-level flow control quota (cs.flow is per stream)
|
||||||
inflow flow // peer's conn-level flow control
|
inflow flow // peer's conn-level flow control
|
||||||
|
closing bool
|
||||||
closed bool
|
closed bool
|
||||||
wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back
|
wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back
|
||||||
goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received
|
goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received
|
||||||
|
@ -211,9 +213,10 @@ type clientStream struct {
|
||||||
done chan struct{} // closed when stream remove from cc.streams map; close calls guarded by cc.mu
|
done chan struct{} // closed when stream remove from cc.streams map; close calls guarded by cc.mu
|
||||||
|
|
||||||
// owned by clientConnReadLoop:
|
// owned by clientConnReadLoop:
|
||||||
firstByte bool // got the first response byte
|
firstByte bool // got the first response byte
|
||||||
pastHeaders bool // got first MetaHeadersFrame (actual headers)
|
pastHeaders bool // got first MetaHeadersFrame (actual headers)
|
||||||
pastTrailers bool // got optional second MetaHeadersFrame (trailers)
|
pastTrailers bool // got optional second MetaHeadersFrame (trailers)
|
||||||
|
num1xx uint8 // number of 1xx responses seen
|
||||||
|
|
||||||
trailer http.Header // accumulated trailers
|
trailer http.Header // accumulated trailers
|
||||||
resTrailer *http.Header // client's Response.Trailer
|
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
|
// 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
|
// 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,
|
// cc.streams map: peer reset, successful completion, TCP connection breakage,
|
||||||
|
@ -423,27 +437,36 @@ func shouldRetryRequest(req *http.Request, err error, afterBodyWrite bool) (*htt
|
||||||
if !canRetryError(err) {
|
if !canRetryError(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !afterBodyWrite {
|
|
||||||
return req, nil
|
|
||||||
}
|
|
||||||
// If the Body is nil (or http.NoBody), it's safe to reuse
|
// If the Body is nil (or http.NoBody), it's safe to reuse
|
||||||
// this request and its Body.
|
// this request and its Body.
|
||||||
if req.Body == nil || reqBodyIsNoBody(req.Body) {
|
if req.Body == nil || reqBodyIsNoBody(req.Body) {
|
||||||
return req, nil
|
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
|
getBody := reqGetBody(req) // Go 1.8: getBody = req.GetBody
|
||||||
if getBody == nil {
|
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)
|
// TODO: consider a req.Body.Close here? or audit that all caller paths do?
|
||||||
|
body, err := getBody()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
newReq := *req
|
||||||
|
newReq.Body = body
|
||||||
|
return &newReq, nil
|
||||||
}
|
}
|
||||||
body, err := getBody()
|
|
||||||
if err != nil {
|
// The Request.Body can't reset back to the beginning, but we
|
||||||
return nil, err
|
// 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
|
||||||
}
|
}
|
||||||
newReq := *req
|
|
||||||
newReq.Body = body
|
return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err)
|
||||||
return &newReq, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func canRetryError(err error) bool {
|
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?
|
// henc in response to SETTINGS frames?
|
||||||
cc.henc = hpack.NewEncoder(&cc.hbuf)
|
cc.henc = hpack.NewEncoder(&cc.hbuf)
|
||||||
|
|
||||||
|
if t.AllowHTTP {
|
||||||
|
cc.nextStreamID = 3
|
||||||
|
}
|
||||||
|
|
||||||
if cs, ok := c.(connectionStater); ok {
|
if cs, ok := c.(connectionStater); ok {
|
||||||
state := cs.ConnectionState()
|
state := cs.ConnectionState()
|
||||||
cc.tlsState = &state
|
cc.tlsState = &state
|
||||||
|
@ -626,12 +653,32 @@ func (cc *ClientConn) CanTakeNewRequest() bool {
|
||||||
return cc.canTakeNewRequestLocked()
|
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 {
|
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
|
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
|
// onIdleTimeout is called from a time.AfterFunc goroutine. It will
|
||||||
|
@ -661,6 +708,88 @@ func (cc *ClientConn) closeIfIdle() {
|
||||||
cc.tconn.Close()
|
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
|
const maxAllocFrameSize = 512 << 10
|
||||||
|
|
||||||
// frameBuffer returns a scratch buffer suitable for writing DATA frames.
|
// 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") {
|
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)
|
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 fmt.Errorf("http2: invalid Connection request header: %q", vv)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -931,6 +1060,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
cc.forgetStreamID(cs.ID)
|
||||||
return nil, cs.getStartedWrite(), err
|
return nil, cs.getStartedWrite(), err
|
||||||
}
|
}
|
||||||
bodyWritten = true
|
bodyWritten = true
|
||||||
|
@ -951,6 +1081,9 @@ func (cc *ClientConn) awaitOpenSlotForRequest(req *http.Request) error {
|
||||||
for {
|
for {
|
||||||
cc.lastActive = time.Now()
|
cc.lastActive = time.Now()
|
||||||
if cc.closed || !cc.canTakeNewRequestLocked() {
|
if cc.closed || !cc.canTakeNewRequestLocked() {
|
||||||
|
if waitingForConn != nil {
|
||||||
|
close(waitingForConn)
|
||||||
|
}
|
||||||
return errClientConnUnusable
|
return errClientConnUnusable
|
||||||
}
|
}
|
||||||
if int64(len(cc.streams))+1 <= int64(cc.maxConcurrentStreams) {
|
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
|
sawEOF = true
|
||||||
err = nil
|
err = nil
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
cc.writeStreamReset(cs.ID, ErrCodeCancel, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1174,7 +1308,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
|
||||||
if host == "" {
|
if host == "" {
|
||||||
host = req.URL.Host
|
host = req.URL.Host
|
||||||
}
|
}
|
||||||
host, err := httplex.PunycodeHostPort(host)
|
host, err := httpguts.PunycodeHostPort(host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// potentially pollute our hpack state. (We want to be able to
|
||||||
// continue to reuse the hpack encoder for future requests)
|
// continue to reuse the hpack encoder for future requests)
|
||||||
for k, vv := range req.Header {
|
for k, vv := range req.Header {
|
||||||
if !httplex.ValidHeaderFieldName(k) {
|
if !httpguts.ValidHeaderFieldName(k) {
|
||||||
return nil, fmt.Errorf("invalid HTTP header name %q", k)
|
return nil, fmt.Errorf("invalid HTTP header name %q", k)
|
||||||
}
|
}
|
||||||
for _, v := range vv {
|
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)
|
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
|
return nil, errRequestHeaderListSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace := requestTrace(req)
|
||||||
|
traceHeaders := traceHasWroteHeaderField(trace)
|
||||||
|
|
||||||
// Header list size is ok. Write the headers.
|
// Header list size is ok. Write the headers.
|
||||||
enumerateHeaders(func(name, value string) {
|
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
|
return cc.hbuf.Bytes(), nil
|
||||||
|
@ -1608,8 +1749,7 @@ func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
|
||||||
// is the detail.
|
// is the detail.
|
||||||
//
|
//
|
||||||
// As a special case, handleResponse may return (nil, nil) to skip the
|
// As a special case, handleResponse may return (nil, nil) to skip the
|
||||||
// frame (currently only used for 100 expect continue). This special
|
// frame (currently only used for 1xx responses).
|
||||||
// case is going away after Issue 13851 is fixed.
|
|
||||||
func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFrame) (*http.Response, error) {
|
func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFrame) (*http.Response, error) {
|
||||||
if f.Truncated {
|
if f.Truncated {
|
||||||
return nil, errResponseHeaderListSize
|
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")
|
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)
|
header := make(http.Header)
|
||||||
res := &http.Response{
|
res := &http.Response{
|
||||||
Proto: "HTTP/2.0",
|
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()
|
streamEnded := f.StreamEnded()
|
||||||
isHead := cs.req.Method == "HEAD"
|
isHead := cs.req.Method == "HEAD"
|
||||||
if !streamEnded || isHead {
|
if !streamEnded || isHead {
|
||||||
|
@ -2244,7 +2396,7 @@ func (t *Transport) getBodyWriterState(cs *clientStream, body io.Reader) (s body
|
||||||
}
|
}
|
||||||
s.delay = t.expectContinueTimeout()
|
s.delay = t.expectContinueTimeout()
|
||||||
if s.delay == 0 ||
|
if s.delay == 0 ||
|
||||||
!httplex.HeaderValuesContainsToken(
|
!httpguts.HeaderValuesContainsToken(
|
||||||
cs.req.Header["Expect"],
|
cs.req.Header["Expect"],
|
||||||
"100-continue") {
|
"100-continue") {
|
||||||
return
|
return
|
||||||
|
@ -2299,5 +2451,5 @@ func (s bodyWriterState) scheduleBodyWrite() {
|
||||||
// isConnectionCloseRequest reports whether req should use its own
|
// isConnectionCloseRequest reports whether req should use its own
|
||||||
// connection for a single request and then close the connection.
|
// connection for a single request and then close the connection.
|
||||||
func isConnectionCloseRequest(req *http.Request) bool {
|
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/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
"golang.org/x/net/http/httpguts"
|
||||||
"golang.org/x/net/http2/hpack"
|
"golang.org/x/net/http2/hpack"
|
||||||
"golang.org/x/net/lex/httplex"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// writeFramer is implemented by any type that is used to write frames.
|
// 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"
|
isTE := k == "transfer-encoding"
|
||||||
for _, v := range vv {
|
for _, v := range vv {
|
||||||
if !httplex.ValidHeaderFieldValue(v) {
|
if !httpguts.ValidHeaderFieldValue(v) {
|
||||||
// TODO: return an error? golang.org/issue/14048
|
// TODO: return an error? golang.org/issue/14048
|
||||||
// For now just omit it.
|
// For now just omit it.
|
||||||
continue
|
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",
|
importmap = "k8s.io/kubernetes/vendor/golang.org/x/net/proxy",
|
||||||
importpath = "golang.org/x/net/proxy",
|
importpath = "golang.org/x/net/proxy",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
|
deps = ["//vendor/golang.org/x/net/internal/socks:go_default_library"],
|
||||||
)
|
)
|
||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
|
|
|
@ -5,210 +5,32 @@
|
||||||
package proxy
|
package proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"context"
|
||||||
"io"
|
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
|
||||||
|
"golang.org/x/net/internal/socks"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
|
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given
|
||||||
// with an optional username and password. See RFC 1928 and RFC 1929.
|
// address with an optional username and password.
|
||||||
func SOCKS5(network, addr string, auth *Auth, forward Dialer) (Dialer, error) {
|
// See RFC 1928 and RFC 1929.
|
||||||
s := &socks5{
|
func SOCKS5(network, address string, auth *Auth, forward Dialer) (Dialer, error) {
|
||||||
network: network,
|
d := socks.NewDialer(network, address)
|
||||||
addr: addr,
|
if forward != nil {
|
||||||
forward: forward,
|
d.ProxyDial = func(_ context.Context, network string, address string) (net.Conn, error) {
|
||||||
|
return forward.Dial(network, address)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if auth != nil {
|
if auth != nil {
|
||||||
s.user = auth.User
|
up := socks.UsernamePassword{
|
||||||
s.password = auth.Password
|
Username: auth.User,
|
||||||
|
Password: auth.Password,
|
||||||
|
}
|
||||||
|
d.AuthMethods = []socks.AuthMethod{
|
||||||
|
socks.AuthMethodNotRequired,
|
||||||
|
socks.AuthMethodUsernamePassword,
|
||||||
|
}
|
||||||
|
d.Authenticate = up.Authenticate
|
||||||
}
|
}
|
||||||
|
return d, nil
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -110,6 +111,13 @@ var AuthRequest = func(req *http.Request) (any, sensitive bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
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?
|
// TODO(jbd): Serve Traces from /debug/traces in the future?
|
||||||
// There is no requirement for a request to be present to have traces.
|
// There is no requirement for a request to be present to have traces.
|
||||||
http.HandleFunc("/debug/requests", Traces)
|
http.HandleFunc("/debug/requests", Traces)
|
||||||
|
@ -368,7 +376,11 @@ func New(family, title string) Trace {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *trace) Finish() {
|
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 {
|
if DebugUseAfterFinish {
|
||||||
buf := make([]byte, 4<<10) // 4 KB should be enough
|
buf := make([]byte, 4<<10) // 4 KB should be enough
|
||||||
n := runtime.Stack(buf, false)
|
n := runtime.Stack(buf, false)
|
||||||
|
@ -381,14 +393,17 @@ func (tr *trace) Finish() {
|
||||||
m.Remove(tr)
|
m.Remove(tr)
|
||||||
|
|
||||||
f := getFamily(tr.Family, true)
|
f := getFamily(tr.Family, true)
|
||||||
|
tr.mu.RLock() // protects tr fields in Cond.match calls
|
||||||
for _, b := range f.Buckets {
|
for _, b := range f.Buckets {
|
||||||
if b.Cond.match(tr) {
|
if b.Cond.match(tr) {
|
||||||
b.Add(tr)
|
b.Add(tr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tr.mu.RUnlock()
|
||||||
|
|
||||||
// Add a sample of elapsed time as microseconds to the family's timeseries
|
// Add a sample of elapsed time as microseconds to the family's timeseries
|
||||||
h := new(histogram)
|
h := new(histogram)
|
||||||
h.addMeasurement(tr.Elapsed.Nanoseconds() / 1e3)
|
h.addMeasurement(elapsed.Nanoseconds() / 1e3)
|
||||||
f.LatencyMu.Lock()
|
f.LatencyMu.Lock()
|
||||||
f.Latency.Add(h)
|
f.Latency.Add(h)
|
||||||
f.LatencyMu.Unlock()
|
f.LatencyMu.Unlock()
|
||||||
|
@ -684,25 +699,20 @@ type trace struct {
|
||||||
// Title is the title of this trace.
|
// Title is the title of this trace.
|
||||||
Title string
|
Title string
|
||||||
|
|
||||||
// Timing information.
|
// Start time of the this trace.
|
||||||
Start time.Time
|
Start time.Time
|
||||||
Elapsed time.Duration // zero while active
|
|
||||||
|
|
||||||
// Trace information if non-zero.
|
|
||||||
traceID uint64
|
|
||||||
spanID uint64
|
|
||||||
|
|
||||||
// Whether this trace resulted in an error.
|
|
||||||
IsError bool
|
|
||||||
|
|
||||||
// Append-only sequence of events (modulo discards).
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
events []event
|
events []event // Append-only sequence of events (modulo discards).
|
||||||
maxEvents int
|
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
|
||||||
|
|
||||||
refs int32 // how many buckets this is in
|
refs int32 // how many buckets this is in
|
||||||
recycler func(interface{})
|
disc discarded // scratch space to avoid allocation
|
||||||
disc discarded // scratch space to avoid allocation
|
|
||||||
|
|
||||||
finishStack []byte // where finish was called, if DebugUseAfterFinish is set
|
finishStack []byte // where finish was called, if DebugUseAfterFinish is set
|
||||||
|
|
||||||
|
@ -714,14 +724,18 @@ func (tr *trace) reset() {
|
||||||
tr.Family = ""
|
tr.Family = ""
|
||||||
tr.Title = ""
|
tr.Title = ""
|
||||||
tr.Start = time.Time{}
|
tr.Start = time.Time{}
|
||||||
|
|
||||||
|
tr.mu.Lock()
|
||||||
tr.Elapsed = 0
|
tr.Elapsed = 0
|
||||||
tr.traceID = 0
|
tr.traceID = 0
|
||||||
tr.spanID = 0
|
tr.spanID = 0
|
||||||
tr.IsError = false
|
tr.IsError = false
|
||||||
tr.maxEvents = 0
|
tr.maxEvents = 0
|
||||||
tr.events = nil
|
tr.events = nil
|
||||||
tr.refs = 0
|
|
||||||
tr.recycler = nil
|
tr.recycler = nil
|
||||||
|
tr.mu.Unlock()
|
||||||
|
|
||||||
|
tr.refs = 0
|
||||||
tr.disc = 0
|
tr.disc = 0
|
||||||
tr.finishStack = nil
|
tr.finishStack = nil
|
||||||
for i := range tr.eventsBuf {
|
for i := range tr.eventsBuf {
|
||||||
|
@ -801,21 +815,31 @@ func (tr *trace) LazyPrintf(format string, a ...interface{}) {
|
||||||
tr.addEvent(&lazySprintf{format, a}, false, false)
|
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{})) {
|
func (tr *trace) SetRecycler(f func(interface{})) {
|
||||||
|
tr.mu.Lock()
|
||||||
tr.recycler = f
|
tr.recycler = f
|
||||||
|
tr.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *trace) SetTraceInfo(traceID, spanID uint64) {
|
func (tr *trace) SetTraceInfo(traceID, spanID uint64) {
|
||||||
|
tr.mu.Lock()
|
||||||
tr.traceID, tr.spanID = traceID, spanID
|
tr.traceID, tr.spanID = traceID, spanID
|
||||||
|
tr.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *trace) SetMaxEvents(m int) {
|
func (tr *trace) SetMaxEvents(m int) {
|
||||||
|
tr.mu.Lock()
|
||||||
// Always keep at least three events: first, discarded count, last.
|
// Always keep at least three events: first, discarded count, last.
|
||||||
if len(tr.events) == 0 && m > 3 {
|
if len(tr.events) == 0 && m > 3 {
|
||||||
tr.maxEvents = m
|
tr.maxEvents = m
|
||||||
}
|
}
|
||||||
|
tr.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *trace) ref() {
|
func (tr *trace) ref() {
|
||||||
|
@ -824,6 +848,7 @@ func (tr *trace) ref() {
|
||||||
|
|
||||||
func (tr *trace) unref() {
|
func (tr *trace) unref() {
|
||||||
if atomic.AddInt32(&tr.refs, -1) == 0 {
|
if atomic.AddInt32(&tr.refs, -1) == 0 {
|
||||||
|
tr.mu.RLock()
|
||||||
if tr.recycler != nil {
|
if tr.recycler != nil {
|
||||||
// freeTrace clears tr, so we hold tr.recycler and tr.events here.
|
// freeTrace clears tr, so we hold tr.recycler and tr.events here.
|
||||||
go func(f func(interface{}), es []event) {
|
go func(f func(interface{}), es []event) {
|
||||||
|
@ -834,6 +859,7 @@ func (tr *trace) unref() {
|
||||||
}
|
}
|
||||||
}(tr.recycler, tr.events)
|
}(tr.recycler, tr.events)
|
||||||
}
|
}
|
||||||
|
tr.mu.RUnlock()
|
||||||
|
|
||||||
freeTrace(tr)
|
freeTrace(tr)
|
||||||
}
|
}
|
||||||
|
@ -844,7 +870,10 @@ func (tr *trace) When() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *trace) ElapsedTime() string {
|
func (tr *trace) ElapsedTime() string {
|
||||||
|
tr.mu.RLock()
|
||||||
t := tr.Elapsed
|
t := tr.Elapsed
|
||||||
|
tr.mu.RUnlock()
|
||||||
|
|
||||||
if t == 0 {
|
if t == 0 {
|
||||||
// Active trace.
|
// Active trace.
|
||||||
t = time.Since(tr.Start)
|
t = time.Since(tr.Start)
|
||||||
|
|
|
@ -241,7 +241,10 @@ func (ws *Conn) Close() error {
|
||||||
return err1
|
return err1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsClientConn reports whether ws is a client-side connection.
|
||||||
func (ws *Conn) IsClientConn() bool { return ws.request == nil }
|
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 }
|
func (ws *Conn) IsServerConn() bool { return ws.request != nil }
|
||||||
|
|
||||||
// LocalAddr returns the WebSocket Origin for the connection for client, or
|
// LocalAddr returns the WebSocket Origin for the connection for client, or
|
||||||
|
|
Loading…
Reference in New Issue