aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Pivotto <roidelapluie@inuits.eu>2020-05-01 14:26:51 +0200
committerGitHub <noreply@github.com>2020-05-01 14:26:51 +0200
commit202ecf9c9d1d1960cc9cac24838d13e9cff5edca (patch)
tree114dd7799aad5b6d8241bcf4756565ed16aa7f59
parentb42819b69d4d6f81c9b13ab6c7cefcb7aa6a395a (diff)
downloadprometheus_node_collector-202ecf9c9d1d1960cc9cac24838d13e9cff5edca.tar.bz2
prometheus_node_collector-202ecf9c9d1d1960cc9cac24838d13e9cff5edca.tar.xz
prometheus_node_collector-202ecf9c9d1d1960cc9cac24838d13e9cff5edca.zip
Add basic authentication (#1683)
* Add basic authentication Signed-off-by: Julien Pivotto <roidelapluie@inuits.eu>
-rw-r--r--CHANGELOG.md1
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rw-r--r--https/README.md23
-rw-r--r--https/testdata/tls_config_auth_user_list_invalid.bad.yml5
-rw-r--r--https/testdata/tls_config_junk_key.yml2
-rw-r--r--https/testdata/tls_config_noAuth_certPath_keyPath_empty.bad.yml3
-rw-r--r--https/testdata/tls_config_users.good.yml8
-rw-r--r--https/testdata/tls_config_users_noTLS.good.yml5
-rw-r--r--https/tls_config.go110
-rw-r--r--https/tls_config_test.go117
-rw-r--r--https/users.go73
-rw-r--r--node_exporter.go7
-rw-r--r--vendor/github.com/mwitkow/go-conntrack/.gitignore163
-rw-r--r--vendor/github.com/mwitkow/go-conntrack/.travis.yml13
-rw-r--r--vendor/github.com/mwitkow/go-conntrack/LICENSE201
-rw-r--r--vendor/github.com/mwitkow/go-conntrack/README.md88
-rw-r--r--vendor/github.com/mwitkow/go-conntrack/dialer_reporter.go108
-rw-r--r--vendor/github.com/mwitkow/go-conntrack/dialer_wrapper.go166
-rw-r--r--vendor/github.com/mwitkow/go-conntrack/listener_reporter.go43
-rw-r--r--vendor/github.com/mwitkow/go-conntrack/listener_wrapper.go137
-rw-r--r--vendor/github.com/prometheus/common/config/config.go34
-rw-r--r--vendor/github.com/prometheus/common/config/http_config.go472
-rw-r--r--vendor/golang.org/x/crypto/AUTHORS3
-rw-r--r--vendor/golang.org/x/crypto/CONTRIBUTORS3
-rw-r--r--vendor/golang.org/x/crypto/LICENSE27
-rw-r--r--vendor/golang.org/x/crypto/PATENTS22
-rw-r--r--vendor/golang.org/x/crypto/bcrypt/base64.go35
-rw-r--r--vendor/golang.org/x/crypto/bcrypt/bcrypt.go295
-rw-r--r--vendor/golang.org/x/crypto/blowfish/block.go159
-rw-r--r--vendor/golang.org/x/crypto/blowfish/cipher.go99
-rw-r--r--vendor/golang.org/x/crypto/blowfish/const.go199
-rw-r--r--vendor/golang.org/x/net/internal/timeseries/timeseries.go525
-rw-r--r--vendor/golang.org/x/net/trace/events.go532
-rw-r--r--vendor/golang.org/x/net/trace/histogram.go365
-rw-r--r--vendor/golang.org/x/net/trace/trace.go1130
-rw-r--r--vendor/modules.txt8
37 files changed, 5132 insertions, 52 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d78f005..6e8a795 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
2 2
3* [CHANGE] 3* [CHANGE]
4* [FEATURE] 4* [FEATURE]
5* [FEATURE] Add basic authentication #1673
5* [ENHANCEMENT] Add model_name and stepping to node_cpu_info metric #1617 6* [ENHANCEMENT] Add model_name and stepping to node_cpu_info metric #1617
6* [ENHANCEMENT] Add metrics for IO errors and retires on Darwin. #1636 7* [ENHANCEMENT] Add metrics for IO errors and retires on Darwin. #1636
7* [BUGFIX] collector/systemd: use regexp to extract systemd version #1647 8* [BUGFIX] collector/systemd: use regexp to extract systemd version #1647
diff --git a/go.mod b/go.mod
index c84801a..de7db9d 100644
--- a/go.mod
+++ b/go.mod
@@ -23,6 +23,7 @@ require (
23 github.com/soundcloud/go-runit v0.0.0-20150630195641-06ad41a06c4a 23 github.com/soundcloud/go-runit v0.0.0-20150630195641-06ad41a06c4a
24 go.uber.org/atomic v1.5.1 // indirect 24 go.uber.org/atomic v1.5.1 // indirect
25 go.uber.org/multierr v1.4.0 // indirect 25 go.uber.org/multierr v1.4.0 // indirect
26 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
26 golang.org/x/lint v0.0.0-20200130185559-910be7a94367 // indirect 27 golang.org/x/lint v0.0.0-20200130185559-910be7a94367 // indirect
27 golang.org/x/sys v0.0.0-20200217220822-9197077df867 28 golang.org/x/sys v0.0.0-20200217220822-9197077df867
28 golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 // indirect 29 golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 // indirect
diff --git a/go.sum b/go.sum
index 1b43a8b..0d58055 100644
--- a/go.sum
+++ b/go.sum
@@ -208,6 +208,7 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
208github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 208github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
209github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 209github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
210github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 210github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
211github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc=
211github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 212github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
212github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= 213github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
213github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= 214github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
@@ -338,6 +339,7 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf
338golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 339golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
339golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 340golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
340golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 341golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
342golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
341golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 343golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
342golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 344golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
343golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 345golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
diff --git a/https/README.md b/https/README.md
index e8e4504..70b321e 100644
--- a/https/README.md
+++ b/https/README.md
@@ -25,4 +25,27 @@ tls_config:
25 25
26 # CA certificate for client certificate authentication to the server 26 # CA certificate for client certificate authentication to the server
27 [ client_ca_file: <filename> ] 27 [ client_ca_file: <filename> ]
28
29# List of usernames and hashed passwords that have full access to the web
30# server via basic authentication. If empty, no basic authentication is
31# required. Passwords are hashed with bcrypt.
32basic_auth_users:
33 [ <username>: <password> ... ]
28``` 34```
35
36## About bcrypt
37
38There are several tools out there to generate bcrypt passwords, e.g.
39[htpasswd](https://httpd.apache.org/docs/2.4/programs/htpasswd.html):
40
41`htpasswd -nBC 10 "" | tr -d ':\n`
42
43That command will prompt you for a password and output the hashed password,
44which will look something like:
45`$2y$10$X0h1gDsPszWURQaxFh.zoubFi6DXncSjhoQNJgRrnGs7EsimhC7zG`
46
47The cost (10 in the example) influences the time it takes for computing the
48hash. A higher cost will en up slowing down the authentication process.
49Depending on the machine, a cost of 10 will take about ~70ms where a cost of
5018 can take up to a few seconds. That hash will be computed on every
51password-protected request.
diff --git a/https/testdata/tls_config_auth_user_list_invalid.bad.yml b/https/testdata/tls_config_auth_user_list_invalid.bad.yml
new file mode 100644
index 0000000..90c1d95
--- /dev/null
+++ b/https/testdata/tls_config_auth_user_list_invalid.bad.yml
@@ -0,0 +1,5 @@
1tls_config :
2 cert_file : "testdata/server.crt"
3 key_file : "testdata/server.key"
4basic_auth_users:
5 john: doe
diff --git a/https/testdata/tls_config_junk_key.yml b/https/testdata/tls_config_junk_key.yml
new file mode 100644
index 0000000..acb2cc3
--- /dev/null
+++ b/https/testdata/tls_config_junk_key.yml
@@ -0,0 +1,2 @@
1tls_config :
2 cert_filse: "testdata/server.crt"
diff --git a/https/testdata/tls_config_noAuth_certPath_keyPath_empty.bad.yml b/https/testdata/tls_config_noAuth_certPath_keyPath_empty.bad.yml
index 2ed9195..1511b5a 100644
--- a/https/testdata/tls_config_noAuth_certPath_keyPath_empty.bad.yml
+++ b/https/testdata/tls_config_noAuth_certPath_keyPath_empty.bad.yml
@@ -1,3 +1,4 @@
1tls_config : 1tls_config :
2 cert_file : "" 2 cert_file : ""
3 key_file : "" \ No newline at end of file 3 key_file : ""
4 client_auth_type: "x"
diff --git a/https/testdata/tls_config_users.good.yml b/https/testdata/tls_config_users.good.yml
new file mode 100644
index 0000000..278177d
--- /dev/null
+++ b/https/testdata/tls_config_users.good.yml
@@ -0,0 +1,8 @@
1tls_config :
2 cert_file : "testdata/server.crt"
3 key_file : "testdata/server.key"
4basic_auth_users:
5 alice: $2y$12$1DpfPeqF9HzHJt.EWswy1exHluGfbhnn3yXhR7Xes6m3WJqFg0Wby
6 bob: $2y$18$4VeFDzXIoPHKnKTU3O3GH.N.vZu06CVqczYZ8WvfzrddFU6tGqjR.
7 carol: $2y$10$qRTBuFoULoYNA7AQ/F3ck.trZBPyjV64.oA4ZsSBCIWvXuvQlQTuu
8 dave: $2y$10$2UXri9cIDdgeKjBo4Rlpx.U3ZLDV8X1IxKmsfOvhcM5oXQt/mLmXq
diff --git a/https/testdata/tls_config_users_noTLS.good.yml b/https/testdata/tls_config_users_noTLS.good.yml
new file mode 100644
index 0000000..d3a7987
--- /dev/null
+++ b/https/testdata/tls_config_users_noTLS.good.yml
@@ -0,0 +1,5 @@
1basic_auth_users:
2 alice: $2y$12$1DpfPeqF9HzHJt.EWswy1exHluGfbhnn3yXhR7Xes6m3WJqFg0Wby
3 bob: $2y$18$4VeFDzXIoPHKnKTU3O3GH.N.vZu06CVqczYZ8WvfzrddFU6tGqjR.
4 carol: $2y$10$qRTBuFoULoYNA7AQ/F3ck.trZBPyjV64.oA4ZsSBCIWvXuvQlQTuu
5 dave: $2y$10$2UXri9cIDdgeKjBo4Rlpx.U3ZLDV8X1IxKmsfOvhcM5oXQt/mLmXq
diff --git a/https/tls_config.go b/https/tls_config.go
index 4b29862..44e57e9 100644
--- a/https/tls_config.go
+++ b/https/tls_config.go
@@ -20,12 +20,20 @@ import (
20 "io/ioutil" 20 "io/ioutil"
21 "net/http" 21 "net/http"
22 22
23 "github.com/go-kit/kit/log"
24 "github.com/go-kit/kit/log/level"
23 "github.com/pkg/errors" 25 "github.com/pkg/errors"
26 config_util "github.com/prometheus/common/config"
24 "gopkg.in/yaml.v2" 27 "gopkg.in/yaml.v2"
25) 28)
26 29
30var (
31 errNoTLSConfig = errors.New("TLS config is not present")
32)
33
27type Config struct { 34type Config struct {
28 TLSConfig TLSStruct `yaml:"tls_config"` 35 TLSConfig TLSStruct `yaml:"tls_config"`
36 Users map[string]config_util.Secret `yaml:"basic_auth_users"`
29} 37}
30 38
31type TLSStruct struct { 39type TLSStruct struct {
@@ -35,13 +43,18 @@ type TLSStruct struct {
35 ClientCAs string `yaml:"client_ca_file"` 43 ClientCAs string `yaml:"client_ca_file"`
36} 44}
37 45
38func getTLSConfig(configPath string) (*tls.Config, error) { 46func getConfig(configPath string) (*Config, error) {
39 content, err := ioutil.ReadFile(configPath) 47 content, err := ioutil.ReadFile(configPath)
40 if err != nil { 48 if err != nil {
41 return nil, err 49 return nil, err
42 } 50 }
43 c := &Config{} 51 c := &Config{}
44 err = yaml.Unmarshal(content, c) 52 err = yaml.UnmarshalStrict(content, c)
53 return c, err
54}
55
56func getTLSConfig(configPath string) (*tls.Config, error) {
57 c, err := getConfig(configPath)
45 if err != nil { 58 if err != nil {
46 return nil, err 59 return nil, err
47 } 60 }
@@ -50,14 +63,18 @@ func getTLSConfig(configPath string) (*tls.Config, error) {
50 63
51// ConfigToTLSConfig generates the golang tls.Config from the TLSStruct config. 64// ConfigToTLSConfig generates the golang tls.Config from the TLSStruct config.
52func ConfigToTLSConfig(c *TLSStruct) (*tls.Config, error) { 65func ConfigToTLSConfig(c *TLSStruct) (*tls.Config, error) {
53 cfg := &tls.Config{ 66 if c.TLSCertPath == "" && c.TLSKeyPath == "" && c.ClientAuth == "" && c.ClientCAs == "" {
54 MinVersion: tls.VersionTLS12, 67 return nil, errNoTLSConfig
55 } 68 }
56 if len(c.TLSCertPath) == 0 { 69
57 return nil, errors.New("missing TLSCertPath") 70 if c.TLSCertPath == "" {
71 return nil, errors.New("missing cert_file")
58 } 72 }
59 if len(c.TLSKeyPath) == 0 { 73 if c.TLSKeyPath == "" {
60 return nil, errors.New("missing TLSKeyPath") 74 return nil, errors.New("missing key_file")
75 }
76 cfg := &tls.Config{
77 MinVersion: tls.VersionTLS12,
61 } 78 }
62 loadCert := func() (*tls.Certificate, error) { 79 loadCert := func() (*tls.Certificate, error) {
63 cert, err := tls.LoadX509KeyPair(c.TLSCertPath, c.TLSKeyPath) 80 cert, err := tls.LoadX509KeyPair(c.TLSCertPath, c.TLSKeyPath)
@@ -74,7 +91,7 @@ func ConfigToTLSConfig(c *TLSStruct) (*tls.Config, error) {
74 return loadCert() 91 return loadCert()
75 } 92 }
76 93
77 if len(c.ClientCAs) > 0 { 94 if c.ClientCAs != "" {
78 clientCAPool := x509.NewCertPool() 95 clientCAPool := x509.NewCertPool()
79 clientCAFile, err := ioutil.ReadFile(c.ClientCAs) 96 clientCAFile, err := ioutil.ReadFile(c.ClientCAs)
80 if err != nil { 97 if err != nil {
@@ -83,40 +100,67 @@ func ConfigToTLSConfig(c *TLSStruct) (*tls.Config, error) {
83 clientCAPool.AppendCertsFromPEM(clientCAFile) 100 clientCAPool.AppendCertsFromPEM(clientCAFile)
84 cfg.ClientCAs = clientCAPool 101 cfg.ClientCAs = clientCAPool
85 } 102 }
86 if len(c.ClientAuth) > 0 { 103
87 switch s := (c.ClientAuth); s { 104 switch c.ClientAuth {
88 case "NoClientCert": 105 case "RequestClientCert":
89 cfg.ClientAuth = tls.NoClientCert 106 cfg.ClientAuth = tls.RequestClientCert
90 case "RequestClientCert": 107 case "RequireClientCert":
91 cfg.ClientAuth = tls.RequestClientCert 108 cfg.ClientAuth = tls.RequireAnyClientCert
92 case "RequireClientCert": 109 case "VerifyClientCertIfGiven":
93 cfg.ClientAuth = tls.RequireAnyClientCert 110 cfg.ClientAuth = tls.VerifyClientCertIfGiven
94 case "VerifyClientCertIfGiven": 111 case "RequireAndVerifyClientCert":
95 cfg.ClientAuth = tls.VerifyClientCertIfGiven 112 cfg.ClientAuth = tls.RequireAndVerifyClientCert
96 case "RequireAndVerifyClientCert": 113 case "", "NoClientCert":
97 cfg.ClientAuth = tls.RequireAndVerifyClientCert 114 cfg.ClientAuth = tls.NoClientCert
98 case "": 115 default:
99 cfg.ClientAuth = tls.NoClientCert 116 return nil, errors.New("Invalid ClientAuth: " + c.ClientAuth)
100 default:
101 return nil, errors.New("Invalid ClientAuth: " + s)
102 }
103 } 117 }
104 if len(c.ClientCAs) > 0 && cfg.ClientAuth == tls.NoClientCert { 118
119 if c.ClientCAs != "" && cfg.ClientAuth == tls.NoClientCert {
105 return nil, errors.New("Client CA's have been configured without a Client Auth Policy") 120 return nil, errors.New("Client CA's have been configured without a Client Auth Policy")
106 } 121 }
122
107 return cfg, nil 123 return cfg, nil
108} 124}
109 125
110// Listen starts the server on the given address. If tlsConfigPath isn't empty the server connection will be started using TLS. 126// Listen starts the server on the given address. If tlsConfigPath isn't empty the server connection will be started using TLS.
111func Listen(server *http.Server, tlsConfigPath string) error { 127func Listen(server *http.Server, tlsConfigPath string, logger log.Logger) error {
112 if (tlsConfigPath) == "" { 128 if tlsConfigPath == "" {
129 level.Info(logger).Log("msg", "TLS is disabled and it cannot be enabled on the fly.")
113 return server.ListenAndServe() 130 return server.ListenAndServe()
114 } 131 }
115 var err error 132
116 server.TLSConfig, err = getTLSConfig(tlsConfigPath) 133 if err := validateUsers(tlsConfigPath); err != nil {
117 if err != nil {
118 return err 134 return err
119 } 135 }
136
137 // Setup basic authentication.
138 var handler http.Handler = http.DefaultServeMux
139 if server.Handler != nil {
140 handler = server.Handler
141 }
142 server.Handler = &userAuthRoundtrip{
143 tlsConfigPath: tlsConfigPath,
144 logger: logger,
145 handler: handler,
146 }
147
148 config, err := getTLSConfig(tlsConfigPath)
149 switch err {
150 case nil:
151 // Valid TLS config.
152 level.Info(logger).Log("msg", "TLS is enabled and it cannot be disabled on the fly.")
153 case errNoTLSConfig:
154 // No TLS config, back to plain HTTP.
155 level.Info(logger).Log("msg", "TLS is disabled and it cannot be enabled on the fly.")
156 return server.ListenAndServe()
157 default:
158 // Invalid TLS config.
159 return err
160 }
161
162 server.TLSConfig = config
163
120 // Set the GetConfigForClient method of the HTTPS server so that the config 164 // Set the GetConfigForClient method of the HTTPS server so that the config
121 // and certs are reloaded on new connections. 165 // and certs are reloaded on new connections.
122 server.TLSConfig.GetConfigForClient = func(*tls.ClientHelloInfo) (*tls.Config, error) { 166 server.TLSConfig.GetConfigForClient = func(*tls.ClientHelloInfo) (*tls.Config, error) {
diff --git a/https/tls_config_test.go b/https/tls_config_test.go
index 4b1b4e0..07f412a 100644
--- a/https/tls_config_test.go
+++ b/https/tls_config_test.go
@@ -28,7 +28,8 @@ import (
28) 28)
29 29
30var ( 30var (
31 port = getPort() 31 port = getPort()
32 testlogger = &testLogger{}
32 33
33 ErrorMap = map[string]*regexp.Regexp{ 34 ErrorMap = map[string]*regexp.Regexp{
34 "HTTP Response to HTTPS": regexp.MustCompile(`server gave HTTP response to HTTPS client`), 35 "HTTP Response to HTTPS": regexp.MustCompile(`server gave HTTP response to HTTPS client`),
@@ -38,12 +39,21 @@ var (
38 "Invalid ClientAuth": regexp.MustCompile(`invalid ClientAuth`), 39 "Invalid ClientAuth": regexp.MustCompile(`invalid ClientAuth`),
39 "TLS handshake": regexp.MustCompile(`tls`), 40 "TLS handshake": regexp.MustCompile(`tls`),
40 "HTTP Request to HTTPS server": regexp.MustCompile(`HTTP`), 41 "HTTP Request to HTTPS server": regexp.MustCompile(`HTTP`),
41 "Invalid CertPath": regexp.MustCompile(`missing TLSCertPath`), 42 "Invalid CertPath": regexp.MustCompile(`missing cert_file`),
42 "Invalid KeyPath": regexp.MustCompile(`missing TLSKeyPath`), 43 "Invalid KeyPath": regexp.MustCompile(`missing key_file`),
43 "ClientCA set without policy": regexp.MustCompile(`Client CA's have been configured without a Client Auth Policy`), 44 "ClientCA set without policy": regexp.MustCompile(`Client CA's have been configured without a Client Auth Policy`),
45 "Bad password": regexp.MustCompile(`hashedSecret too short to be a bcrypted password`),
46 "Unauthorized": regexp.MustCompile(`Unauthorized`),
47 "Forbidden": regexp.MustCompile(`Forbidden`),
44 } 48 }
45) 49)
46 50
51type testLogger struct{}
52
53func (t *testLogger) Log(keyvals ...interface{}) error {
54 return nil
55}
56
47func getPort() string { 57func getPort() string {
48 listener, err := net.Listen("tcp", ":0") 58 listener, err := net.Listen("tcp", ":0")
49 if err != nil { 59 if err != nil {
@@ -61,6 +71,8 @@ type TestInputs struct {
61 YAMLConfigPath string 71 YAMLConfigPath string
62 ExpectedError *regexp.Regexp 72 ExpectedError *regexp.Regexp
63 UseTLSClient bool 73 UseTLSClient bool
74 Username string
75 Password string
64} 76}
65 77
66func TestYAMLFiles(t *testing.T) { 78func TestYAMLFiles(t *testing.T) {
@@ -73,7 +85,7 @@ func TestYAMLFiles(t *testing.T) {
73 { 85 {
74 Name: `empty config yml`, 86 Name: `empty config yml`,
75 YAMLConfigPath: "testdata/tls_config_empty.yml", 87 YAMLConfigPath: "testdata/tls_config_empty.yml",
76 ExpectedError: ErrorMap["Invalid CertPath"], 88 ExpectedError: nil,
77 }, 89 },
78 { 90 {
79 Name: `invalid config yml (invalid structure)`, 91 Name: `invalid config yml (invalid structure)`,
@@ -81,6 +93,11 @@ func TestYAMLFiles(t *testing.T) {
81 ExpectedError: ErrorMap["YAML error"], 93 ExpectedError: ErrorMap["YAML error"],
82 }, 94 },
83 { 95 {
96 Name: `invalid config yml (invalid key)`,
97 YAMLConfigPath: "testdata/tls_config_junk_key.yml",
98 ExpectedError: ErrorMap["YAML error"],
99 },
100 {
84 Name: `invalid config yml (cert path empty)`, 101 Name: `invalid config yml (cert path empty)`,
85 YAMLConfigPath: "testdata/tls_config_noAuth_certPath_empty.bad.yml", 102 YAMLConfigPath: "testdata/tls_config_noAuth_certPath_empty.bad.yml",
86 ExpectedError: ErrorMap["Invalid CertPath"], 103 ExpectedError: ErrorMap["Invalid CertPath"],
@@ -120,6 +137,11 @@ func TestYAMLFiles(t *testing.T) {
120 YAMLConfigPath: "testdata/tls_config_auth_clientCAs_invalid.bad.yml", 137 YAMLConfigPath: "testdata/tls_config_auth_clientCAs_invalid.bad.yml",
121 ExpectedError: ErrorMap["No such file"], 138 ExpectedError: ErrorMap["No such file"],
122 }, 139 },
140 {
141 Name: `invalid config yml (invalid user list)`,
142 YAMLConfigPath: "testdata/tls_config_auth_user_list_invalid.bad.yml",
143 ExpectedError: ErrorMap["Bad password"],
144 },
123 } 145 }
124 for _, testInputs := range testTables { 146 for _, testInputs := range testTables {
125 t.Run(testInputs.Name, testInputs.Test) 147 t.Run(testInputs.Name, testInputs.Test)
@@ -189,7 +211,7 @@ func TestConfigReloading(t *testing.T) {
189 recordConnectionError(errors.New("Panic starting server")) 211 recordConnectionError(errors.New("Panic starting server"))
190 } 212 }
191 }() 213 }()
192 err := Listen(server, badYAMLPath) 214 err := Listen(server, badYAMLPath, testlogger)
193 recordConnectionError(err) 215 recordConnectionError(err)
194 }() 216 }()
195 217
@@ -266,21 +288,28 @@ func (test *TestInputs) Test(t *testing.T) {
266 recordConnectionError(errors.New("Panic starting server")) 288 recordConnectionError(errors.New("Panic starting server"))
267 } 289 }
268 }() 290 }()
269 err := Listen(server, test.YAMLConfigPath) 291 err := Listen(server, test.YAMLConfigPath, testlogger)
270 recordConnectionError(err) 292 recordConnectionError(err)
271 }() 293 }()
272 294
273 var ClientConnection func() (*http.Response, error) 295 ClientConnection := func() (*http.Response, error) {
274 if test.UseTLSClient { 296 var client *http.Client
275 ClientConnection = func() (*http.Response, error) { 297 var proto string
276 client := getTLSClient() 298 if test.UseTLSClient {
277 return client.Get("https://localhost" + port) 299 client = getTLSClient()
300 proto = "https"
301 } else {
302 client = http.DefaultClient
303 proto = "http"
278 } 304 }
279 } else { 305 req, err := http.NewRequest("GET", proto+"://localhost"+port, nil)
280 ClientConnection = func() (*http.Response, error) { 306 if err != nil {
281 client := http.DefaultClient 307 t.Error(err)
282 return client.Get("http://localhost" + port)
283 } 308 }
309 if test.Username != "" {
310 req.SetBasicAuth(test.Username, test.Password)
311 }
312 return client.Do(req)
284 } 313 }
285 go func() { 314 go func() {
286 time.Sleep(250 * time.Millisecond) 315 time.Sleep(250 * time.Millisecond)
@@ -360,3 +389,61 @@ func swapFileContents(file1, file2 string) error {
360 } 389 }
361 return nil 390 return nil
362} 391}
392
393func TestUsers(t *testing.T) {
394 testTables := []*TestInputs{
395 {
396 Name: `without basic auth`,
397 YAMLConfigPath: "testdata/tls_config_users_noTLS.good.yml",
398 ExpectedError: ErrorMap["Unauthorized"],
399 },
400 {
401 Name: `with correct basic auth`,
402 YAMLConfigPath: "testdata/tls_config_users_noTLS.good.yml",
403 Username: "dave",
404 Password: "dave123",
405 ExpectedError: nil,
406 },
407 {
408 Name: `without basic auth and TLS`,
409 YAMLConfigPath: "testdata/tls_config_users.good.yml",
410 UseTLSClient: true,
411 ExpectedError: ErrorMap["Unauthorized"],
412 },
413 {
414 Name: `with correct basic auth and TLS`,
415 YAMLConfigPath: "testdata/tls_config_users.good.yml",
416 UseTLSClient: true,
417 Username: "dave",
418 Password: "dave123",
419 ExpectedError: nil,
420 },
421 {
422 Name: `with another correct basic auth and TLS`,
423 YAMLConfigPath: "testdata/tls_config_users.good.yml",
424 UseTLSClient: true,
425 Username: "carol",
426 Password: "carol123",
427 ExpectedError: nil,
428 },
429 {
430 Name: `with bad password and TLS`,
431 YAMLConfigPath: "testdata/tls_config_users.good.yml",
432 UseTLSClient: true,
433 Username: "dave",
434 Password: "bad",
435 ExpectedError: ErrorMap["Forbidden"],
436 },
437 {
438 Name: `with bad username and TLS`,
439 YAMLConfigPath: "testdata/tls_config_users.good.yml",
440 UseTLSClient: true,
441 Username: "nonexistent",
442 Password: "nonexistent",
443 ExpectedError: ErrorMap["Forbidden"],
444 },
445 }
446 for _, testInputs := range testTables {
447 t.Run(testInputs.Name, testInputs.Test)
448 }
449}
diff --git a/https/users.go b/https/users.go
new file mode 100644
index 0000000..170c87b
--- /dev/null
+++ b/https/users.go
@@ -0,0 +1,73 @@
1// Copyright 2020 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package https
15
16import (
17 "net/http"
18
19 "github.com/go-kit/kit/log"
20 "golang.org/x/crypto/bcrypt"
21)
22
23func validateUsers(configPath string) error {
24 c, err := getConfig(configPath)
25 if err != nil {
26 return err
27 }
28
29 for _, p := range c.Users {
30 _, err = bcrypt.Cost([]byte(p))
31 if err != nil {
32 return err
33 }
34 }
35
36 return nil
37}
38
39type userAuthRoundtrip struct {
40 tlsConfigPath string
41 handler http.Handler
42 logger log.Logger
43}
44
45func (u *userAuthRoundtrip) ServeHTTP(w http.ResponseWriter, r *http.Request) {
46 c, err := getConfig(u.tlsConfigPath)
47 if err != nil {
48 u.logger.Log("msg", "Unable to parse configuration", "err", err)
49 http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
50 return
51 }
52
53 if len(c.Users) == 0 {
54 u.handler.ServeHTTP(w, r)
55 return
56 }
57
58 user, pass, ok := r.BasicAuth()
59 if !ok {
60 w.Header().Set("WWW-Authenticate", "Basic")
61 http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
62 return
63 }
64
65 if hashedPassword, ok := c.Users[user]; ok {
66 if err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(pass)); err == nil {
67 u.handler.ServeHTTP(w, r)
68 return
69 }
70 }
71
72 http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
73}
diff --git a/node_exporter.go b/node_exporter.go
index 800be92..d195372 100644
--- a/node_exporter.go
+++ b/node_exporter.go
@@ -15,13 +15,14 @@ package main
15 15
16import ( 16import (
17 "fmt" 17 "fmt"
18 "github.com/prometheus/common/promlog"
19 "github.com/prometheus/common/promlog/flag"
20 "net/http" 18 "net/http"
21 _ "net/http/pprof" 19 _ "net/http/pprof"
22 "os" 20 "os"
23 "sort" 21 "sort"
24 22
23 "github.com/prometheus/common/promlog"
24 "github.com/prometheus/common/promlog/flag"
25
25 "github.com/go-kit/kit/log" 26 "github.com/go-kit/kit/log"
26 "github.com/go-kit/kit/log/level" 27 "github.com/go-kit/kit/log/level"
27 "github.com/prometheus/client_golang/prometheus" 28 "github.com/prometheus/client_golang/prometheus"
@@ -189,7 +190,7 @@ func main() {
189 190
190 level.Info(logger).Log("msg", "Listening on", "address", *listenAddress) 191 level.Info(logger).Log("msg", "Listening on", "address", *listenAddress)
191 server := &http.Server{Addr: *listenAddress} 192 server := &http.Server{Addr: *listenAddress}
192 if err := https.Listen(server, *configFile); err != nil { 193 if err := https.Listen(server, *configFile, logger); err != nil {
193 level.Error(logger).Log("err", err) 194 level.Error(logger).Log("err", err)
194 os.Exit(1) 195 os.Exit(1)
195 } 196 }
diff --git a/vendor/github.com/mwitkow/go-conntrack/.gitignore b/vendor/github.com/mwitkow/go-conntrack/.gitignore
new file mode 100644
index 0000000..406e493
--- /dev/null
+++ b/vendor/github.com/mwitkow/go-conntrack/.gitignore
@@ -0,0 +1,163 @@
1# Created by .ignore support plugin (hsz.mobi)
2### JetBrains template
3# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
4# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
5
6# User-specific stuff:
7.idea
8.idea/workspace.xml
9.idea/tasks.xml
10.idea/dictionaries
11.idea/vcs.xml
12.idea/jsLibraryMappings.xml
13
14# Sensitive or high-churn files:
15.idea/dataSources.ids
16.idea/dataSources.xml
17.idea/dataSources.local.xml
18.idea/sqlDataSources.xml
19.idea/dynamic.xml
20.idea/uiDesigner.xml
21
22# Gradle:
23.idea/gradle.xml
24.idea/libraries
25
26# Mongo Explorer plugin:
27.idea/mongoSettings.xml
28
29## File-based project format:
30*.iws
31
32## Plugin-specific files:
33
34# IntelliJ
35/out/
36
37# mpeltonen/sbt-idea plugin
38.idea_modules/
39
40# JIRA plugin
41atlassian-ide-plugin.xml
42
43# Crashlytics plugin (for Android Studio and IntelliJ)
44com_crashlytics_export_strings.xml
45crashlytics.properties
46crashlytics-build.properties
47fabric.properties
48### Go template
49# Compiled Object files, Static and Dynamic libs (Shared Objects)
50*.o
51*.a
52*.so
53
54# Folders
55_obj
56_test
57
58# Architecture specific extensions/prefixes
59*.[568vq]
60[568vq].out
61
62*.cgo1.go
63*.cgo2.c
64_cgo_defun.c
65_cgo_gotypes.go
66_cgo_export.*
67
68_testmain.go
69
70*.exe
71*.test
72*.prof
73### Python template
74# Byte-compiled / optimized / DLL files
75__pycache__/
76*.py[cod]
77*$py.class
78
79# C extensions
80*.so
81
82# Distribution / packaging
83.Python
84env/
85build/
86develop-eggs/
87dist/
88downloads/
89eggs/
90.eggs/
91lib/
92lib64/
93parts/
94sdist/
95var/
96*.egg-info/
97.installed.cfg
98*.egg
99
100# PyInstaller
101# Usually these files are written by a python script from a template
102# before PyInstaller builds the exe, so as to inject date/other infos into it.
103*.manifest
104*.spec
105
106# Installer logs
107pip-log.txt
108pip-delete-this-directory.txt
109
110# Unit test / coverage reports
111htmlcov/
112.tox/
113.coverage
114.coverage.*
115.cache
116nosetests.xml
117coverage.xml
118*,cover
119.hypothesis/
120
121# Translations
122*.mo
123*.pot
124
125# Django stuff:
126*.log
127local_settings.py
128
129# Flask stuff:
130instance/
131.webassets-cache
132
133# Scrapy stuff:
134.scrapy
135
136# Sphinx documentation
137docs/_build/
138
139# PyBuilder
140target/
141
142# IPython Notebook
143.ipynb_checkpoints
144
145# pyenv
146.python-version
147
148# celery beat schedule file
149celerybeat-schedule
150
151# dotenv
152.env
153
154# virtualenv
155venv/
156ENV/
157
158# Spyder project settings
159.spyderproject
160
161# Rope project settings
162.ropeproject
163
diff --git a/vendor/github.com/mwitkow/go-conntrack/.travis.yml b/vendor/github.com/mwitkow/go-conntrack/.travis.yml
new file mode 100644
index 0000000..db676f6
--- /dev/null
+++ b/vendor/github.com/mwitkow/go-conntrack/.travis.yml
@@ -0,0 +1,13 @@
1sudo: false
2language: go
3go:
4 - 1.7
5
6install:
7 - go get github.com/stretchr/testify
8 - go get github.com/prometheus/client_golang/prometheus
9 - go get golang.org/x/net/context
10 - go get golang.org/x/net/trace
11
12script:
13 - go test -v ./...
diff --git a/vendor/github.com/mwitkow/go-conntrack/LICENSE b/vendor/github.com/mwitkow/go-conntrack/LICENSE
new file mode 100644
index 0000000..8dada3e
--- /dev/null
+++ b/vendor/github.com/mwitkow/go-conntrack/LICENSE
@@ -0,0 +1,201 @@
1 Apache License
2 Version 2.0, January 2004
3 http://www.apache.org/licenses/
4
5 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
7 1. Definitions.
8
9 "License" shall mean the terms and conditions for use, reproduction,
10 and distribution as defined by Sections 1 through 9 of this document.
11
12 "Licensor" shall mean the copyright owner or entity authorized by
13 the copyright owner that is granting the License.
14
15 "Legal Entity" shall mean the union of the acting entity and all
16 other entities that control, are controlled by, or are under common
17 control with that entity. For the purposes of this definition,
18 "control" means (i) the power, direct or indirect, to cause the
19 direction or management of such entity, whether by contract or
20 otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 outstanding shares, or (iii) beneficial ownership of such entity.
22
23 "You" (or "Your") shall mean an individual or Legal Entity
24 exercising permissions granted by this License.
25
26 "Source" form shall mean the preferred form for making modifications,
27 including but not limited to software source code, documentation
28 source, and configuration files.
29
30 "Object" form shall mean any form resulting from mechanical
31 transformation or translation of a Source form, including but
32 not limited to compiled object code, generated documentation,
33 and conversions to other media types.
34
35 "Work" shall mean the work of authorship, whether in Source or
36 Object form, made available under the License, as indicated by a
37 copyright notice that is included in or attached to the work
38 (an example is provided in the Appendix below).
39
40 "Derivative Works" shall mean any work, whether in Source or Object
41 form, that is based on (or derived from) the Work and for which the
42 editorial revisions, annotations, elaborations, or other modifications
43 represent, as a whole, an original work of authorship. For the purposes
44 of this License, Derivative Works shall not include works that remain
45 separable from, or merely link (or bind by name) to the interfaces of,
46 the Work and Derivative Works thereof.
47
48 "Contribution" shall mean any work of authorship, including
49 the original version of the Work and any modifications or additions
50 to that Work or Derivative Works thereof, that is intentionally
51 submitted to Licensor for inclusion in the Work by the copyright owner
52 or by an individual or Legal Entity authorized to submit on behalf of
53 the copyright owner. For the purposes of this definition, "submitted"
54 means any form of electronic, verbal, or written communication sent
55 to the Licensor or its representatives, including but not limited to
56 communication on electronic mailing lists, source code control systems,
57 and issue tracking systems that are managed by, or on behalf of, the
58 Licensor for the purpose of discussing and improving the Work, but
59 excluding communication that is conspicuously marked or otherwise
60 designated in writing by the copyright owner as "Not a Contribution."
61
62 "Contributor" shall mean Licensor and any individual or Legal Entity
63 on behalf of whom a Contribution has been received by Licensor and
64 subsequently incorporated within the Work.
65
66 2. Grant of Copyright License. Subject to the terms and conditions of
67 this License, each Contributor hereby grants to You a perpetual,
68 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 copyright license to reproduce, prepare Derivative Works of,
70 publicly display, publicly perform, sublicense, and distribute the
71 Work and such Derivative Works in Source or Object form.
72
73 3. Grant of Patent License. Subject to the terms and conditions of
74 this License, each Contributor hereby grants to You a perpetual,
75 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 (except as stated in this section) patent license to make, have made,
77 use, offer to sell, sell, import, and otherwise transfer the Work,
78 where such license applies only to those patent claims licensable
79 by such Contributor that are necessarily infringed by their
80 Contribution(s) alone or by combination of their Contribution(s)
81 with the Work to which such Contribution(s) was submitted. If You
82 institute patent litigation against any entity (including a
83 cross-claim or counterclaim in a lawsuit) alleging that the Work
84 or a Contribution incorporated within the Work constitutes direct
85 or contributory patent infringement, then any patent licenses
86 granted to You under this License for that Work shall terminate
87 as of the date such litigation is filed.
88
89 4. Redistribution. You may reproduce and distribute copies of the
90 Work or Derivative Works thereof in any medium, with or without
91 modifications, and in Source or Object form, provided that You
92 meet the following conditions:
93
94 (a) You must give any other recipients of the Work or
95 Derivative Works a copy of this License; and
96
97 (b) You must cause any modified files to carry prominent notices
98 stating that You changed the files; and
99
100 (c) You must retain, in the Source form of any Derivative Works
101 that You distribute, all copyright, patent, trademark, and
102 attribution notices from the Source form of the Work,
103 excluding those notices that do not pertain to any part of
104 the Derivative Works; and
105
106 (d) If the Work includes a "NOTICE" text file as part of its
107 distribution, then any Derivative Works that You distribute must
108 include a readable copy of the attribution notices contained
109 within such NOTICE file, excluding those notices that do not
110 pertain to any part of the Derivative Works, in at least one
111 of the following places: within a NOTICE text file distributed
112 as part of the Derivative Works; within the Source form or
113 documentation, if provided along with the Derivative Works; or,
114 within a display generated by the Derivative Works, if and
115 wherever such third-party notices normally appear. The contents
116 of the NOTICE file are for informational purposes only and
117 do not modify the License. You may add Your own attribution
118 notices within Derivative Works that You distribute, alongside
119 or as an addendum to the NOTICE text from the Work, provided
120 that such additional attribution notices cannot be construed
121 as modifying the License.
122
123 You may add Your own copyright statement to Your modifications and
124 may provide additional or different license terms and conditions
125 for use, reproduction, or distribution of Your modifications, or
126 for any such Derivative Works as a whole, provided Your use,
127 reproduction, and distribution of the Work otherwise complies with
128 the conditions stated in this License.
129
130 5. Submission of Contributions. Unless You explicitly state otherwise,
131 any Contribution intentionally submitted for inclusion in the Work
132 by You to the Licensor shall be under the terms and conditions of
133 this License, without any additional terms or conditions.
134 Notwithstanding the above, nothing herein shall supersede or modify
135 the terms of any separate license agreement you may have executed
136 with Licensor regarding such Contributions.
137
138 6. Trademarks. This License does not grant permission to use the trade
139 names, trademarks, service marks, or product names of the Licensor,
140 except as required for reasonable and customary use in describing the
141 origin of the Work and reproducing the content of the NOTICE file.
142
143 7. Disclaimer of Warranty. Unless required by applicable law or
144 agreed to in writing, Licensor provides the Work (and each
145 Contributor provides its Contributions) on an "AS IS" BASIS,
146 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 implied, including, without limitation, any warranties or conditions
148 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 PARTICULAR PURPOSE. You are solely responsible for determining the
150 appropriateness of using or redistributing the Work and assume any
151 risks associated with Your exercise of permissions under this License.
152
153 8. Limitation of Liability. In no event and under no legal theory,
154 whether in tort (including negligence), contract, or otherwise,
155 unless required by applicable law (such as deliberate and grossly
156 negligent acts) or agreed to in writing, shall any Contributor be
157 liable to You for damages, including any direct, indirect, special,
158 incidental, or consequential damages of any character arising as a
159 result of this License or out of the use or inability to use the
160 Work (including but not limited to damages for loss of goodwill,
161 work stoppage, computer failure or malfunction, or any and all
162 other commercial damages or losses), even if such Contributor
163 has been advised of the possibility of such damages.
164
165 9. Accepting Warranty or Additional Liability. While redistributing
166 the Work or Derivative Works thereof, You may choose to offer,
167 and charge a fee for, acceptance of support, warranty, indemnity,
168 or other liability obligations and/or rights consistent with this
169 License. However, in accepting such obligations, You may act only
170 on Your own behalf and on Your sole responsibility, not on behalf
171 of any other Contributor, and only if You agree to indemnify,
172 defend, and hold each Contributor harmless for any liability
173 incurred by, or claims asserted against, such Contributor by reason
174 of your accepting any such warranty or additional liability.
175
176 END OF TERMS AND CONDITIONS
177
178 APPENDIX: How to apply the Apache License to your work.
179
180 To apply the Apache License to your work, attach the following
181 boilerplate notice, with the fields enclosed by brackets "{}"
182 replaced with your own identifying information. (Don't include
183 the brackets!) The text should be enclosed in the appropriate
184 comment syntax for the file format. We also recommend that a
185 file or class name and description of purpose be included on the
186 same "printed page" as the copyright notice for easier
187 identification within third-party archives.
188
189 Copyright {yyyy} {name of copyright owner}
190
191 Licensed under the Apache License, Version 2.0 (the "License");
192 you may not use this file except in compliance with the License.
193 You may obtain a copy of the License at
194
195 http://www.apache.org/licenses/LICENSE-2.0
196
197 Unless required by applicable law or agreed to in writing, software
198 distributed under the License is distributed on an "AS IS" BASIS,
199 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 See the License for the specific language governing permissions and
201 limitations under the License.
diff --git a/vendor/github.com/mwitkow/go-conntrack/README.md b/vendor/github.com/mwitkow/go-conntrack/README.md
new file mode 100644
index 0000000..5ae7702
--- /dev/null
+++ b/vendor/github.com/mwitkow/go-conntrack/README.md
@@ -0,0 +1,88 @@
1# Go tracing and monitoring (Prometheus) for `net.Conn`
2
3[![Travis Build](https://travis-ci.org/mwitkow/go-conntrack.svg)](https://travis-ci.org/mwitkow/go-conntrack)
4[![Go Report Card](https://goreportcard.com/badge/github.com/mwitkow/go-conntrack)](http://goreportcard.com/report/mwitkow/go-conntrack)
5[![GoDoc](http://img.shields.io/badge/GoDoc-Reference-blue.svg)](https://godoc.org/github.com/mwitkow/go-conntrack)
6[![Apache 2.0 License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
7
8[Prometheus](https://prometheus.io/) monitoring and [`x/net/trace`](https://godoc.org/golang.org/x/net/trace#EventLog) tracing wrappers `net.Conn`, both inbound (`net.Listener`) and outbound (`net.Dialer`).
9
10## Why?
11
12Go standard library does a great job of doing "the right" things with your connections: `http.Transport` pools outbound ones, and `http.Server` sets good *Keep Alive* defaults.
13However, it is still easy to get it wrong, see the excellent [*The complete guide to Go net/http timeouts*](https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/).
14
15That's why you should be able to monitor (using Prometheus) how many connections your Go frontend servers have inbound, and how big are the connection pools to your backends. You should also be able to inspect your connection without `ssh` and `netstat`.
16
17![Events page with connections](https://raw.githubusercontent.com/mwitkow/go-conntrack/images/events.png)
18
19## How to use?
20
21All of these examples can be found in [`example/server.go`](example/server.go):
22
23### Conntrack Dialer for HTTP DefaultClient
24
25Most often people use the default `http.DefaultClient` that uses `http.DefaultTransport`. The easiest way to make sure all your outbound connections monitored and trace is:
26
27```go
28http.DefaultTransport.(*http.Transport).DialContext = conntrack.NewDialContextFunc(
29 conntrack.DialWithTracing(),
30 conntrack.DialWithDialer(&net.Dialer{
31 Timeout: 30 * time.Second,
32 KeepAlive: 30 * time.Second,
33 }),
34)
35```
36
37#### Dialer Name
38
39Tracked outbound connections are organised by *dialer name* (with `default` being default). The *dialer name* is used for monitoring (`dialer_name` label) and tracing (`net.ClientConn.<dialer_name>` family).
40
41You can pass `conntrack.WithDialerName()` to `NewDialContextFunc` to set the name for the dialer. Moreover, you can set the *dialer name* per invocation of the dialer, by passing it in the `Context`. For example using the [`ctxhttp`](https://godoc.org/golang.org/x/net/context/ctxhttp) lib:
42
43```go
44callCtx := conntrack.DialNameToContext(parentCtx, "google")
45ctxhttp.Get(callCtx, http.DefaultClient, "https://www.google.com")
46```
47
48### Conntrack Listener for HTTP Server
49
50Tracked inbound connections are organised by *listener name* (with `default` being default). The *listener name* is used for monitoring (`listener_name` label) and tracing (`net.ServerConn.<listener_name>` family). For example, a simple `http.Server` can be instrumented like this:
51
52```go
53listener, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
54listener = conntrack.NewListener(listener,
55 conntrack.TrackWithName("http"),
56 conntrack.TrackWithTracing(),
57 conntrack.TrackWithTcpKeepAlive(5 * time.Minutes))
58httpServer.Serve(listener)
59```
60
61Note, the `TrackWithTcpKeepAlive`. The default `http.ListenAndServe` adds a tcp keep alive wrapper to inbound TCP connections. `conntrack.NewListener` allows you to do that without another layer of wrapping.
62
63#### TLS server example
64
65The standard lobrary `http.ListenAndServerTLS` does a lot to bootstrap TLS connections, including supporting HTTP2 negotiation. Unfortunately, that is hard to do if you want to provide your own `net.Listener`. That's why this repo comes with `connhelpers` package, which takes care of configuring `tls.Config` for that use case. Here's an example of use:
66
67```go
68listener, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
69listener = conntrack.NewListener(listener,
70 conntrack.TrackWithName("https"),
71 conntrack.TrackWithTracing(),
72 conntrack.TrackWithTcpKeepAlive(5 * time.Minutes))
73tlsConfig, err := connhelpers.TlsConfigForServerCerts(*tlsCertFilePath, *tlsKeyFilePath)
74tlsConfig, err = connhelpers.TlsConfigWithHttp2Enabled(tlsConfig)
75tlsListener := tls.NewListener(listener, tlsConfig)
76httpServer.Serve(listener)
77```
78
79# Status
80
81This code is used by Improbable's HTTP frontending and proxying stack for debuging and monitoring of established user connections.
82
83Additional tooling will be added if needed, and contributions are welcome.
84
85#License
86
87`go-conntrack` is released under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details.
88
diff --git a/vendor/github.com/mwitkow/go-conntrack/dialer_reporter.go b/vendor/github.com/mwitkow/go-conntrack/dialer_reporter.go
new file mode 100644
index 0000000..0e39886
--- /dev/null
+++ b/vendor/github.com/mwitkow/go-conntrack/dialer_reporter.go
@@ -0,0 +1,108 @@
1// Copyright 2016 Michal Witkowski. All Rights Reserved.
2// See LICENSE for licensing terms.
3
4package conntrack
5
6import (
7 "context"
8 "net"
9 "os"
10 "syscall"
11
12 prom "github.com/prometheus/client_golang/prometheus"
13)
14
15type failureReason string
16
17const (
18 failedResolution = "resolution"
19 failedConnRefused = "refused"
20 failedTimeout = "timeout"
21 failedUnknown = "unknown"
22)
23
24var (
25 dialerAttemptedTotal = prom.NewCounterVec(
26 prom.CounterOpts{
27 Namespace: "net",
28 Subsystem: "conntrack",
29 Name: "dialer_conn_attempted_total",
30 Help: "Total number of connections attempted by the given dialer a given name.",
31 }, []string{"dialer_name"})
32
33 dialerConnEstablishedTotal = prom.NewCounterVec(
34 prom.CounterOpts{
35 Namespace: "net",
36 Subsystem: "conntrack",
37 Name: "dialer_conn_established_total",
38 Help: "Total number of connections successfully established by the given dialer a given name.",
39 }, []string{"dialer_name"})
40
41 dialerConnFailedTotal = prom.NewCounterVec(
42 prom.CounterOpts{
43 Namespace: "net",
44 Subsystem: "conntrack",
45 Name: "dialer_conn_failed_total",
46 Help: "Total number of connections failed to dial by the dialer a given name.",
47 }, []string{"dialer_name", "reason"})
48
49 dialerConnClosedTotal = prom.NewCounterVec(
50 prom.CounterOpts{
51 Namespace: "net",
52 Subsystem: "conntrack",
53 Name: "dialer_conn_closed_total",
54 Help: "Total number of connections closed which originated from the dialer of a given name.",
55 }, []string{"dialer_name"})
56)
57
58func init() {
59 prom.MustRegister(dialerAttemptedTotal)
60 prom.MustRegister(dialerConnEstablishedTotal)
61 prom.MustRegister(dialerConnFailedTotal)
62 prom.MustRegister(dialerConnClosedTotal)
63}
64
65// preRegisterDialerMetrics pre-populates Prometheus labels for the given dialer name, to avoid Prometheus missing labels issue.
66func PreRegisterDialerMetrics(dialerName string) {
67 dialerAttemptedTotal.WithLabelValues(dialerName)
68 dialerConnEstablishedTotal.WithLabelValues(dialerName)
69 for _, reason := range []failureReason{failedTimeout, failedResolution, failedConnRefused, failedUnknown} {
70 dialerConnFailedTotal.WithLabelValues(dialerName, string(reason))
71 }
72 dialerConnClosedTotal.WithLabelValues(dialerName)
73}
74
75func reportDialerConnAttempt(dialerName string) {
76 dialerAttemptedTotal.WithLabelValues(dialerName).Inc()
77}
78
79func reportDialerConnEstablished(dialerName string) {
80 dialerConnEstablishedTotal.WithLabelValues(dialerName).Inc()
81}
82
83func reportDialerConnClosed(dialerName string) {
84 dialerConnClosedTotal.WithLabelValues(dialerName).Inc()
85}
86
87func reportDialerConnFailed(dialerName string, err error) {
88 if netErr, ok := err.(*net.OpError); ok {
89 switch nestErr := netErr.Err.(type) {
90 case *net.DNSError:
91 dialerConnFailedTotal.WithLabelValues(dialerName, string(failedResolution)).Inc()
92 return
93 case *os.SyscallError:
94 if nestErr.Err == syscall.ECONNREFUSED {
95 dialerConnFailedTotal.WithLabelValues(dialerName, string(failedConnRefused)).Inc()
96 }
97 dialerConnFailedTotal.WithLabelValues(dialerName, string(failedUnknown)).Inc()
98 return
99 }
100 if netErr.Timeout() {
101 dialerConnFailedTotal.WithLabelValues(dialerName, string(failedTimeout)).Inc()
102 }
103 } else if err == context.Canceled || err == context.DeadlineExceeded {
104 dialerConnFailedTotal.WithLabelValues(dialerName, string(failedTimeout)).Inc()
105 return
106 }
107 dialerConnFailedTotal.WithLabelValues(dialerName, string(failedUnknown)).Inc()
108}
diff --git a/vendor/github.com/mwitkow/go-conntrack/dialer_wrapper.go b/vendor/github.com/mwitkow/go-conntrack/dialer_wrapper.go
new file mode 100644
index 0000000..cebaf96
--- /dev/null
+++ b/vendor/github.com/mwitkow/go-conntrack/dialer_wrapper.go
@@ -0,0 +1,166 @@
1// Copyright 2016 Michal Witkowski. All Rights Reserved.
2// See LICENSE for licensing terms.
3
4package conntrack
5
6import (
7 "context"
8 "fmt"
9 "net"
10 "sync"
11
12 "golang.org/x/net/trace"
13)
14
15var (
16 dialerNameKey = "conntrackDialerKey"
17)
18
19type dialerOpts struct {
20 name string
21 monitoring bool
22 tracing bool
23 parentDialContextFunc dialerContextFunc
24}
25
26type dialerOpt func(*dialerOpts)
27
28type dialerContextFunc func(context.Context, string, string) (net.Conn, error)
29
30// DialWithName sets the name of the dialer for tracking and monitoring.
31// This is the name for the dialer (default is `default`), but for `NewDialContextFunc` can be overwritten from the
32// Context using `DialNameToContext`.
33func DialWithName(name string) dialerOpt {
34 return func(opts *dialerOpts) {
35 opts.name = name
36 }
37}
38
39// DialWithoutMonitoring turns *off* Prometheus monitoring for this dialer.
40func DialWithoutMonitoring() dialerOpt {
41 return func(opts *dialerOpts) {
42 opts.monitoring = false
43 }
44}
45
46// DialWithTracing turns *on* the /debug/events tracing of the dial calls.
47func DialWithTracing() dialerOpt {
48 return func(opts *dialerOpts) {
49 opts.tracing = true
50 }
51}
52
53// DialWithDialer allows you to override the `net.Dialer` instance used to actually conduct the dials.
54func DialWithDialer(parentDialer *net.Dialer) dialerOpt {
55 return DialWithDialContextFunc(parentDialer.DialContext)
56}
57
58// DialWithDialContextFunc allows you to override func gets used for the actual dialing. The default is `net.Dialer.DialContext`.
59func DialWithDialContextFunc(parentDialerFunc dialerContextFunc) dialerOpt {
60 return func(opts *dialerOpts) {
61 opts.parentDialContextFunc = parentDialerFunc
62 }
63}
64
65// DialNameFromContext returns the name of the dialer from the context of the DialContext func, if any.
66func DialNameFromContext(ctx context.Context) string {
67 val, ok := ctx.Value(dialerNameKey).(string)
68 if !ok {
69 return ""
70 }
71 return val
72}
73
74// DialNameToContext returns a context that will contain a dialer name override.
75func DialNameToContext(ctx context.Context, dialerName string) context.Context {
76 return context.WithValue(ctx, dialerNameKey, dialerName)
77}
78
79// NewDialContextFunc returns a `DialContext` function that tracks outbound connections.
80// The signature is compatible with `http.Tranport.DialContext` and is meant to be used there.
81func NewDialContextFunc(optFuncs ...dialerOpt) func(context.Context, string, string) (net.Conn, error) {
82 opts := &dialerOpts{name: defaultName, monitoring: true, parentDialContextFunc: (&net.Dialer{}).DialContext}
83 for _, f := range optFuncs {
84 f(opts)
85 }
86 if opts.monitoring {
87 PreRegisterDialerMetrics(opts.name)
88 }
89 return func(ctx context.Context, network string, addr string) (net.Conn, error) {
90 name := opts.name
91 if ctxName := DialNameFromContext(ctx); ctxName != "" {
92 name = ctxName
93 }
94 return dialClientConnTracker(ctx, network, addr, name, opts)
95 }
96}
97
98// NewDialFunc returns a `Dial` function that tracks outbound connections.
99// The signature is compatible with `http.Tranport.Dial` and is meant to be used there for Go < 1.7.
100func NewDialFunc(optFuncs ...dialerOpt) func(string, string) (net.Conn, error) {
101 dialContextFunc := NewDialContextFunc(optFuncs...)
102 return func(network string, addr string) (net.Conn, error) {
103 return dialContextFunc(context.TODO(), network, addr)
104 }
105}
106
107type clientConnTracker struct {
108 net.Conn
109 opts *dialerOpts
110 dialerName string
111 event trace.EventLog
112 mu sync.Mutex
113}
114
115func dialClientConnTracker(ctx context.Context, network string, addr string, dialerName string, opts *dialerOpts) (net.Conn, error) {
116 var event trace.EventLog
117 if opts.tracing {
118 event = trace.NewEventLog(fmt.Sprintf("net.ClientConn.%s", dialerName), fmt.Sprintf("%v", addr))
119 }
120 if opts.monitoring {
121 reportDialerConnAttempt(dialerName)
122 }
123 conn, err := opts.parentDialContextFunc(ctx, network, addr)
124 if err != nil {
125 if event != nil {
126 event.Errorf("failed dialing: %v", err)
127 event.Finish()
128 }
129 if opts.monitoring {
130 reportDialerConnFailed(dialerName, err)
131 }
132 return nil, err
133 }
134 if event != nil {
135 event.Printf("established: %s -> %s", conn.LocalAddr(), conn.RemoteAddr())
136 }
137 if opts.monitoring {
138 reportDialerConnEstablished(dialerName)
139 }
140 tracker := &clientConnTracker{
141 Conn: conn,
142 opts: opts,
143 dialerName: dialerName,
144 event: event,
145 }
146 return tracker, nil
147}
148
149func (ct *clientConnTracker) Close() error {
150 err := ct.Conn.Close()
151 ct.mu.Lock()
152 if ct.event != nil {
153 if err != nil {
154 ct.event.Errorf("failed closing: %v", err)
155 } else {
156 ct.event.Printf("closing")
157 }
158 ct.event.Finish()
159 ct.event = nil
160 }
161 ct.mu.Unlock()
162 if ct.opts.monitoring {
163 reportDialerConnClosed(ct.dialerName)
164 }
165 return err
166}
diff --git a/vendor/github.com/mwitkow/go-conntrack/listener_reporter.go b/vendor/github.com/mwitkow/go-conntrack/listener_reporter.go
new file mode 100644
index 0000000..21a8f55
--- /dev/null
+++ b/vendor/github.com/mwitkow/go-conntrack/listener_reporter.go
@@ -0,0 +1,43 @@
1// Copyright 2016 Michal Witkowski. All Rights Reserved.
2// See LICENSE for licensing terms.
3
4package conntrack
5
6import prom "github.com/prometheus/client_golang/prometheus"
7
8var (
9 listenerAcceptedTotal = prom.NewCounterVec(
10 prom.CounterOpts{
11 Namespace: "net",
12 Subsystem: "conntrack",
13 Name: "listener_conn_accepted_total",
14 Help: "Total number of connections opened to the listener of a given name.",
15 }, []string{"listener_name"})
16
17 listenerClosedTotal = prom.NewCounterVec(
18 prom.CounterOpts{
19 Namespace: "net",
20 Subsystem: "conntrack",
21 Name: "listener_conn_closed_total",
22 Help: "Total number of connections closed that were made to the listener of a given name.",
23 }, []string{"listener_name"})
24)
25
26func init() {
27 prom.MustRegister(listenerAcceptedTotal)
28 prom.MustRegister(listenerClosedTotal)
29}
30
31// preRegisterListener pre-populates Prometheus labels for the given listener name, to avoid Prometheus missing labels issue.
32func preRegisterListenerMetrics(listenerName string) {
33 listenerAcceptedTotal.WithLabelValues(listenerName)
34 listenerClosedTotal.WithLabelValues(listenerName)
35}
36
37func reportListenerConnAccepted(listenerName string) {
38 listenerAcceptedTotal.WithLabelValues(listenerName).Inc()
39}
40
41func reportListenerConnClosed(listenerName string) {
42 listenerClosedTotal.WithLabelValues(listenerName).Inc()
43}
diff --git a/vendor/github.com/mwitkow/go-conntrack/listener_wrapper.go b/vendor/github.com/mwitkow/go-conntrack/listener_wrapper.go
new file mode 100644
index 0000000..6754c00
--- /dev/null
+++ b/vendor/github.com/mwitkow/go-conntrack/listener_wrapper.go
@@ -0,0 +1,137 @@
1// Copyright 2016 Michal Witkowski. All Rights Reserved.
2// See LICENSE for licensing terms.
3
4package conntrack
5
6import (
7 "fmt"
8 "net"
9
10 "sync"
11
12 "golang.org/x/net/trace"
13 "time"
14)
15
16const (
17 defaultName = "default"
18)
19
20type listenerOpts struct {
21 name string
22 monitoring bool
23 tracing bool
24 tcpKeepAlive time.Duration
25}
26
27type listenerOpt func(*listenerOpts)
28
29// TrackWithName sets the name of the Listener for use in tracking and monitoring.
30func TrackWithName(name string) listenerOpt {
31 return func(opts *listenerOpts) {
32 opts.name = name
33 }
34}
35
36// TrackWithoutMonitoring turns *off* Prometheus monitoring for this listener.
37func TrackWithoutMonitoring() listenerOpt {
38 return func(opts *listenerOpts) {
39 opts.monitoring = false
40 }
41}
42
43// TrackWithTracing turns *on* the /debug/events tracing of the live listener connections.
44func TrackWithTracing() listenerOpt {
45 return func(opts *listenerOpts) {
46 opts.tracing = true
47 }
48}
49
50// TrackWithTcpKeepAlive makes sure that any `net.TCPConn` that get accepted have a keep-alive.
51// This is useful for HTTP servers in order for, for example laptops, to not use up resources on the
52// server while they don't utilise their connection.
53// A value of 0 disables it.
54func TrackWithTcpKeepAlive(keepalive time.Duration) listenerOpt {
55 return func(opts *listenerOpts) {
56 opts.tcpKeepAlive = keepalive
57 }
58}
59
60type connTrackListener struct {
61 net.Listener
62 opts *listenerOpts
63}
64
65// NewListener returns the given listener wrapped in connection tracking listener.
66func NewListener(inner net.Listener, optFuncs ...listenerOpt) net.Listener {
67 opts := &listenerOpts{
68 name: defaultName,
69 monitoring: true,
70 tracing: false,
71 }
72 for _, f := range optFuncs {
73 f(opts)
74 }
75 if opts.monitoring {
76 preRegisterListenerMetrics(opts.name)
77 }
78 return &connTrackListener{
79 Listener: inner,
80 opts: opts,
81 }
82}
83
84func (ct *connTrackListener) Accept() (net.Conn, error) {
85 // TODO(mwitkow): Add monitoring of failed accept.
86 conn, err := ct.Listener.Accept()
87 if err != nil {
88 return nil, err
89 }
90 if tcpConn, ok := conn.(*net.TCPConn); ok && ct.opts.tcpKeepAlive > 0 {
91 tcpConn.SetKeepAlive(true)
92 tcpConn.SetKeepAlivePeriod(ct.opts.tcpKeepAlive)
93 }
94 return newServerConnTracker(conn, ct.opts), nil
95}
96
97type serverConnTracker struct {
98 net.Conn
99 opts *listenerOpts
100 event trace.EventLog
101 mu sync.Mutex
102}
103
104func newServerConnTracker(inner net.Conn, opts *listenerOpts) net.Conn {
105
106 tracker := &serverConnTracker{
107 Conn: inner,
108 opts: opts,
109 }
110 if opts.tracing {
111 tracker.event = trace.NewEventLog(fmt.Sprintf("net.ServerConn.%s", opts.name), fmt.Sprintf("%v", inner.RemoteAddr()))
112 tracker.event.Printf("accepted: %v -> %v", inner.RemoteAddr(), inner.LocalAddr())
113 }
114 if opts.monitoring {
115 reportListenerConnAccepted(opts.name)
116 }
117 return tracker
118}
119
120func (ct *serverConnTracker) Close() error {
121 err := ct.Conn.Close()
122 ct.mu.Lock()
123 if ct.event != nil {
124 if err != nil {
125 ct.event.Errorf("failed closing: %v", err)
126 } else {
127 ct.event.Printf("closing")
128 }
129 ct.event.Finish()
130 ct.event = nil
131 }
132 ct.mu.Unlock()
133 if ct.opts.monitoring {
134 reportListenerConnClosed(ct.opts.name)
135 }
136 return err
137}
diff --git a/vendor/github.com/prometheus/common/config/config.go b/vendor/github.com/prometheus/common/config/config.go
new file mode 100644
index 0000000..30719d8
--- /dev/null
+++ b/vendor/github.com/prometheus/common/config/config.go
@@ -0,0 +1,34 @@
1// Copyright 2016 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14// This package no longer handles safe yaml parsing. In order to
15// ensure correct yaml unmarshalling, use "yaml.UnmarshalStrict()".
16
17package config
18
19// Secret special type for storing secrets.
20type Secret string
21
22// MarshalYAML implements the yaml.Marshaler interface for Secrets.
23func (s Secret) MarshalYAML() (interface{}, error) {
24 if s != "" {
25 return "<secret>", nil
26 }
27 return nil, nil
28}
29
30//UnmarshalYAML implements the yaml.Unmarshaler interface for Secrets.
31func (s *Secret) UnmarshalYAML(unmarshal func(interface{}) error) error {
32 type plain Secret
33 return unmarshal((*plain)(s))
34}
diff --git a/vendor/github.com/prometheus/common/config/http_config.go b/vendor/github.com/prometheus/common/config/http_config.go
new file mode 100644
index 0000000..5c373d7
--- /dev/null
+++ b/vendor/github.com/prometheus/common/config/http_config.go
@@ -0,0 +1,472 @@
1// Copyright 2016 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14// +build go1.8
15
16package config
17
18import (
19 "bytes"
20 "crypto/md5"
21 "crypto/tls"
22 "crypto/x509"
23 "fmt"
24 "io/ioutil"
25 "net/http"
26 "net/url"
27 "strings"
28 "sync"
29 "time"
30
31 "github.com/mwitkow/go-conntrack"
32 "gopkg.in/yaml.v2"
33)
34
35type closeIdler interface {
36 CloseIdleConnections()
37}
38
39// BasicAuth contains basic HTTP authentication credentials.
40type BasicAuth struct {
41 Username string `yaml:"username"`
42 Password Secret `yaml:"password,omitempty"`
43 PasswordFile string `yaml:"password_file,omitempty"`
44}
45
46// URL is a custom URL type that allows validation at configuration load time.
47type URL struct {
48 *url.URL
49}
50
51// UnmarshalYAML implements the yaml.Unmarshaler interface for URLs.
52func (u *URL) UnmarshalYAML(unmarshal func(interface{}) error) error {
53 var s string
54 if err := unmarshal(&s); err != nil {
55 return err
56 }
57
58 urlp, err := url.Parse(s)
59 if err != nil {
60 return err
61 }
62 u.URL = urlp
63 return nil
64}
65
66// MarshalYAML implements the yaml.Marshaler interface for URLs.
67func (u URL) MarshalYAML() (interface{}, error) {
68 if u.URL != nil {
69 return u.String(), nil
70 }
71 return nil, nil
72}
73
74// HTTPClientConfig configures an HTTP client.
75type HTTPClientConfig struct {
76 // The HTTP basic authentication credentials for the targets.
77 BasicAuth *BasicAuth `yaml:"basic_auth,omitempty"`
78 // The bearer token for the targets.
79 BearerToken Secret `yaml:"bearer_token,omitempty"`
80 // The bearer token file for the targets.
81 BearerTokenFile string `yaml:"bearer_token_file,omitempty"`
82 // HTTP proxy server to use to connect to the targets.
83 ProxyURL URL `yaml:"proxy_url,omitempty"`
84 // TLSConfig to use to connect to the targets.
85 TLSConfig TLSConfig `yaml:"tls_config,omitempty"`
86}
87
88// Validate validates the HTTPClientConfig to check only one of BearerToken,
89// BasicAuth and BearerTokenFile is configured.
90func (c *HTTPClientConfig) Validate() error {
91 if len(c.BearerToken) > 0 && len(c.BearerTokenFile) > 0 {
92 return fmt.Errorf("at most one of bearer_token & bearer_token_file must be configured")
93 }
94 if c.BasicAuth != nil && (len(c.BearerToken) > 0 || len(c.BearerTokenFile) > 0) {
95 return fmt.Errorf("at most one of basic_auth, bearer_token & bearer_token_file must be configured")
96 }
97 if c.BasicAuth != nil && (string(c.BasicAuth.Password) != "" && c.BasicAuth.PasswordFile != "") {
98 return fmt.Errorf("at most one of basic_auth password & password_file must be configured")
99 }
100 return nil
101}
102
103// UnmarshalYAML implements the yaml.Unmarshaler interface
104func (c *HTTPClientConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
105 type plain HTTPClientConfig
106 if err := unmarshal((*plain)(c)); err != nil {
107 return err
108 }
109 return c.Validate()
110}
111
112// UnmarshalYAML implements the yaml.Unmarshaler interface.
113func (a *BasicAuth) UnmarshalYAML(unmarshal func(interface{}) error) error {
114 type plain BasicAuth
115 return unmarshal((*plain)(a))
116}
117
118// NewClient returns a http.Client using the specified http.RoundTripper.
119func newClient(rt http.RoundTripper) *http.Client {
120 return &http.Client{Transport: rt}
121}
122
123// NewClientFromConfig returns a new HTTP client configured for the
124// given config.HTTPClientConfig. The name is used as go-conntrack metric label.
125func NewClientFromConfig(cfg HTTPClientConfig, name string, disableKeepAlives bool) (*http.Client, error) {
126 rt, err := NewRoundTripperFromConfig(cfg, name, disableKeepAlives)
127 if err != nil {
128 return nil, err
129 }
130 return newClient(rt), nil
131}
132
133// NewRoundTripperFromConfig returns a new HTTP RoundTripper configured for the
134// given config.HTTPClientConfig. The name is used as go-conntrack metric label.
135func NewRoundTripperFromConfig(cfg HTTPClientConfig, name string, disableKeepAlives bool) (http.RoundTripper, error) {
136 newRT := func(tlsConfig *tls.Config) (http.RoundTripper, error) {
137 // The only timeout we care about is the configured scrape timeout.
138 // It is applied on request. So we leave out any timings here.
139 var rt http.RoundTripper = &http.Transport{
140 Proxy: http.ProxyURL(cfg.ProxyURL.URL),
141 MaxIdleConns: 20000,
142 MaxIdleConnsPerHost: 1000, // see https://github.com/golang/go/issues/13801
143 DisableKeepAlives: disableKeepAlives,
144 TLSClientConfig: tlsConfig,
145 DisableCompression: true,
146 // 5 minutes is typically above the maximum sane scrape interval. So we can
147 // use keepalive for all configurations.
148 IdleConnTimeout: 5 * time.Minute,
149 TLSHandshakeTimeout: 10 * time.Second,
150 ExpectContinueTimeout: 1 * time.Second,
151 DialContext: conntrack.NewDialContextFunc(
152 conntrack.DialWithTracing(),
153 conntrack.DialWithName(name),
154 ),
155 }
156
157 // If a bearer token is provided, create a round tripper that will set the
158 // Authorization header correctly on each request.
159 if len(cfg.BearerToken) > 0 {
160 rt = NewBearerAuthRoundTripper(cfg.BearerToken, rt)
161 } else if len(cfg.BearerTokenFile) > 0 {
162 rt = NewBearerAuthFileRoundTripper(cfg.BearerTokenFile, rt)
163 }
164
165 if cfg.BasicAuth != nil {
166 rt = NewBasicAuthRoundTripper(cfg.BasicAuth.Username, cfg.BasicAuth.Password, cfg.BasicAuth.PasswordFile, rt)
167 }
168 // Return a new configured RoundTripper.
169 return rt, nil
170 }
171
172 tlsConfig, err := NewTLSConfig(&cfg.TLSConfig)
173 if err != nil {
174 return nil, err
175 }
176
177 if len(cfg.TLSConfig.CAFile) == 0 {
178 // No need for a RoundTripper that reloads the CA file automatically.
179 return newRT(tlsConfig)
180 }
181
182 return newTLSRoundTripper(tlsConfig, cfg.TLSConfig.CAFile, newRT)
183}
184
185type bearerAuthRoundTripper struct {
186 bearerToken Secret
187 rt http.RoundTripper
188}
189
190// NewBearerAuthRoundTripper adds the provided bearer token to a request unless the authorization
191// header has already been set.
192func NewBearerAuthRoundTripper(token Secret, rt http.RoundTripper) http.RoundTripper {
193 return &bearerAuthRoundTripper{token, rt}
194}
195
196func (rt *bearerAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
197 if len(req.Header.Get("Authorization")) == 0 {
198 req = cloneRequest(req)
199 req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", string(rt.bearerToken)))
200 }
201 return rt.rt.RoundTrip(req)
202}
203
204func (rt *bearerAuthRoundTripper) CloseIdleConnections() {
205 if ci, ok := rt.rt.(closeIdler); ok {
206 ci.CloseIdleConnections()
207 }
208}
209
210type bearerAuthFileRoundTripper struct {
211 bearerFile string
212 rt http.RoundTripper
213}
214
215// NewBearerAuthFileRoundTripper adds the bearer token read from the provided file to a request unless
216// the authorization header has already been set. This file is read for every request.
217func NewBearerAuthFileRoundTripper(bearerFile string, rt http.RoundTripper) http.RoundTripper {
218 return &bearerAuthFileRoundTripper{bearerFile, rt}
219}
220
221func (rt *bearerAuthFileRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
222 if len(req.Header.Get("Authorization")) == 0 {
223 b, err := ioutil.ReadFile(rt.bearerFile)
224 if err != nil {
225 return nil, fmt.Errorf("unable to read bearer token file %s: %s", rt.bearerFile, err)
226 }
227 bearerToken := strings.TrimSpace(string(b))
228
229 req = cloneRequest(req)
230 req.Header.Set("Authorization", "Bearer "+bearerToken)
231 }
232
233 return rt.rt.RoundTrip(req)
234}
235
236func (rt *bearerAuthFileRoundTripper) CloseIdleConnections() {
237 if ci, ok := rt.rt.(closeIdler); ok {
238 ci.CloseIdleConnections()
239 }
240}
241
242type basicAuthRoundTripper struct {
243 username string
244 password Secret
245 passwordFile string
246 rt http.RoundTripper
247}
248
249// NewBasicAuthRoundTripper will apply a BASIC auth authorization header to a request unless it has
250// already been set.
251func NewBasicAuthRoundTripper(username string, password Secret, passwordFile string, rt http.RoundTripper) http.RoundTripper {
252 return &basicAuthRoundTripper{username, password, passwordFile, rt}
253}
254
255func (rt *basicAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
256 if len(req.Header.Get("Authorization")) != 0 {
257 return rt.rt.RoundTrip(req)
258 }
259 req = cloneRequest(req)
260 if rt.passwordFile != "" {
261 bs, err := ioutil.ReadFile(rt.passwordFile)
262 if err != nil {
263 return nil, fmt.Errorf("unable to read basic auth password file %s: %s", rt.passwordFile, err)
264 }
265 req.SetBasicAuth(rt.username, strings.TrimSpace(string(bs)))
266 } else {
267 req.SetBasicAuth(rt.username, strings.TrimSpace(string(rt.password)))
268 }
269 return rt.rt.RoundTrip(req)
270}
271
272func (rt *basicAuthRoundTripper) CloseIdleConnections() {
273 if ci, ok := rt.rt.(closeIdler); ok {
274 ci.CloseIdleConnections()
275 }
276}
277
278// cloneRequest returns a clone of the provided *http.Request.
279// The clone is a shallow copy of the struct and its Header map.
280func cloneRequest(r *http.Request) *http.Request {
281 // Shallow copy of the struct.
282 r2 := new(http.Request)
283 *r2 = *r
284 // Deep copy of the Header.
285 r2.Header = make(http.Header)
286 for k, s := range r.Header {
287 r2.Header[k] = s
288 }
289 return r2
290}
291
292// NewTLSConfig creates a new tls.Config from the given TLSConfig.
293func NewTLSConfig(cfg *TLSConfig) (*tls.Config, error) {
294 tlsConfig := &tls.Config{InsecureSkipVerify: cfg.InsecureSkipVerify}
295
296 // If a CA cert is provided then let's read it in so we can validate the
297 // scrape target's certificate properly.
298 if len(cfg.CAFile) > 0 {
299 b, err := readCAFile(cfg.CAFile)
300 if err != nil {
301 return nil, err
302 }
303 if !updateRootCA(tlsConfig, b) {
304 return nil, fmt.Errorf("unable to use specified CA cert %s", cfg.CAFile)
305 }
306 }
307
308 if len(cfg.ServerName) > 0 {
309 tlsConfig.ServerName = cfg.ServerName
310 }
311 // If a client cert & key is provided then configure TLS config accordingly.
312 if len(cfg.CertFile) > 0 && len(cfg.KeyFile) == 0 {
313 return nil, fmt.Errorf("client cert file %q specified without client key file", cfg.CertFile)
314 } else if len(cfg.KeyFile) > 0 && len(cfg.CertFile) == 0 {
315 return nil, fmt.Errorf("client key file %q specified without client cert file", cfg.KeyFile)
316 } else if len(cfg.CertFile) > 0 && len(cfg.KeyFile) > 0 {
317 // Verify that client cert and key are valid.
318 if _, err := cfg.getClientCertificate(nil); err != nil {
319 return nil, err
320 }
321 tlsConfig.GetClientCertificate = cfg.getClientCertificate
322 }
323
324 return tlsConfig, nil
325}
326
327// TLSConfig configures the options for TLS connections.
328type TLSConfig struct {
329 // The CA cert to use for the targets.
330 CAFile string `yaml:"ca_file,omitempty"`
331 // The client cert file for the targets.
332 CertFile string `yaml:"cert_file,omitempty"`
333 // The client key file for the targets.
334 KeyFile string `yaml:"key_file,omitempty"`
335 // Used to verify the hostname for the targets.
336 ServerName string `yaml:"server_name,omitempty"`
337 // Disable target certificate validation.
338 InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
339}
340
341// UnmarshalYAML implements the yaml.Unmarshaler interface.
342func (c *TLSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
343 type plain TLSConfig
344 return unmarshal((*plain)(c))
345}
346
347// getClientCertificate reads the pair of client cert and key from disk and returns a tls.Certificate.
348func (c *TLSConfig) getClientCertificate(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
349 cert, err := tls.LoadX509KeyPair(c.CertFile, c.KeyFile)
350 if err != nil {
351 return nil, fmt.Errorf("unable to use specified client cert (%s) & key (%s): %s", c.CertFile, c.KeyFile, err)
352 }
353 return &cert, nil
354}
355
356// readCAFile reads the CA cert file from disk.
357func readCAFile(f string) ([]byte, error) {
358 data, err := ioutil.ReadFile(f)
359 if err != nil {
360 return nil, fmt.Errorf("unable to load specified CA cert %s: %s", f, err)
361 }
362 return data, nil
363}
364
365// updateRootCA parses the given byte slice as a series of PEM encoded certificates and updates tls.Config.RootCAs.
366func updateRootCA(cfg *tls.Config, b []byte) bool {
367 caCertPool := x509.NewCertPool()
368 if !caCertPool.AppendCertsFromPEM(b) {
369 return false
370 }
371 cfg.RootCAs = caCertPool
372 return true
373}
374
375// tlsRoundTripper is a RoundTripper that updates automatically its TLS
376// configuration whenever the content of the CA file changes.
377type tlsRoundTripper struct {
378 caFile string
379 // newRT returns a new RoundTripper.
380 newRT func(*tls.Config) (http.RoundTripper, error)
381
382 mtx sync.RWMutex
383 rt http.RoundTripper
384 hashCAFile []byte
385 tlsConfig *tls.Config
386}
387
388func newTLSRoundTripper(
389 cfg *tls.Config,
390 caFile string,
391 newRT func(*tls.Config) (http.RoundTripper, error),
392) (http.RoundTripper, error) {
393 t := &tlsRoundTripper{
394 caFile: caFile,
395 newRT: newRT,
396 tlsConfig: cfg,
397 }
398
399 rt, err := t.newRT(t.tlsConfig)
400 if err != nil {
401 return nil, err
402 }
403 t.rt = rt
404
405 _, t.hashCAFile, err = t.getCAWithHash()
406 if err != nil {
407 return nil, err
408 }
409
410 return t, nil
411}
412
413func (t *tlsRoundTripper) getCAWithHash() ([]byte, []byte, error) {
414 b, err := readCAFile(t.caFile)
415 if err != nil {
416 return nil, nil, err
417 }
418 h := md5.Sum(b)
419 return b, h[:], nil
420
421}
422
423// RoundTrip implements the http.RoundTrip interface.
424func (t *tlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
425 b, h, err := t.getCAWithHash()
426 if err != nil {
427 return nil, err
428 }
429
430 t.mtx.RLock()
431 equal := bytes.Equal(h[:], t.hashCAFile)
432 rt := t.rt
433 t.mtx.RUnlock()
434 if equal {
435 // The CA cert hasn't changed, use the existing RoundTripper.
436 return rt.RoundTrip(req)
437 }
438
439 // Create a new RoundTripper.
440 tlsConfig := t.tlsConfig.Clone()
441 if !updateRootCA(tlsConfig, b) {
442 return nil, fmt.Errorf("unable to use specified CA cert %s", t.caFile)
443 }
444 rt, err = t.newRT(tlsConfig)
445 if err != nil {
446 return nil, err
447 }
448 t.CloseIdleConnections()
449
450 t.mtx.Lock()
451 t.rt = rt
452 t.hashCAFile = h[:]
453 t.mtx.Unlock()
454
455 return rt.RoundTrip(req)
456}
457
458func (t *tlsRoundTripper) CloseIdleConnections() {
459 t.mtx.RLock()
460 defer t.mtx.RUnlock()
461 if ci, ok := t.rt.(closeIdler); ok {
462 ci.CloseIdleConnections()
463 }
464}
465
466func (c HTTPClientConfig) String() string {
467 b, err := yaml.Marshal(c)
468 if err != nil {
469 return fmt.Sprintf("<error creating http client config string: %s>", err)
470 }
471 return string(b)
472}
diff --git a/vendor/golang.org/x/crypto/AUTHORS b/vendor/golang.org/x/crypto/AUTHORS
new file mode 100644
index 0000000..2b00ddb
--- /dev/null
+++ b/vendor/golang.org/x/crypto/AUTHORS
@@ -0,0 +1,3 @@
1# This source code refers to The Go Authors for copyright purposes.
2# The master list of authors is in the main Go distribution,
3# visible at https://tip.golang.org/AUTHORS.
diff --git a/vendor/golang.org/x/crypto/CONTRIBUTORS b/vendor/golang.org/x/crypto/CONTRIBUTORS
new file mode 100644
index 0000000..1fbd3e9
--- /dev/null
+++ b/vendor/golang.org/x/crypto/CONTRIBUTORS
@@ -0,0 +1,3 @@
1# This source code was written by the Go contributors.
2# The master list of contributors is in the main Go distribution,
3# visible at https://tip.golang.org/CONTRIBUTORS.
diff --git a/vendor/golang.org/x/crypto/LICENSE b/vendor/golang.org/x/crypto/LICENSE
new file mode 100644
index 0000000..6a66aea
--- /dev/null
+++ b/vendor/golang.org/x/crypto/LICENSE
@@ -0,0 +1,27 @@
1Copyright (c) 2009 The Go Authors. All rights reserved.
2
3Redistribution and use in source and binary forms, with or without
4modification, are permitted provided that the following conditions are
5met:
6
7 * Redistributions of source code must retain the above copyright
8notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above
10copyright notice, this list of conditions and the following disclaimer
11in the documentation and/or other materials provided with the
12distribution.
13 * Neither the name of Google Inc. nor the names of its
14contributors may be used to endorse or promote products derived from
15this software without specific prior written permission.
16
17THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/golang.org/x/crypto/PATENTS b/vendor/golang.org/x/crypto/PATENTS
new file mode 100644
index 0000000..7330990
--- /dev/null
+++ b/vendor/golang.org/x/crypto/PATENTS
@@ -0,0 +1,22 @@
1Additional IP Rights Grant (Patents)
2
3"This implementation" means the copyrightable works distributed by
4Google as part of the Go project.
5
6Google hereby grants to You a perpetual, worldwide, non-exclusive,
7no-charge, royalty-free, irrevocable (except as stated in this section)
8patent license to make, have made, use, offer to sell, sell, import,
9transfer and otherwise run, modify and propagate the contents of this
10implementation of Go, where such license applies only to those patent
11claims, both currently owned or controlled by Google and acquired in
12the future, licensable by Google that are necessarily infringed by this
13implementation of Go. This grant does not include claims that would be
14infringed only as a consequence of further modification of this
15implementation. If you or your agent or exclusive licensee institute or
16order or agree to the institution of patent litigation against any
17entity (including a cross-claim or counterclaim in a lawsuit) alleging
18that this implementation of Go or any code incorporated within this
19implementation of Go constitutes direct or contributory patent
20infringement, or inducement of patent infringement, then any patent
21rights granted to you under this License for this implementation of Go
22shall terminate as of the date such litigation is filed.
diff --git a/vendor/golang.org/x/crypto/bcrypt/base64.go b/vendor/golang.org/x/crypto/bcrypt/base64.go
new file mode 100644
index 0000000..fc31160
--- /dev/null
+++ b/vendor/golang.org/x/crypto/bcrypt/base64.go
@@ -0,0 +1,35 @@
1// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package bcrypt
6
7import "encoding/base64"
8
9const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
10
11var bcEncoding = base64.NewEncoding(alphabet)
12
13func base64Encode(src []byte) []byte {
14 n := bcEncoding.EncodedLen(len(src))
15 dst := make([]byte, n)
16 bcEncoding.Encode(dst, src)
17 for dst[n-1] == '=' {
18 n--
19 }
20 return dst[:n]
21}
22
23func base64Decode(src []byte) ([]byte, error) {
24 numOfEquals := 4 - (len(src) % 4)
25 for i := 0; i < numOfEquals; i++ {
26 src = append(src, '=')
27 }
28
29 dst := make([]byte, bcEncoding.DecodedLen(len(src)))
30 n, err := bcEncoding.Decode(dst, src)
31 if err != nil {
32 return nil, err
33 }
34 return dst[:n], nil
35}
diff --git a/vendor/golang.org/x/crypto/bcrypt/bcrypt.go b/vendor/golang.org/x/crypto/bcrypt/bcrypt.go
new file mode 100644
index 0000000..aeb73f8
--- /dev/null
+++ b/vendor/golang.org/x/crypto/bcrypt/bcrypt.go
@@ -0,0 +1,295 @@
1// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing
6// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
7package bcrypt // import "golang.org/x/crypto/bcrypt"
8
9// The code is a port of Provos and Mazières's C implementation.
10import (
11 "crypto/rand"
12 "crypto/subtle"
13 "errors"
14 "fmt"
15 "io"
16 "strconv"
17
18 "golang.org/x/crypto/blowfish"
19)
20
21const (
22 MinCost int = 4 // the minimum allowable cost as passed in to GenerateFromPassword
23 MaxCost int = 31 // the maximum allowable cost as passed in to GenerateFromPassword
24 DefaultCost int = 10 // the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword
25)
26
27// The error returned from CompareHashAndPassword when a password and hash do
28// not match.
29var ErrMismatchedHashAndPassword = errors.New("crypto/bcrypt: hashedPassword is not the hash of the given password")
30
31// The error returned from CompareHashAndPassword when a hash is too short to
32// be a bcrypt hash.
33var ErrHashTooShort = errors.New("crypto/bcrypt: hashedSecret too short to be a bcrypted password")
34
35// The error returned from CompareHashAndPassword when a hash was created with
36// a bcrypt algorithm newer than this implementation.
37type HashVersionTooNewError byte
38
39func (hv HashVersionTooNewError) Error() string {
40 return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' requested is newer than current version '%c'", byte(hv), majorVersion)
41}
42
43// The error returned from CompareHashAndPassword when a hash starts with something other than '$'
44type InvalidHashPrefixError byte
45
46func (ih InvalidHashPrefixError) Error() string {
47 return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih))
48}
49
50type InvalidCostError int
51
52func (ic InvalidCostError) Error() string {
53 return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost))
54}
55
56const (
57 majorVersion = '2'
58 minorVersion = 'a'
59 maxSaltSize = 16
60 maxCryptedHashSize = 23
61 encodedSaltSize = 22
62 encodedHashSize = 31
63 minHashSize = 59
64)
65
66// magicCipherData is an IV for the 64 Blowfish encryption calls in
67// bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes.
68var magicCipherData = []byte{
69 0x4f, 0x72, 0x70, 0x68,
70 0x65, 0x61, 0x6e, 0x42,
71 0x65, 0x68, 0x6f, 0x6c,
72 0x64, 0x65, 0x72, 0x53,
73 0x63, 0x72, 0x79, 0x44,
74 0x6f, 0x75, 0x62, 0x74,
75}
76
77type hashed struct {
78 hash []byte
79 salt []byte
80 cost int // allowed range is MinCost to MaxCost
81 major byte
82 minor byte
83}
84
85// GenerateFromPassword returns the bcrypt hash of the password at the given
86// cost. If the cost given is less than MinCost, the cost will be set to
87// DefaultCost, instead. Use CompareHashAndPassword, as defined in this package,
88// to compare the returned hashed password with its cleartext version.
89func GenerateFromPassword(password []byte, cost int) ([]byte, error) {
90 p, err := newFromPassword(password, cost)
91 if err != nil {
92 return nil, err
93 }
94 return p.Hash(), nil
95}
96
97// CompareHashAndPassword compares a bcrypt hashed password with its possible
98// plaintext equivalent. Returns nil on success, or an error on failure.
99func CompareHashAndPassword(hashedPassword, password []byte) error {
100 p, err := newFromHash(hashedPassword)
101 if err != nil {
102 return err
103 }
104
105 otherHash, err := bcrypt(password, p.cost, p.salt)
106 if err != nil {
107 return err
108 }
109
110 otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor}
111 if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 {
112 return nil
113 }
114
115 return ErrMismatchedHashAndPassword
116}
117
118// Cost returns the hashing cost used to create the given hashed
119// password. When, in the future, the hashing cost of a password system needs
120// to be increased in order to adjust for greater computational power, this
121// function allows one to establish which passwords need to be updated.
122func Cost(hashedPassword []byte) (int, error) {
123 p, err := newFromHash(hashedPassword)
124 if err != nil {
125 return 0, err
126 }
127 return p.cost, nil
128}
129
130func newFromPassword(password []byte, cost int) (*hashed, error) {
131 if cost < MinCost {
132 cost = DefaultCost
133 }
134 p := new(hashed)
135 p.major = majorVersion
136 p.minor = minorVersion
137
138 err := checkCost(cost)
139 if err != nil {
140 return nil, err
141 }
142 p.cost = cost
143
144 unencodedSalt := make([]byte, maxSaltSize)
145 _, err = io.ReadFull(rand.Reader, unencodedSalt)
146 if err != nil {
147 return nil, err
148 }
149
150 p.salt = base64Encode(unencodedSalt)
151 hash, err := bcrypt(password, p.cost, p.salt)
152 if err != nil {
153 return nil, err
154 }
155 p.hash = hash
156 return p, err
157}
158
159func newFromHash(hashedSecret []byte) (*hashed, error) {
160 if len(hashedSecret) < minHashSize {
161 return nil, ErrHashTooShort
162 }
163 p := new(hashed)
164 n, err := p.decodeVersion(hashedSecret)
165 if err != nil {
166 return nil, err
167 }
168 hashedSecret = hashedSecret[n:]
169 n, err = p.decodeCost(hashedSecret)
170 if err != nil {
171 return nil, err
172 }
173 hashedSecret = hashedSecret[n:]
174
175 // The "+2" is here because we'll have to append at most 2 '=' to the salt
176 // when base64 decoding it in expensiveBlowfishSetup().
177 p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2)
178 copy(p.salt, hashedSecret[:encodedSaltSize])
179
180 hashedSecret = hashedSecret[encodedSaltSize:]
181 p.hash = make([]byte, len(hashedSecret))
182 copy(p.hash, hashedSecret)
183
184 return p, nil
185}
186
187func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) {
188 cipherData := make([]byte, len(magicCipherData))
189 copy(cipherData, magicCipherData)
190
191 c, err := expensiveBlowfishSetup(password, uint32(cost), salt)
192 if err != nil {
193 return nil, err
194 }
195
196 for i := 0; i < 24; i += 8 {
197 for j := 0; j < 64; j++ {
198 c.Encrypt(cipherData[i:i+8], cipherData[i:i+8])
199 }
200 }
201
202 // Bug compatibility with C bcrypt implementations. We only encode 23 of
203 // the 24 bytes encrypted.
204 hsh := base64Encode(cipherData[:maxCryptedHashSize])
205 return hsh, nil
206}
207
208func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) {
209 csalt, err := base64Decode(salt)
210 if err != nil {
211 return nil, err
212 }
213
214 // Bug compatibility with C bcrypt implementations. They use the trailing
215 // NULL in the key string during expansion.
216 // We copy the key to prevent changing the underlying array.
217 ckey := append(key[:len(key):len(key)], 0)
218
219 c, err := blowfish.NewSaltedCipher(ckey, csalt)
220 if err != nil {
221 return nil, err
222 }
223
224 var i, rounds uint64
225 rounds = 1 << cost
226 for i = 0; i < rounds; i++ {
227 blowfish.ExpandKey(ckey, c)
228 blowfish.ExpandKey(csalt, c)
229 }
230
231 return c, nil
232}
233
234func (p *hashed) Hash() []byte {
235 arr := make([]byte, 60)
236 arr[0] = '$'
237 arr[1] = p.major
238 n := 2
239 if p.minor != 0 {
240 arr[2] = p.minor
241 n = 3
242 }
243 arr[n] = '$'
244 n++
245 copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
246 n += 2
247 arr[n] = '$'
248 n++
249 copy(arr[n:], p.salt)
250 n += encodedSaltSize
251 copy(arr[n:], p.hash)
252 n += encodedHashSize
253 return arr[:n]
254}
255
256func (p *hashed) decodeVersion(sbytes []byte) (int, error) {
257 if sbytes[0] != '$' {
258 return -1, InvalidHashPrefixError(sbytes[0])
259 }
260 if sbytes[1] > majorVersion {
261 return -1, HashVersionTooNewError(sbytes[1])
262 }
263 p.major = sbytes[1]
264 n := 3
265 if sbytes[2] != '$' {
266 p.minor = sbytes[2]
267 n++
268 }
269 return n, nil
270}
271
272// sbytes should begin where decodeVersion left off.
273func (p *hashed) decodeCost(sbytes []byte) (int, error) {
274 cost, err := strconv.Atoi(string(sbytes[0:2]))
275 if err != nil {
276 return -1, err
277 }
278 err = checkCost(cost)
279 if err != nil {
280 return -1, err
281 }
282 p.cost = cost
283 return 3, nil
284}
285
286func (p *hashed) String() string {
287 return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor)
288}
289
290func checkCost(cost int) error {
291 if cost < MinCost || cost > MaxCost {
292 return InvalidCostError(cost)
293 }
294 return nil
295}
diff --git a/vendor/golang.org/x/crypto/blowfish/block.go b/vendor/golang.org/x/crypto/blowfish/block.go
new file mode 100644
index 0000000..9d80f19
--- /dev/null
+++ b/vendor/golang.org/x/crypto/blowfish/block.go
@@ -0,0 +1,159 @@
1// Copyright 2010 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package blowfish
6
7// getNextWord returns the next big-endian uint32 value from the byte slice
8// at the given position in a circular manner, updating the position.
9func getNextWord(b []byte, pos *int) uint32 {
10 var w uint32
11 j := *pos
12 for i := 0; i < 4; i++ {
13 w = w<<8 | uint32(b[j])
14 j++
15 if j >= len(b) {
16 j = 0
17 }
18 }
19 *pos = j
20 return w
21}
22
23// ExpandKey performs a key expansion on the given *Cipher. Specifically, it
24// performs the Blowfish algorithm's key schedule which sets up the *Cipher's
25// pi and substitution tables for calls to Encrypt. This is used, primarily,
26// by the bcrypt package to reuse the Blowfish key schedule during its
27// set up. It's unlikely that you need to use this directly.
28func ExpandKey(key []byte, c *Cipher) {
29 j := 0
30 for i := 0; i < 18; i++ {
31 // Using inlined getNextWord for performance.
32 var d uint32
33 for k := 0; k < 4; k++ {
34 d = d<<8 | uint32(key[j])
35 j++
36 if j >= len(key) {
37 j = 0
38 }
39 }
40 c.p[i] ^= d
41 }
42
43 var l, r uint32
44 for i := 0; i < 18; i += 2 {
45 l, r = encryptBlock(l, r, c)
46 c.p[i], c.p[i+1] = l, r
47 }
48
49 for i := 0; i < 256; i += 2 {
50 l, r = encryptBlock(l, r, c)
51 c.s0[i], c.s0[i+1] = l, r
52 }
53 for i := 0; i < 256; i += 2 {
54 l, r = encryptBlock(l, r, c)
55 c.s1[i], c.s1[i+1] = l, r
56 }
57 for i := 0; i < 256; i += 2 {
58 l, r = encryptBlock(l, r, c)
59 c.s2[i], c.s2[i+1] = l, r
60 }
61 for i := 0; i < 256; i += 2 {
62 l, r = encryptBlock(l, r, c)
63 c.s3[i], c.s3[i+1] = l, r
64 }
65}
66
67// This is similar to ExpandKey, but folds the salt during the key
68// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero
69// salt passed in, reusing ExpandKey turns out to be a place of inefficiency
70// and specializing it here is useful.
71func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) {
72 j := 0
73 for i := 0; i < 18; i++ {
74 c.p[i] ^= getNextWord(key, &j)
75 }
76
77 j = 0
78 var l, r uint32
79 for i := 0; i < 18; i += 2 {
80 l ^= getNextWord(salt, &j)
81 r ^= getNextWord(salt, &j)
82 l, r = encryptBlock(l, r, c)
83 c.p[i], c.p[i+1] = l, r
84 }
85
86 for i := 0; i < 256; i += 2 {
87 l ^= getNextWord(salt, &j)
88 r ^= getNextWord(salt, &j)
89 l, r = encryptBlock(l, r, c)
90 c.s0[i], c.s0[i+1] = l, r
91 }
92
93 for i := 0; i < 256; i += 2 {
94 l ^= getNextWord(salt, &j)
95 r ^= getNextWord(salt, &j)
96 l, r = encryptBlock(l, r, c)
97 c.s1[i], c.s1[i+1] = l, r
98 }
99
100 for i := 0; i < 256; i += 2 {
101 l ^= getNextWord(salt, &j)
102 r ^= getNextWord(salt, &j)
103 l, r = encryptBlock(l, r, c)
104 c.s2[i], c.s2[i+1] = l, r
105 }
106
107 for i := 0; i < 256; i += 2 {
108 l ^= getNextWord(salt, &j)
109 r ^= getNextWord(salt, &j)
110 l, r = encryptBlock(l, r, c)
111 c.s3[i], c.s3[i+1] = l, r
112 }
113}
114
115func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
116 xl, xr := l, r
117 xl ^= c.p[0]
118 xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[1]
119 xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[2]
120 xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[3]
121 xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[4]
122 xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[5]
123 xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[6]
124 xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[7]
125 xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[8]
126 xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[9]
127 xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[10]
128 xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[11]
129 xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[12]
130 xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[13]
131 xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[14]
132 xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[15]
133 xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[16]
134 xr ^= c.p[17]
135 return xr, xl
136}
137
138func decryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
139 xl, xr := l, r
140 xl ^= c.p[17]
141 xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[16]
142 xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[15]
143 xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[14]
144 xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[13]
145 xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[12]
146 xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[11]
147 xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[10]
148 xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[9]
149 xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[8]
150 xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[7]
151 xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[6]
152 xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[5]
153 xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[4]
154 xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[3]
155 xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[2]
156 xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[1]
157 xr ^= c.p[0]
158 return xr, xl
159}
diff --git a/vendor/golang.org/x/crypto/blowfish/cipher.go b/vendor/golang.org/x/crypto/blowfish/cipher.go
new file mode 100644
index 0000000..213bf20
--- /dev/null
+++ b/vendor/golang.org/x/crypto/blowfish/cipher.go
@@ -0,0 +1,99 @@
1// Copyright 2010 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package blowfish implements Bruce Schneier's Blowfish encryption algorithm.
6//
7// Blowfish is a legacy cipher and its short block size makes it vulnerable to
8// birthday bound attacks (see https://sweet32.info). It should only be used
9// where compatibility with legacy systems, not security, is the goal.
10//
11// Deprecated: any new system should use AES (from crypto/aes, if necessary in
12// an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from
13// golang.org/x/crypto/chacha20poly1305).
14package blowfish // import "golang.org/x/crypto/blowfish"
15
16// The code is a port of Bruce Schneier's C implementation.
17// See https://www.schneier.com/blowfish.html.
18
19import "strconv"
20
21// The Blowfish block size in bytes.
22const BlockSize = 8
23
24// A Cipher is an instance of Blowfish encryption using a particular key.
25type Cipher struct {
26 p [18]uint32
27 s0, s1, s2, s3 [256]uint32
28}
29
30type KeySizeError int
31
32func (k KeySizeError) Error() string {
33 return "crypto/blowfish: invalid key size " + strconv.Itoa(int(k))
34}
35
36// NewCipher creates and returns a Cipher.
37// The key argument should be the Blowfish key, from 1 to 56 bytes.
38func NewCipher(key []byte) (*Cipher, error) {
39 var result Cipher
40 if k := len(key); k < 1 || k > 56 {
41 return nil, KeySizeError(k)
42 }
43 initCipher(&result)
44 ExpandKey(key, &result)
45 return &result, nil
46}
47
48// NewSaltedCipher creates a returns a Cipher that folds a salt into its key
49// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is
50// sufficient and desirable. For bcrypt compatibility, the key can be over 56
51// bytes.
52func NewSaltedCipher(key, salt []byte) (*Cipher, error) {
53 if len(salt) == 0 {
54 return NewCipher(key)
55 }
56 var result Cipher
57 if k := len(key); k < 1 {
58 return nil, KeySizeError(k)
59 }
60 initCipher(&result)
61 expandKeyWithSalt(key, salt, &result)
62 return &result, nil
63}
64
65// BlockSize returns the Blowfish block size, 8 bytes.
66// It is necessary to satisfy the Block interface in the
67// package "crypto/cipher".
68func (c *Cipher) BlockSize() int { return BlockSize }
69
70// Encrypt encrypts the 8-byte buffer src using the key k
71// and stores the result in dst.
72// Note that for amounts of data larger than a block,
73// it is not safe to just call Encrypt on successive blocks;
74// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
75func (c *Cipher) Encrypt(dst, src []byte) {
76 l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
77 r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
78 l, r = encryptBlock(l, r, c)
79 dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
80 dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
81}
82
83// Decrypt decrypts the 8-byte buffer src using the key k
84// and stores the result in dst.
85func (c *Cipher) Decrypt(dst, src []byte) {
86 l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
87 r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
88 l, r = decryptBlock(l, r, c)
89 dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
90 dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
91}
92
93func initCipher(c *Cipher) {
94 copy(c.p[0:], p[0:])
95 copy(c.s0[0:], s0[0:])
96 copy(c.s1[0:], s1[0:])
97 copy(c.s2[0:], s2[0:])
98 copy(c.s3[0:], s3[0:])
99}
diff --git a/vendor/golang.org/x/crypto/blowfish/const.go b/vendor/golang.org/x/crypto/blowfish/const.go
new file mode 100644
index 0000000..d040775
--- /dev/null
+++ b/vendor/golang.org/x/crypto/blowfish/const.go
@@ -0,0 +1,199 @@
1// Copyright 2010 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// The startup permutation array and substitution boxes.
6// They are the hexadecimal digits of PI; see:
7// https://www.schneier.com/code/constants.txt.
8
9package blowfish
10
11var s0 = [256]uint32{
12 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,
13 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
14 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658,
15 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
16 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e,
17 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
18 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6,
19 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
20 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c,
21 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
22 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1,
23 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
24 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a,
25 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
26 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176,
27 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
28 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706,
29 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
30 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b,
31 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
32 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c,
33 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
34 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a,
35 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
36 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,
37 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
38 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8,
39 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
40 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33,
41 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
42 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0,
43 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
44 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777,
45 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
46 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,
47 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
48 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e,
49 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
50 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9,
51 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
52 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f,
53 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
54 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
55}
56
57var s1 = [256]uint32{
58 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d,
59 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
60 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65,
61 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
62 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9,
63 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
64 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d,
65 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
66 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,
67 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
68 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908,
69 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
70 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124,
71 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
72 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908,
73 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
74 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b,
75 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
76 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,
77 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
78 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d,
79 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
80 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5,
81 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
82 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96,
83 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
84 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca,
85 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
86 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,
87 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
88 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054,
89 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
90 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea,
91 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
92 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646,
93 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
94 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea,
95 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
96 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,
97 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
98 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd,
99 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
100 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
101}
102
103var s2 = [256]uint32{
104 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7,
105 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
106 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af,
107 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
108 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4,
109 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
110 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec,
111 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
112 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332,
113 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
114 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58,
115 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
116 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22,
117 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
118 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60,
119 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
120 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99,
121 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
122 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74,
123 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
124 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3,
125 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
126 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979,
127 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
128 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa,
129 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
130 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086,
131 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
132 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24,
133 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
134 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84,
135 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
136 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09,
137 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
138 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe,
139 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
140 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0,
141 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
142 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188,
143 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
144 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8,
145 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
146 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
147}
148
149var s3 = [256]uint32{
150 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742,
151 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
152 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79,
153 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
154 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a,
155 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
156 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,
157 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
158 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797,
159 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
160 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6,
161 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
162 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba,
163 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
164 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5,
165 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
166 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,
167 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
168 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd,
169 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
170 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb,
171 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
172 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc,
173 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
174 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc,
175 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
176 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a,
177 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
178 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a,
179 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
180 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b,
181 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
182 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e,
183 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
184 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623,
185 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
186 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a,
187 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
188 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3,
189 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
190 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c,
191 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
192 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
193}
194
195var p = [18]uint32{
196 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
197 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
198 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b,
199}
diff --git a/vendor/golang.org/x/net/internal/timeseries/timeseries.go b/vendor/golang.org/x/net/internal/timeseries/timeseries.go
new file mode 100644
index 0000000..dc5225b
--- /dev/null
+++ b/vendor/golang.org/x/net/internal/timeseries/timeseries.go
@@ -0,0 +1,525 @@
1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package timeseries implements a time series structure for stats collection.
6package timeseries // import "golang.org/x/net/internal/timeseries"
7
8import (
9 "fmt"
10 "log"
11 "time"
12)
13
14const (
15 timeSeriesNumBuckets = 64
16 minuteHourSeriesNumBuckets = 60
17)
18
19var timeSeriesResolutions = []time.Duration{
20 1 * time.Second,
21 10 * time.Second,
22 1 * time.Minute,
23 10 * time.Minute,
24 1 * time.Hour,
25 6 * time.Hour,
26 24 * time.Hour, // 1 day
27 7 * 24 * time.Hour, // 1 week
28 4 * 7 * 24 * time.Hour, // 4 weeks
29 16 * 7 * 24 * time.Hour, // 16 weeks
30}
31
32var minuteHourSeriesResolutions = []time.Duration{
33 1 * time.Second,
34 1 * time.Minute,
35}
36
37// An Observable is a kind of data that can be aggregated in a time series.
38type Observable interface {
39 Multiply(ratio float64) // Multiplies the data in self by a given ratio
40 Add(other Observable) // Adds the data from a different observation to self
41 Clear() // Clears the observation so it can be reused.
42 CopyFrom(other Observable) // Copies the contents of a given observation to self
43}
44
45// Float attaches the methods of Observable to a float64.
46type Float float64
47
48// NewFloat returns a Float.
49func NewFloat() Observable {
50 f := Float(0)
51 return &f
52}
53
54// String returns the float as a string.
55func (f *Float) String() string { return fmt.Sprintf("%g", f.Value()) }
56
57// Value returns the float's value.
58func (f *Float) Value() float64 { return float64(*f) }
59
60func (f *Float) Multiply(ratio float64) { *f *= Float(ratio) }
61
62func (f *Float) Add(other Observable) {
63 o := other.(*Float)
64 *f += *o
65}
66
67func (f *Float) Clear() { *f = 0 }
68
69func (f *Float) CopyFrom(other Observable) {
70 o := other.(*Float)
71 *f = *o
72}
73
74// A Clock tells the current time.
75type Clock interface {
76 Time() time.Time
77}
78
79type defaultClock int
80
81var defaultClockInstance defaultClock
82
83func (defaultClock) Time() time.Time { return time.Now() }
84
85// Information kept per level. Each level consists of a circular list of
86// observations. The start of the level may be derived from end and the
87// len(buckets) * sizeInMillis.
88type tsLevel struct {
89 oldest int // index to oldest bucketed Observable
90 newest int // index to newest bucketed Observable
91 end time.Time // end timestamp for this level
92 size time.Duration // duration of the bucketed Observable
93 buckets []Observable // collections of observations
94 provider func() Observable // used for creating new Observable
95}
96
97func (l *tsLevel) Clear() {
98 l.oldest = 0
99 l.newest = len(l.buckets) - 1
100 l.end = time.Time{}
101 for i := range l.buckets {
102 if l.buckets[i] != nil {
103 l.buckets[i].Clear()
104 l.buckets[i] = nil
105 }
106 }
107}
108
109func (l *tsLevel) InitLevel(size time.Duration, numBuckets int, f func() Observable) {
110 l.size = size
111 l.provider = f
112 l.buckets = make([]Observable, numBuckets)
113}
114
115// Keeps a sequence of levels. Each level is responsible for storing data at
116// a given resolution. For example, the first level stores data at a one
117// minute resolution while the second level stores data at a one hour
118// resolution.
119
120// Each level is represented by a sequence of buckets. Each bucket spans an
121// interval equal to the resolution of the level. New observations are added
122// to the last bucket.
123type timeSeries struct {
124 provider func() Observable // make more Observable
125 numBuckets int // number of buckets in each level
126 levels []*tsLevel // levels of bucketed Observable
127 lastAdd time.Time // time of last Observable tracked
128 total Observable // convenient aggregation of all Observable
129 clock Clock // Clock for getting current time
130 pending Observable // observations not yet bucketed
131 pendingTime time.Time // what time are we keeping in pending
132 dirty bool // if there are pending observations
133}
134
135// init initializes a level according to the supplied criteria.
136func (ts *timeSeries) init(resolutions []time.Duration, f func() Observable, numBuckets int, clock Clock) {
137 ts.provider = f
138 ts.numBuckets = numBuckets
139 ts.clock = clock
140 ts.levels = make([]*tsLevel, len(resolutions))
141
142 for i := range resolutions {
143 if i > 0 && resolutions[i-1] >= resolutions[i] {
144 log.Print("timeseries: resolutions must be monotonically increasing")
145 break
146 }
147 newLevel := new(tsLevel)
148 newLevel.InitLevel(resolutions[i], ts.numBuckets, ts.provider)
149 ts.levels[i] = newLevel
150 }
151
152 ts.Clear()
153}
154
155// Clear removes all observations from the time series.
156func (ts *timeSeries) Clear() {
157 ts.lastAdd = time.Time{}
158 ts.total = ts.resetObservation(ts.total)
159 ts.pending = ts.resetObservation(ts.pending)
160 ts.pendingTime = time.Time{}
161 ts.dirty = false
162
163 for i := range ts.levels {
164 ts.levels[i].Clear()
165 }
166}
167
168// Add records an observation at the current time.
169func (ts *timeSeries) Add(observation Observable) {
170 ts.AddWithTime(observation, ts.clock.Time())
171}
172
173// AddWithTime records an observation at the specified time.
174func (ts *timeSeries) AddWithTime(observation Observable, t time.Time) {
175
176 smallBucketDuration := ts.levels[0].size
177
178 if t.After(ts.lastAdd) {
179 ts.lastAdd = t
180 }
181
182 if t.After(ts.pendingTime) {
183 ts.advance(t)
184 ts.mergePendingUpdates()
185 ts.pendingTime = ts.levels[0].end
186 ts.pending.CopyFrom(observation)
187 ts.dirty = true
188 } else if t.After(ts.pendingTime.Add(-1 * smallBucketDuration)) {
189 // The observation is close enough to go into the pending bucket.
190 // This compensates for clock skewing and small scheduling delays
191 // by letting the update stay in the fast path.
192 ts.pending.Add(observation)
193 ts.dirty = true
194 } else {
195 ts.mergeValue(observation, t)
196 }
197}
198
199// mergeValue inserts the observation at the specified time in the past into all levels.
200func (ts *timeSeries) mergeValue(observation Observable, t time.Time) {
201 for _, level := range ts.levels {
202 index := (ts.numBuckets - 1) - int(level.end.Sub(t)/level.size)
203 if 0 <= index && index < ts.numBuckets {
204 bucketNumber := (level.oldest + index) % ts.numBuckets
205 if level.buckets[bucketNumber] == nil {
206 level.buckets[bucketNumber] = level.provider()
207 }
208 level.buckets[bucketNumber].Add(observation)
209 }
210 }
211 ts.total.Add(observation)
212}
213
214// mergePendingUpdates applies the pending updates into all levels.
215func (ts *timeSeries) mergePendingUpdates() {
216 if ts.dirty {
217 ts.mergeValue(ts.pending, ts.pendingTime)
218 ts.pending = ts.resetObservation(ts.pending)
219 ts.dirty = false
220 }
221}
222
223// advance cycles the buckets at each level until the latest bucket in
224// each level can hold the time specified.
225func (ts *timeSeries) advance(t time.Time) {
226 if !t.After(ts.levels[0].end) {
227 return
228 }
229 for i := 0; i < len(ts.levels); i++ {
230 level := ts.levels[i]
231 if !level.end.Before(t) {
232 break
233 }
234
235 // If the time is sufficiently far, just clear the level and advance
236 // directly.
237 if !t.Before(level.end.Add(level.size * time.Duration(ts.numBuckets))) {
238 for _, b := range level.buckets {
239 ts.resetObservation(b)
240 }
241 level.end = time.Unix(0, (t.UnixNano()/level.size.Nanoseconds())*level.size.Nanoseconds())
242 }
243
244 for t.After(level.end) {
245 level.end = level.end.Add(level.size)
246 level.newest = level.oldest
247 level.oldest = (level.oldest + 1) % ts.numBuckets
248 ts.resetObservation(level.buckets[level.newest])
249 }
250
251 t = level.end
252 }
253}
254
255// Latest returns the sum of the num latest buckets from the level.
256func (ts *timeSeries) Latest(level, num int) Observable {
257 now := ts.clock.Time()
258 if ts.levels[0].end.Before(now) {
259 ts.advance(now)
260 }
261
262 ts.mergePendingUpdates()
263
264 result := ts.provider()
265 l := ts.levels[level]
266 index := l.newest
267
268 for i := 0; i < num; i++ {
269 if l.buckets[index] != nil {
270 result.Add(l.buckets[index])
271 }
272 if index == 0 {
273 index = ts.numBuckets
274 }
275 index--
276 }
277
278 return result
279}
280
281// LatestBuckets returns a copy of the num latest buckets from level.
282func (ts *timeSeries) LatestBuckets(level, num int) []Observable {
283 if level < 0 || level > len(ts.levels) {
284 log.Print("timeseries: bad level argument: ", level)
285 return nil
286 }
287 if num < 0 || num >= ts.numBuckets {
288 log.Print("timeseries: bad num argument: ", num)
289 return nil
290 }
291
292 results := make([]Observable, num)
293 now := ts.clock.Time()
294 if ts.levels[0].end.Before(now) {
295 ts.advance(now)
296 }
297
298 ts.mergePendingUpdates()
299
300 l := ts.levels[level]
301 index := l.newest
302
303 for i := 0; i < num; i++ {
304 result := ts.provider()
305 results[i] = result
306 if l.buckets[index] != nil {
307 result.CopyFrom(l.buckets[index])
308 }
309
310 if index == 0 {
311 index = ts.numBuckets
312 }
313 index -= 1
314 }
315 return results
316}
317
318// ScaleBy updates observations by scaling by factor.
319func (ts *timeSeries) ScaleBy(factor float64) {
320 for _, l := range ts.levels {
321 for i := 0; i < ts.numBuckets; i++ {
322 l.buckets[i].Multiply(factor)
323 }
324 }
325
326 ts.total.Multiply(factor)
327 ts.pending.Multiply(factor)
328}
329
330// Range returns the sum of observations added over the specified time range.
331// If start or finish times don't fall on bucket boundaries of the same
332// level, then return values are approximate answers.
333func (ts *timeSeries) Range(start, finish time.Time) Observable {
334 return ts.ComputeRange(start, finish, 1)[0]
335}
336
337// Recent returns the sum of observations from the last delta.
338func (ts *timeSeries) Recent(delta time.Duration) Observable {
339 now := ts.clock.Time()
340 return ts.Range(now.Add(-delta), now)
341}
342
343// Total returns the total of all observations.
344func (ts *timeSeries) Total() Observable {
345 ts.mergePendingUpdates()
346 return ts.total
347}
348
349// ComputeRange computes a specified number of values into a slice using
350// the observations recorded over the specified time period. The return
351// values are approximate if the start or finish times don't fall on the
352// bucket boundaries at the same level or if the number of buckets spanning
353// the range is not an integral multiple of num.
354func (ts *timeSeries) ComputeRange(start, finish time.Time, num int) []Observable {
355 if start.After(finish) {
356 log.Printf("timeseries: start > finish, %v>%v", start, finish)
357 return nil
358 }
359
360 if num < 0 {
361 log.Printf("timeseries: num < 0, %v", num)
362 return nil
363 }
364
365 results := make([]Observable, num)
366
367 for _, l := range ts.levels {
368 if !start.Before(l.end.Add(-l.size * time.Duration(ts.numBuckets))) {
369 ts.extract(l, start, finish, num, results)
370 return results
371 }
372 }
373
374 // Failed to find a level that covers the desired range. So just
375 // extract from the last level, even if it doesn't cover the entire
376 // desired range.
377 ts.extract(ts.levels[len(ts.levels)-1], start, finish, num, results)
378
379 return results
380}
381
382// RecentList returns the specified number of values in slice over the most
383// recent time period of the specified range.
384func (ts *timeSeries) RecentList(delta time.Duration, num int) []Observable {
385 if delta < 0 {
386 return nil
387 }
388 now := ts.clock.Time()
389 return ts.ComputeRange(now.Add(-delta), now, num)
390}
391
392// extract returns a slice of specified number of observations from a given
393// level over a given range.
394func (ts *timeSeries) extract(l *tsLevel, start, finish time.Time, num int, results []Observable) {
395 ts.mergePendingUpdates()
396
397 srcInterval := l.size
398 dstInterval := finish.Sub(start) / time.Duration(num)
399 dstStart := start
400 srcStart := l.end.Add(-srcInterval * time.Duration(ts.numBuckets))
401
402 srcIndex := 0
403
404 // Where should scanning start?
405 if dstStart.After(srcStart) {
406 advance := int(dstStart.Sub(srcStart) / srcInterval)
407 srcIndex += advance
408 srcStart = srcStart.Add(time.Duration(advance) * srcInterval)
409 }
410
411 // The i'th value is computed as show below.
412 // interval = (finish/start)/num
413 // i'th value = sum of observation in range
414 // [ start + i * interval,
415 // start + (i + 1) * interval )
416 for i := 0; i < num; i++ {
417 results[i] = ts.resetObservation(results[i])
418 dstEnd := dstStart.Add(dstInterval)
419 for srcIndex < ts.numBuckets && srcStart.Before(dstEnd) {
420 srcEnd := srcStart.Add(srcInterval)
421 if srcEnd.After(ts.lastAdd) {
422 srcEnd = ts.lastAdd
423 }
424
425 if !srcEnd.Before(dstStart) {
426 srcValue := l.buckets[(srcIndex+l.oldest)%ts.numBuckets]
427 if !srcStart.Before(dstStart) && !srcEnd.After(dstEnd) {
428 // dst completely contains src.
429 if srcValue != nil {
430 results[i].Add(srcValue)
431 }
432 } else {
433 // dst partially overlaps src.
434 overlapStart := maxTime(srcStart, dstStart)
435 overlapEnd := minTime(srcEnd, dstEnd)
436 base := srcEnd.Sub(srcStart)
437 fraction := overlapEnd.Sub(overlapStart).Seconds() / base.Seconds()
438
439 used := ts.provider()
440 if srcValue != nil {
441 used.CopyFrom(srcValue)
442 }
443 used.Multiply(fraction)
444 results[i].Add(used)
445 }
446
447 if srcEnd.After(dstEnd) {
448 break
449 }
450 }
451 srcIndex++
452 srcStart = srcStart.Add(srcInterval)
453 }
454 dstStart = dstStart.Add(dstInterval)
455 }
456}
457
458// resetObservation clears the content so the struct may be reused.
459func (ts *timeSeries) resetObservation(observation Observable) Observable {
460 if observation == nil {
461 observation = ts.provider()
462 } else {
463 observation.Clear()
464 }
465 return observation
466}
467
468// TimeSeries tracks data at granularities from 1 second to 16 weeks.
469type TimeSeries struct {
470 timeSeries
471}
472
473// NewTimeSeries creates a new TimeSeries using the function provided for creating new Observable.
474func NewTimeSeries(f func() Observable) *TimeSeries {
475 return NewTimeSeriesWithClock(f, defaultClockInstance)
476}
477
478// NewTimeSeriesWithClock creates a new TimeSeries using the function provided for creating new Observable and the clock for
479// assigning timestamps.
480func NewTimeSeriesWithClock(f func() Observable, clock Clock) *TimeSeries {
481 ts := new(TimeSeries)
482 ts.timeSeries.init(timeSeriesResolutions, f, timeSeriesNumBuckets, clock)
483 return ts
484}
485
486// MinuteHourSeries tracks data at granularities of 1 minute and 1 hour.
487type MinuteHourSeries struct {
488 timeSeries
489}
490
491// NewMinuteHourSeries creates a new MinuteHourSeries using the function provided for creating new Observable.
492func NewMinuteHourSeries(f func() Observable) *MinuteHourSeries {
493 return NewMinuteHourSeriesWithClock(f, defaultClockInstance)
494}
495
496// NewMinuteHourSeriesWithClock creates a new MinuteHourSeries using the function provided for creating new Observable and the clock for
497// assigning timestamps.
498func NewMinuteHourSeriesWithClock(f func() Observable, clock Clock) *MinuteHourSeries {
499 ts := new(MinuteHourSeries)
500 ts.timeSeries.init(minuteHourSeriesResolutions, f,
501 minuteHourSeriesNumBuckets, clock)
502 return ts
503}
504
505func (ts *MinuteHourSeries) Minute() Observable {
506 return ts.timeSeries.Latest(0, 60)
507}
508
509func (ts *MinuteHourSeries) Hour() Observable {
510 return ts.timeSeries.Latest(1, 60)
511}
512
513func minTime(a, b time.Time) time.Time {
514 if a.Before(b) {
515 return a
516 }
517 return b
518}
519
520func maxTime(a, b time.Time) time.Time {
521 if a.After(b) {
522 return a
523 }
524 return b
525}
diff --git a/vendor/golang.org/x/net/trace/events.go b/vendor/golang.org/x/net/trace/events.go
new file mode 100644
index 0000000..c646a69
--- /dev/null
+++ b/vendor/golang.org/x/net/trace/events.go
@@ -0,0 +1,532 @@
1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package trace
6
7import (
8 "bytes"
9 "fmt"
10 "html/template"
11 "io"
12 "log"
13 "net/http"
14 "runtime"
15 "sort"
16 "strconv"
17 "strings"
18 "sync"
19 "sync/atomic"
20 "text/tabwriter"
21 "time"
22)
23
24const maxEventsPerLog = 100
25
26type bucket struct {
27 MaxErrAge time.Duration
28 String string
29}
30
31var buckets = []bucket{
32 {0, "total"},
33 {10 * time.Second, "errs<10s"},
34 {1 * time.Minute, "errs<1m"},
35 {10 * time.Minute, "errs<10m"},
36 {1 * time.Hour, "errs<1h"},
37 {10 * time.Hour, "errs<10h"},
38 {24000 * time.Hour, "errors"},
39}
40
41// RenderEvents renders the HTML page typically served at /debug/events.
42// It does not do any auth checking. The request may be nil.
43//
44// Most users will use the Events handler.
45func RenderEvents(w http.ResponseWriter, req *http.Request, sensitive bool) {
46 now := time.Now()
47 data := &struct {
48 Families []string // family names
49 Buckets []bucket
50 Counts [][]int // eventLog count per family/bucket
51
52 // Set when a bucket has been selected.
53 Family string
54 Bucket int
55 EventLogs eventLogs
56 Expanded bool
57 }{
58 Buckets: buckets,
59 }
60
61 data.Families = make([]string, 0, len(families))
62 famMu.RLock()
63 for name := range families {
64 data.Families = append(data.Families, name)
65 }
66 famMu.RUnlock()
67 sort.Strings(data.Families)
68
69 // Count the number of eventLogs in each family for each error age.
70 data.Counts = make([][]int, len(data.Families))
71 for i, name := range data.Families {
72 // TODO(sameer): move this loop under the family lock.
73 f := getEventFamily(name)
74 data.Counts[i] = make([]int, len(data.Buckets))
75 for j, b := range data.Buckets {
76 data.Counts[i][j] = f.Count(now, b.MaxErrAge)
77 }
78 }
79
80 if req != nil {
81 var ok bool
82 data.Family, data.Bucket, ok = parseEventsArgs(req)
83 if !ok {
84 // No-op
85 } else {
86 data.EventLogs = getEventFamily(data.Family).Copy(now, buckets[data.Bucket].MaxErrAge)
87 }
88 if data.EventLogs != nil {
89 defer data.EventLogs.Free()
90 sort.Sort(data.EventLogs)
91 }
92 if exp, err := strconv.ParseBool(req.FormValue("exp")); err == nil {
93 data.Expanded = exp
94 }
95 }
96
97 famMu.RLock()
98 defer famMu.RUnlock()
99 if err := eventsTmpl().Execute(w, data); err != nil {
100 log.Printf("net/trace: Failed executing template: %v", err)
101 }
102}
103
104func parseEventsArgs(req *http.Request) (fam string, b int, ok bool) {
105 fam, bStr := req.FormValue("fam"), req.FormValue("b")
106 if fam == "" || bStr == "" {
107 return "", 0, false
108 }
109 b, err := strconv.Atoi(bStr)
110 if err != nil || b < 0 || b >= len(buckets) {
111 return "", 0, false
112 }
113 return fam, b, true
114}
115
116// An EventLog provides a log of events associated with a specific object.
117type EventLog interface {
118 // Printf formats its arguments with fmt.Sprintf and adds the
119 // result to the event log.
120 Printf(format string, a ...interface{})
121
122 // Errorf is like Printf, but it marks this event as an error.
123 Errorf(format string, a ...interface{})
124
125 // Finish declares that this event log is complete.
126 // The event log should not be used after calling this method.
127 Finish()
128}
129
130// NewEventLog returns a new EventLog with the specified family name
131// and title.
132func NewEventLog(family, title string) EventLog {
133 el := newEventLog()
134 el.ref()
135 el.Family, el.Title = family, title
136 el.Start = time.Now()
137 el.events = make([]logEntry, 0, maxEventsPerLog)
138 el.stack = make([]uintptr, 32)
139 n := runtime.Callers(2, el.stack)
140 el.stack = el.stack[:n]
141
142 getEventFamily(family).add(el)
143 return el
144}
145
146func (el *eventLog) Finish() {
147 getEventFamily(el.Family).remove(el)
148 el.unref() // matches ref in New
149}
150
151var (
152 famMu sync.RWMutex
153 families = make(map[string]*eventFamily) // family name => family
154)
155
156func getEventFamily(fam string) *eventFamily {
157 famMu.Lock()
158 defer famMu.Unlock()
159 f := families[fam]
160 if f == nil {
161 f = &eventFamily{}
162 families[fam] = f
163 }
164 return f
165}
166
167type eventFamily struct {
168 mu sync.RWMutex
169 eventLogs eventLogs
170}
171
172func (f *eventFamily) add(el *eventLog) {
173 f.mu.Lock()
174 f.eventLogs = append(f.eventLogs, el)
175 f.mu.Unlock()
176}
177
178func (f *eventFamily) remove(el *eventLog) {
179 f.mu.Lock()
180 defer f.mu.Unlock()
181 for i, el0 := range f.eventLogs {
182 if el == el0 {
183 copy(f.eventLogs[i:], f.eventLogs[i+1:])
184 f.eventLogs = f.eventLogs[:len(f.eventLogs)-1]
185 return
186 }
187 }
188}
189
190func (f *eventFamily) Count(now time.Time, maxErrAge time.Duration) (n int) {
191 f.mu.RLock()
192 defer f.mu.RUnlock()
193 for _, el := range f.eventLogs {
194 if el.hasRecentError(now, maxErrAge) {
195 n++
196 }
197 }
198 return
199}
200
201func (f *eventFamily) Copy(now time.Time, maxErrAge time.Duration) (els eventLogs) {
202 f.mu.RLock()
203 defer f.mu.RUnlock()
204 els = make(eventLogs, 0, len(f.eventLogs))
205 for _, el := range f.eventLogs {
206 if el.hasRecentError(now, maxErrAge) {
207 el.ref()
208 els = append(els, el)
209 }
210 }
211 return
212}
213
214type eventLogs []*eventLog
215
216// Free calls unref on each element of the list.
217func (els eventLogs) Free() {
218 for _, el := range els {
219 el.unref()
220 }
221}
222
223// eventLogs may be sorted in reverse chronological order.
224func (els eventLogs) Len() int { return len(els) }
225func (els eventLogs) Less(i, j int) bool { return els[i].Start.After(els[j].Start) }
226func (els eventLogs) Swap(i, j int) { els[i], els[j] = els[j], els[i] }
227
228// A logEntry is a timestamped log entry in an event log.
229type logEntry struct {
230 When time.Time
231 Elapsed time.Duration // since previous event in log
232 NewDay bool // whether this event is on a different day to the previous event
233 What string
234 IsErr bool
235}
236
237// WhenString returns a string representation of the elapsed time of the event.
238// It will include the date if midnight was crossed.
239func (e logEntry) WhenString() string {
240 if e.NewDay {
241 return e.When.Format("2006/01/02 15:04:05.000000")
242 }
243 return e.When.Format("15:04:05.000000")
244}
245
246// An eventLog represents an active event log.
247type eventLog struct {
248 // Family is the top-level grouping of event logs to which this belongs.
249 Family string
250
251 // Title is the title of this event log.
252 Title string
253
254 // Timing information.
255 Start time.Time
256
257 // Call stack where this event log was created.
258 stack []uintptr
259
260 // Append-only sequence of events.
261 //
262 // TODO(sameer): change this to a ring buffer to avoid the array copy
263 // when we hit maxEventsPerLog.
264 mu sync.RWMutex
265 events []logEntry
266 LastErrorTime time.Time
267 discarded int
268
269 refs int32 // how many buckets this is in
270}
271
272func (el *eventLog) reset() {
273 // Clear all but the mutex. Mutexes may not be copied, even when unlocked.
274 el.Family = ""
275 el.Title = ""
276 el.Start = time.Time{}
277 el.stack = nil
278 el.events = nil
279 el.LastErrorTime = time.Time{}
280 el.discarded = 0
281 el.refs = 0
282}
283
284func (el *eventLog) hasRecentError(now time.Time, maxErrAge time.Duration) bool {
285 if maxErrAge == 0 {
286 return true
287 }
288 el.mu.RLock()
289 defer el.mu.RUnlock()
290 return now.Sub(el.LastErrorTime) < maxErrAge
291}
292
293// delta returns the elapsed time since the last event or the log start,
294// and whether it spans midnight.
295// L >= el.mu
296func (el *eventLog) delta(t time.Time) (time.Duration, bool) {
297 if len(el.events) == 0 {
298 return t.Sub(el.Start), false
299 }
300 prev := el.events[len(el.events)-1].When
301 return t.Sub(prev), prev.Day() != t.Day()
302
303}
304
305func (el *eventLog) Printf(format string, a ...interface{}) {
306 el.printf(false, format, a...)
307}
308
309func (el *eventLog) Errorf(format string, a ...interface{}) {
310 el.printf(true, format, a...)
311}
312
313func (el *eventLog) printf(isErr bool, format string, a ...interface{}) {
314 e := logEntry{When: time.Now(), IsErr: isErr, What: fmt.Sprintf(format, a...)}
315 el.mu.Lock()
316 e.Elapsed, e.NewDay = el.delta(e.When)
317 if len(el.events) < maxEventsPerLog {
318 el.events = append(el.events, e)
319 } else {
320 // Discard the oldest event.
321 if el.discarded == 0 {
322 // el.discarded starts at two to count for the event it
323 // is replacing, plus the next one that we are about to
324 // drop.
325 el.discarded = 2
326 } else {
327 el.discarded++
328 }
329 // TODO(sameer): if this causes allocations on a critical path,
330 // change eventLog.What to be a fmt.Stringer, as in trace.go.
331 el.events[0].What = fmt.Sprintf("(%d events discarded)", el.discarded)
332 // The timestamp of the discarded meta-event should be
333 // the time of the last event it is representing.
334 el.events[0].When = el.events[1].When
335 copy(el.events[1:], el.events[2:])
336 el.events[maxEventsPerLog-1] = e
337 }
338 if e.IsErr {
339 el.LastErrorTime = e.When
340 }
341 el.mu.Unlock()
342}
343
344func (el *eventLog) ref() {
345 atomic.AddInt32(&el.refs, 1)
346}
347
348func (el *eventLog) unref() {
349 if atomic.AddInt32(&el.refs, -1) == 0 {
350 freeEventLog(el)
351 }
352}
353
354func (el *eventLog) When() string {
355 return el.Start.Format("2006/01/02 15:04:05.000000")
356}
357
358func (el *eventLog) ElapsedTime() string {
359 elapsed := time.Since(el.Start)
360 return fmt.Sprintf("%.6f", elapsed.Seconds())
361}
362
363func (el *eventLog) Stack() string {
364 buf := new(bytes.Buffer)
365 tw := tabwriter.NewWriter(buf, 1, 8, 1, '\t', 0)
366 printStackRecord(tw, el.stack)
367 tw.Flush()
368 return buf.String()
369}
370
371// printStackRecord prints the function + source line information
372// for a single stack trace.
373// Adapted from runtime/pprof/pprof.go.
374func printStackRecord(w io.Writer, stk []uintptr) {
375 for _, pc := range stk {
376 f := runtime.FuncForPC(pc)
377 if f == nil {
378 continue
379 }
380 file, line := f.FileLine(pc)
381 name := f.Name()
382 // Hide runtime.goexit and any runtime functions at the beginning.
383 if strings.HasPrefix(name, "runtime.") {
384 continue
385 }
386 fmt.Fprintf(w, "# %s\t%s:%d\n", name, file, line)
387 }
388}
389
390func (el *eventLog) Events() []logEntry {
391 el.mu.RLock()
392 defer el.mu.RUnlock()
393 return el.events
394}
395
396// freeEventLogs is a freelist of *eventLog
397var freeEventLogs = make(chan *eventLog, 1000)
398
399// newEventLog returns a event log ready to use.
400func newEventLog() *eventLog {
401 select {
402 case el := <-freeEventLogs:
403 return el
404 default:
405 return new(eventLog)
406 }
407}
408
409// freeEventLog adds el to freeEventLogs if there's room.
410// This is non-blocking.
411func freeEventLog(el *eventLog) {
412 el.reset()
413 select {
414 case freeEventLogs <- el:
415 default:
416 }
417}
418
419var eventsTmplCache *template.Template
420var eventsTmplOnce sync.Once
421
422func eventsTmpl() *template.Template {
423 eventsTmplOnce.Do(func() {
424 eventsTmplCache = template.Must(template.New("events").Funcs(template.FuncMap{
425 "elapsed": elapsed,
426 "trimSpace": strings.TrimSpace,
427 }).Parse(eventsHTML))
428 })
429 return eventsTmplCache
430}
431
432const eventsHTML = `
433<html>
434 <head>
435 <title>events</title>
436 </head>
437 <style type="text/css">
438 body {
439 font-family: sans-serif;
440 }
441 table#req-status td.family {
442 padding-right: 2em;
443 }
444 table#req-status td.active {
445 padding-right: 1em;
446 }
447 table#req-status td.empty {
448 color: #aaa;
449 }
450 table#reqs {
451 margin-top: 1em;
452 }
453 table#reqs tr.first {
454 {{if $.Expanded}}font-weight: bold;{{end}}
455 }
456 table#reqs td {
457 font-family: monospace;
458 }
459 table#reqs td.when {
460 text-align: right;
461 white-space: nowrap;
462 }
463 table#reqs td.elapsed {
464 padding: 0 0.5em;
465 text-align: right;
466 white-space: pre;
467 width: 10em;
468 }
469 address {
470 font-size: smaller;
471 margin-top: 5em;
472 }
473 </style>
474 <body>
475
476<h1>/debug/events</h1>
477
478<table id="req-status">
479 {{range $i, $fam := .Families}}
480 <tr>
481 <td class="family">{{$fam}}</td>
482
483 {{range $j, $bucket := $.Buckets}}
484 {{$n := index $.Counts $i $j}}
485 <td class="{{if not $bucket.MaxErrAge}}active{{end}}{{if not $n}}empty{{end}}">
486 {{if $n}}<a href="?fam={{$fam}}&b={{$j}}{{if $.Expanded}}&exp=1{{end}}">{{end}}
487 [{{$n}} {{$bucket.String}}]
488 {{if $n}}</a>{{end}}
489 </td>
490 {{end}}
491
492 </tr>{{end}}
493</table>
494
495{{if $.EventLogs}}
496<hr />
497<h3>Family: {{$.Family}}</h3>
498
499{{if $.Expanded}}<a href="?fam={{$.Family}}&b={{$.Bucket}}">{{end}}
500[Summary]{{if $.Expanded}}</a>{{end}}
501
502{{if not $.Expanded}}<a href="?fam={{$.Family}}&b={{$.Bucket}}&exp=1">{{end}}
503[Expanded]{{if not $.Expanded}}</a>{{end}}
504
505<table id="reqs">
506 <tr><th>When</th><th>Elapsed</th></tr>
507 {{range $el := $.EventLogs}}
508 <tr class="first">
509 <td class="when">{{$el.When}}</td>
510 <td class="elapsed">{{$el.ElapsedTime}}</td>
511 <td>{{$el.Title}}
512 </tr>
513 {{if $.Expanded}}
514 <tr>
515 <td class="when"></td>
516 <td class="elapsed"></td>
517 <td><pre>{{$el.Stack|trimSpace}}</pre></td>
518 </tr>
519 {{range $el.Events}}
520 <tr>
521 <td class="when">{{.WhenString}}</td>
522 <td class="elapsed">{{elapsed .Elapsed}}</td>
523 <td>.{{if .IsErr}}E{{else}}.{{end}}. {{.What}}</td>
524 </tr>
525 {{end}}
526 {{end}}
527 {{end}}
528</table>
529{{end}}
530 </body>
531</html>
532`
diff --git a/vendor/golang.org/x/net/trace/histogram.go b/vendor/golang.org/x/net/trace/histogram.go
new file mode 100644
index 0000000..9bf4286
--- /dev/null
+++ b/vendor/golang.org/x/net/trace/histogram.go
@@ -0,0 +1,365 @@
1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package trace
6
7// This file implements histogramming for RPC statistics collection.
8
9import (
10 "bytes"
11 "fmt"
12 "html/template"
13 "log"
14 "math"
15 "sync"
16
17 "golang.org/x/net/internal/timeseries"
18)
19
20const (
21 bucketCount = 38
22)
23
24// histogram keeps counts of values in buckets that are spaced
25// out in powers of 2: 0-1, 2-3, 4-7...
26// histogram implements timeseries.Observable
27type histogram struct {
28 sum int64 // running total of measurements
29 sumOfSquares float64 // square of running total
30 buckets []int64 // bucketed values for histogram
31 value int // holds a single value as an optimization
32 valueCount int64 // number of values recorded for single value
33}
34
35// AddMeasurement records a value measurement observation to the histogram.
36func (h *histogram) addMeasurement(value int64) {
37 // TODO: assert invariant
38 h.sum += value
39 h.sumOfSquares += float64(value) * float64(value)
40
41 bucketIndex := getBucket(value)
42
43 if h.valueCount == 0 || (h.valueCount > 0 && h.value == bucketIndex) {
44 h.value = bucketIndex
45 h.valueCount++
46 } else {
47 h.allocateBuckets()
48 h.buckets[bucketIndex]++
49 }
50}
51
52func (h *histogram) allocateBuckets() {
53 if h.buckets == nil {
54 h.buckets = make([]int64, bucketCount)
55 h.buckets[h.value] = h.valueCount
56 h.value = 0
57 h.valueCount = -1
58 }
59}
60
61func log2(i int64) int {
62 n := 0
63 for ; i >= 0x100; i >>= 8 {
64 n += 8
65 }
66 for ; i > 0; i >>= 1 {
67 n += 1
68 }
69 return n
70}
71
72func getBucket(i int64) (index int) {
73 index = log2(i) - 1
74 if index < 0 {
75 index = 0
76 }
77 if index >= bucketCount {
78 index = bucketCount - 1
79 }
80 return
81}
82
83// Total returns the number of recorded observations.
84func (h *histogram) total() (total int64) {
85 if h.valueCount >= 0 {
86 total = h.valueCount
87 }
88 for _, val := range h.buckets {
89 total += int64(val)
90 }
91 return
92}
93
94// Average returns the average value of recorded observations.
95func (h *histogram) average() float64 {
96 t := h.total()
97 if t == 0 {
98 return 0
99 }
100 return float64(h.sum) / float64(t)
101}
102
103// Variance returns the variance of recorded observations.
104func (h *histogram) variance() float64 {
105 t := float64(h.total())
106 if t == 0 {
107 return 0
108 }
109 s := float64(h.sum) / t
110 return h.sumOfSquares/t - s*s
111}
112
113// StandardDeviation returns the standard deviation of recorded observations.
114func (h *histogram) standardDeviation() float64 {
115 return math.Sqrt(h.variance())
116}
117
118// PercentileBoundary estimates the value that the given fraction of recorded
119// observations are less than.
120func (h *histogram) percentileBoundary(percentile float64) int64 {
121 total := h.total()
122
123 // Corner cases (make sure result is strictly less than Total())
124 if total == 0 {
125 return 0
126 } else if total == 1 {
127 return int64(h.average())
128 }
129
130 percentOfTotal := round(float64(total) * percentile)
131 var runningTotal int64
132
133 for i := range h.buckets {
134 value := h.buckets[i]
135 runningTotal += value
136 if runningTotal == percentOfTotal {
137 // We hit an exact bucket boundary. If the next bucket has data, it is a
138 // good estimate of the value. If the bucket is empty, we interpolate the
139 // midpoint between the next bucket's boundary and the next non-zero
140 // bucket. If the remaining buckets are all empty, then we use the
141 // boundary for the next bucket as the estimate.
142 j := uint8(i + 1)
143 min := bucketBoundary(j)
144 if runningTotal < total {
145 for h.buckets[j] == 0 {
146 j++
147 }
148 }
149 max := bucketBoundary(j)
150 return min + round(float64(max-min)/2)
151 } else if runningTotal > percentOfTotal {
152 // The value is in this bucket. Interpolate the value.
153 delta := runningTotal - percentOfTotal
154 percentBucket := float64(value-delta) / float64(value)
155 bucketMin := bucketBoundary(uint8(i))
156 nextBucketMin := bucketBoundary(uint8(i + 1))
157 bucketSize := nextBucketMin - bucketMin
158 return bucketMin + round(percentBucket*float64(bucketSize))
159 }
160 }
161 return bucketBoundary(bucketCount - 1)
162}
163
164// Median returns the estimated median of the observed values.
165func (h *histogram) median() int64 {
166 return h.percentileBoundary(0.5)
167}
168
169// Add adds other to h.
170func (h *histogram) Add(other timeseries.Observable) {
171 o := other.(*histogram)
172 if o.valueCount == 0 {
173 // Other histogram is empty
174 } else if h.valueCount >= 0 && o.valueCount > 0 && h.value == o.value {
175 // Both have a single bucketed value, aggregate them
176 h.valueCount += o.valueCount
177 } else {
178 // Two different values necessitate buckets in this histogram
179 h.allocateBuckets()
180 if o.valueCount >= 0 {
181 h.buckets[o.value] += o.valueCount
182 } else {
183 for i := range h.buckets {
184 h.buckets[i] += o.buckets[i]
185 }
186 }
187 }
188 h.sumOfSquares += o.sumOfSquares
189 h.sum += o.sum
190}
191
192// Clear resets the histogram to an empty state, removing all observed values.
193func (h *histogram) Clear() {
194 h.buckets = nil
195 h.value = 0
196 h.valueCount = 0
197 h.sum = 0
198 h.sumOfSquares = 0
199}
200
201// CopyFrom copies from other, which must be a *histogram, into h.
202func (h *histogram) CopyFrom(other timeseries.Observable) {
203 o := other.(*histogram)
204 if o.valueCount == -1 {
205 h.allocateBuckets()
206 copy(h.buckets, o.buckets)
207 }
208 h.sum = o.sum
209 h.sumOfSquares = o.sumOfSquares
210 h.value = o.value
211 h.valueCount = o.valueCount
212}
213
214// Multiply scales the histogram by the specified ratio.
215func (h *histogram) Multiply(ratio float64) {
216 if h.valueCount == -1 {
217 for i := range h.buckets {
218 h.buckets[i] = int64(float64(h.buckets[i]) * ratio)
219 }
220 } else {
221 h.valueCount = int64(float64(h.valueCount) * ratio)
222 }
223 h.sum = int64(float64(h.sum) * ratio)
224 h.sumOfSquares = h.sumOfSquares * ratio
225}
226
227// New creates a new histogram.
228func (h *histogram) New() timeseries.Observable {
229 r := new(histogram)
230 r.Clear()
231 return r
232}
233
234func (h *histogram) String() string {
235 return fmt.Sprintf("%d, %f, %d, %d, %v",
236 h.sum, h.sumOfSquares, h.value, h.valueCount, h.buckets)
237}
238
239// round returns the closest int64 to the argument
240func round(in float64) int64 {
241 return int64(math.Floor(in + 0.5))
242}
243
244// bucketBoundary returns the first value in the bucket.
245func bucketBoundary(bucket uint8) int64 {
246 if bucket == 0 {
247 return 0
248 }
249 return 1 << bucket
250}
251
252// bucketData holds data about a specific bucket for use in distTmpl.
253type bucketData struct {
254 Lower, Upper int64
255 N int64
256 Pct, CumulativePct float64
257 GraphWidth int
258}
259
260// data holds data about a Distribution for use in distTmpl.
261type data struct {
262 Buckets []*bucketData
263 Count, Median int64
264 Mean, StandardDeviation float64
265}
266
267// maxHTMLBarWidth is the maximum width of the HTML bar for visualizing buckets.
268const maxHTMLBarWidth = 350.0
269
270// newData returns data representing h for use in distTmpl.
271func (h *histogram) newData() *data {
272 // Force the allocation of buckets to simplify the rendering implementation
273 h.allocateBuckets()
274 // We scale the bars on the right so that the largest bar is
275 // maxHTMLBarWidth pixels in width.
276 maxBucket := int64(0)
277 for _, n := range h.buckets {
278 if n > maxBucket {
279 maxBucket = n
280 }
281 }
282 total := h.total()
283 barsizeMult := maxHTMLBarWidth / float64(maxBucket)
284 var pctMult float64
285 if total == 0 {
286 pctMult = 1.0
287 } else {
288 pctMult = 100.0 / float64(total)
289 }
290
291 buckets := make([]*bucketData, len(h.buckets))
292 runningTotal := int64(0)
293 for i, n := range h.buckets {
294 if n == 0 {
295 continue
296 }
297 runningTotal += n
298 var upperBound int64
299 if i < bucketCount-1 {
300 upperBound = bucketBoundary(uint8(i + 1))
301 } else {
302 upperBound = math.MaxInt64
303 }
304 buckets[i] = &bucketData{
305 Lower: bucketBoundary(uint8(i)),
306 Upper: upperBound,
307 N: n,
308 Pct: float64(n) * pctMult,
309 CumulativePct: float64(runningTotal) * pctMult,
310 GraphWidth: int(float64(n) * barsizeMult),
311 }
312 }
313 return &data{
314 Buckets: buckets,
315 Count: total,
316 Median: h.median(),
317 Mean: h.average(),
318 StandardDeviation: h.standardDeviation(),
319 }
320}
321
322func (h *histogram) html() template.HTML {
323 buf := new(bytes.Buffer)
324 if err := distTmpl().Execute(buf, h.newData()); err != nil {
325 buf.Reset()
326 log.Printf("net/trace: couldn't execute template: %v", err)
327 }
328 return template.HTML(buf.String())
329}
330
331var distTmplCache *template.Template
332var distTmplOnce sync.Once
333
334func distTmpl() *template.Template {
335 distTmplOnce.Do(func() {
336 // Input: data
337 distTmplCache = template.Must(template.New("distTmpl").Parse(`
338<table>
339<tr>
340 <td style="padding:0.25em">Count: {{.Count}}</td>
341 <td style="padding:0.25em">Mean: {{printf "%.0f" .Mean}}</td>
342 <td style="padding:0.25em">StdDev: {{printf "%.0f" .StandardDeviation}}</td>
343 <td style="padding:0.25em">Median: {{.Median}}</td>
344</tr>
345</table>
346<hr>
347<table>
348{{range $b := .Buckets}}
349{{if $b}}
350 <tr>
351 <td style="padding:0 0 0 0.25em">[</td>
352 <td style="text-align:right;padding:0 0.25em">{{.Lower}},</td>
353 <td style="text-align:right;padding:0 0.25em">{{.Upper}})</td>
354 <td style="text-align:right;padding:0 0.25em">{{.N}}</td>
355 <td style="text-align:right;padding:0 0.25em">{{printf "%#.3f" .Pct}}%</td>
356 <td style="text-align:right;padding:0 0.25em">{{printf "%#.3f" .CumulativePct}}%</td>
357 <td><div style="background-color: blue; height: 1em; width: {{.GraphWidth}};"></div></td>
358 </tr>
359{{end}}
360{{end}}
361</table>
362`))
363 })
364 return distTmplCache
365}
diff --git a/vendor/golang.org/x/net/trace/trace.go b/vendor/golang.org/x/net/trace/trace.go
new file mode 100644
index 0000000..3ebf6f2
--- /dev/null
+++ b/vendor/golang.org/x/net/trace/trace.go
@@ -0,0 +1,1130 @@
1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5/*
6Package trace implements tracing of requests and long-lived objects.
7It exports HTTP interfaces on /debug/requests and /debug/events.
8
9A trace.Trace provides tracing for short-lived objects, usually requests.
10A request handler might be implemented like this:
11
12 func fooHandler(w http.ResponseWriter, req *http.Request) {
13 tr := trace.New("mypkg.Foo", req.URL.Path)
14 defer tr.Finish()
15 ...
16 tr.LazyPrintf("some event %q happened", str)
17 ...
18 if err := somethingImportant(); err != nil {
19 tr.LazyPrintf("somethingImportant failed: %v", err)
20 tr.SetError()
21 }
22 }
23
24The /debug/requests HTTP endpoint organizes the traces by family,
25errors, and duration. It also provides histogram of request duration
26for each family.
27
28A trace.EventLog provides tracing for long-lived objects, such as RPC
29connections.
30
31 // A Fetcher fetches URL paths for a single domain.
32 type Fetcher struct {
33 domain string
34 events trace.EventLog
35 }
36
37 func NewFetcher(domain string) *Fetcher {
38 return &Fetcher{
39 domain,
40 trace.NewEventLog("mypkg.Fetcher", domain),
41 }
42 }
43
44 func (f *Fetcher) Fetch(path string) (string, error) {
45 resp, err := http.Get("http://" + f.domain + "/" + path)
46 if err != nil {
47 f.events.Errorf("Get(%q) = %v", path, err)
48 return "", err
49 }
50 f.events.Printf("Get(%q) = %s", path, resp.Status)
51 ...
52 }
53
54 func (f *Fetcher) Close() error {
55 f.events.Finish()
56 return nil
57 }
58
59The /debug/events HTTP endpoint organizes the event logs by family and
60by time since the last error. The expanded view displays recent log
61entries and the log's call stack.
62*/
63package trace // import "golang.org/x/net/trace"
64
65import (
66 "bytes"
67 "context"
68 "fmt"
69 "html/template"
70 "io"
71 "log"
72 "net"
73 "net/http"
74 "net/url"
75 "runtime"
76 "sort"
77 "strconv"
78 "sync"
79 "sync/atomic"
80 "time"
81
82 "golang.org/x/net/internal/timeseries"
83)
84
85// DebugUseAfterFinish controls whether to debug uses of Trace values after finishing.
86// FOR DEBUGGING ONLY. This will slow down the program.
87var DebugUseAfterFinish = false
88
89// HTTP ServeMux paths.
90const (
91 debugRequestsPath = "/debug/requests"
92 debugEventsPath = "/debug/events"
93)
94
95// AuthRequest determines whether a specific request is permitted to load the
96// /debug/requests or /debug/events pages.
97//
98// It returns two bools; the first indicates whether the page may be viewed at all,
99// and the second indicates whether sensitive events will be shown.
100//
101// AuthRequest may be replaced by a program to customize its authorization requirements.
102//
103// The default AuthRequest function returns (true, true) if and only if the request
104// comes from localhost/127.0.0.1/[::1].
105var AuthRequest = func(req *http.Request) (any, sensitive bool) {
106 // RemoteAddr is commonly in the form "IP" or "IP:port".
107 // If it is in the form "IP:port", split off the port.
108 host, _, err := net.SplitHostPort(req.RemoteAddr)
109 if err != nil {
110 host = req.RemoteAddr
111 }
112 switch host {
113 case "localhost", "127.0.0.1", "::1":
114 return true, true
115 default:
116 return false, false
117 }
118}
119
120func init() {
121 _, pat := http.DefaultServeMux.Handler(&http.Request{URL: &url.URL{Path: debugRequestsPath}})
122 if pat == debugRequestsPath {
123 panic("/debug/requests is already registered. You may have two independent copies of " +
124 "golang.org/x/net/trace in your binary, trying to maintain separate state. This may " +
125 "involve a vendored copy of golang.org/x/net/trace.")
126 }
127
128 // TODO(jbd): Serve Traces from /debug/traces in the future?
129 // There is no requirement for a request to be present to have traces.
130 http.HandleFunc(debugRequestsPath, Traces)
131 http.HandleFunc(debugEventsPath, Events)
132}
133
134// NewContext returns a copy of the parent context
135// and associates it with a Trace.
136func NewContext(ctx context.Context, tr Trace) context.Context {
137 return context.WithValue(ctx, contextKey, tr)
138}
139
140// FromContext returns the Trace bound to the context, if any.
141func FromContext(ctx context.Context) (tr Trace, ok bool) {
142 tr, ok = ctx.Value(contextKey).(Trace)
143 return
144}
145
146// Traces responds with traces from the program.
147// The package initialization registers it in http.DefaultServeMux
148// at /debug/requests.
149//
150// It performs authorization by running AuthRequest.
151func Traces(w http.ResponseWriter, req *http.Request) {
152 any, sensitive := AuthRequest(req)
153 if !any {
154 http.Error(w, "not allowed", http.StatusUnauthorized)
155 return
156 }
157 w.Header().Set("Content-Type", "text/html; charset=utf-8")
158 Render(w, req, sensitive)
159}
160
161// Events responds with a page of events collected by EventLogs.
162// The package initialization registers it in http.DefaultServeMux
163// at /debug/events.
164//
165// It performs authorization by running AuthRequest.
166func Events(w http.ResponseWriter, req *http.Request) {
167 any, sensitive := AuthRequest(req)
168 if !any {
169 http.Error(w, "not allowed", http.StatusUnauthorized)
170 return
171 }
172 w.Header().Set("Content-Type", "text/html; charset=utf-8")
173 RenderEvents(w, req, sensitive)
174}
175
176// Render renders the HTML page typically served at /debug/requests.
177// It does not do any auth checking. The request may be nil.
178//
179// Most users will use the Traces handler.
180func Render(w io.Writer, req *http.Request, sensitive bool) {
181 data := &struct {
182 Families []string
183 ActiveTraceCount map[string]int
184 CompletedTraces map[string]*family
185
186 // Set when a bucket has been selected.
187 Traces traceList
188 Family string
189 Bucket int
190 Expanded bool
191 Traced bool
192 Active bool
193 ShowSensitive bool // whether to show sensitive events
194
195 Histogram template.HTML
196 HistogramWindow string // e.g. "last minute", "last hour", "all time"
197
198 // If non-zero, the set of traces is a partial set,
199 // and this is the total number.
200 Total int
201 }{
202 CompletedTraces: completedTraces,
203 }
204
205 data.ShowSensitive = sensitive
206 if req != nil {
207 // Allow show_sensitive=0 to force hiding of sensitive data for testing.
208 // This only goes one way; you can't use show_sensitive=1 to see things.
209 if req.FormValue("show_sensitive") == "0" {
210 data.ShowSensitive = false
211 }
212
213 if exp, err := strconv.ParseBool(req.FormValue("exp")); err == nil {
214 data.Expanded = exp
215 }
216 if exp, err := strconv.ParseBool(req.FormValue("rtraced")); err == nil {
217 data.Traced = exp
218 }
219 }
220
221 completedMu.RLock()
222 data.Families = make([]string, 0, len(completedTraces))
223 for fam := range completedTraces {
224 data.Families = append(data.Families, fam)
225 }
226 completedMu.RUnlock()
227 sort.Strings(data.Families)
228
229 // We are careful here to minimize the time spent locking activeMu,
230 // since that lock is required every time an RPC starts and finishes.
231 data.ActiveTraceCount = make(map[string]int, len(data.Families))
232 activeMu.RLock()
233 for fam, s := range activeTraces {
234 data.ActiveTraceCount[fam] = s.Len()
235 }
236 activeMu.RUnlock()
237
238 var ok bool
239 data.Family, data.Bucket, ok = parseArgs(req)
240 switch {
241 case !ok:
242 // No-op
243 case data.Bucket == -1:
244 data.Active = true
245 n := data.ActiveTraceCount[data.Family]
246 data.Traces = getActiveTraces(data.Family)
247 if len(data.Traces) < n {
248 data.Total = n
249 }
250 case data.Bucket < bucketsPerFamily:
251 if b := lookupBucket(data.Family, data.Bucket); b != nil {
252 data.Traces = b.Copy(data.Traced)
253 }
254 default:
255 if f := getFamily(data.Family, false); f != nil {
256 var obs timeseries.Observable
257 f.LatencyMu.RLock()
258 switch o := data.Bucket - bucketsPerFamily; o {
259 case 0:
260 obs = f.Latency.Minute()
261 data.HistogramWindow = "last minute"
262 case 1:
263 obs = f.Latency.Hour()
264 data.HistogramWindow = "last hour"
265 case 2:
266 obs = f.Latency.Total()
267 data.HistogramWindow = "all time"
268 }
269 f.LatencyMu.RUnlock()
270 if obs != nil {
271 data.Histogram = obs.(*histogram).html()
272 }
273 }
274 }
275
276 if data.Traces != nil {
277 defer data.Traces.Free()
278 sort.Sort(data.Traces)
279 }
280
281 completedMu.RLock()
282 defer completedMu.RUnlock()
283 if err := pageTmpl().ExecuteTemplate(w, "Page", data); err != nil {
284 log.Printf("net/trace: Failed executing template: %v", err)
285 }
286}
287
288func parseArgs(req *http.Request) (fam string, b int, ok bool) {
289 if req == nil {
290 return "", 0, false
291 }
292 fam, bStr := req.FormValue("fam"), req.FormValue("b")
293 if fam == "" || bStr == "" {
294 return "", 0, false
295 }
296 b, err := strconv.Atoi(bStr)
297 if err != nil || b < -1 {
298 return "", 0, false
299 }
300
301 return fam, b, true
302}
303
304func lookupBucket(fam string, b int) *traceBucket {
305 f := getFamily(fam, false)
306 if f == nil || b < 0 || b >= len(f.Buckets) {
307 return nil
308 }
309 return f.Buckets[b]
310}
311
312type contextKeyT string
313
314var contextKey = contextKeyT("golang.org/x/net/trace.Trace")
315
316// Trace represents an active request.
317type Trace interface {
318 // LazyLog adds x to the event log. It will be evaluated each time the
319 // /debug/requests page is rendered. Any memory referenced by x will be
320 // pinned until the trace is finished and later discarded.
321 LazyLog(x fmt.Stringer, sensitive bool)
322
323 // LazyPrintf evaluates its arguments with fmt.Sprintf each time the
324 // /debug/requests page is rendered. Any memory referenced by a will be
325 // pinned until the trace is finished and later discarded.
326 LazyPrintf(format string, a ...interface{})
327
328 // SetError declares that this trace resulted in an error.
329 SetError()
330
331 // SetRecycler sets a recycler for the trace.
332 // f will be called for each event passed to LazyLog at a time when
333 // it is no longer required, whether while the trace is still active
334 // and the event is discarded, or when a completed trace is discarded.
335 SetRecycler(f func(interface{}))
336
337 // SetTraceInfo sets the trace info for the trace.
338 // This is currently unused.
339 SetTraceInfo(traceID, spanID uint64)
340
341 // SetMaxEvents sets the maximum number of events that will be stored
342 // in the trace. This has no effect if any events have already been
343 // added to the trace.
344 SetMaxEvents(m int)
345
346 // Finish declares that this trace is complete.
347 // The trace should not be used after calling this method.
348 Finish()
349}
350
351type lazySprintf struct {
352 format string
353 a []interface{}
354}
355
356func (l *lazySprintf) String() string {
357 return fmt.Sprintf(l.format, l.a...)
358}
359
360// New returns a new Trace with the specified family and title.
361func New(family, title string) Trace {
362 tr := newTrace()
363 tr.ref()
364 tr.Family, tr.Title = family, title
365 tr.Start = time.Now()
366 tr.maxEvents = maxEventsPerTrace
367 tr.events = tr.eventsBuf[:0]
368
369 activeMu.RLock()
370 s := activeTraces[tr.Family]
371 activeMu.RUnlock()
372 if s == nil {
373 activeMu.Lock()
374 s = activeTraces[tr.Family] // check again
375 if s == nil {
376 s = new(traceSet)
377 activeTraces[tr.Family] = s
378 }
379 activeMu.Unlock()
380 }
381 s.Add(tr)
382
383 // Trigger allocation of the completed trace structure for this family.
384 // This will cause the family to be present in the request page during
385 // the first trace of this family. We don't care about the return value,
386 // nor is there any need for this to run inline, so we execute it in its
387 // own goroutine, but only if the family isn't allocated yet.
388 completedMu.RLock()
389 if _, ok := completedTraces[tr.Family]; !ok {
390 go allocFamily(tr.Family)
391 }
392 completedMu.RUnlock()
393
394 return tr
395}
396
397func (tr *trace) Finish() {
398 elapsed := time.Now().Sub(tr.Start)
399 tr.mu.Lock()
400 tr.Elapsed = elapsed
401 tr.mu.Unlock()
402
403 if DebugUseAfterFinish {
404 buf := make([]byte, 4<<10) // 4 KB should be enough
405 n := runtime.Stack(buf, false)
406 tr.finishStack = buf[:n]
407 }
408
409 activeMu.RLock()
410 m := activeTraces[tr.Family]
411 activeMu.RUnlock()
412 m.Remove(tr)
413
414 f := getFamily(tr.Family, true)
415 tr.mu.RLock() // protects tr fields in Cond.match calls
416 for _, b := range f.Buckets {
417 if b.Cond.match(tr) {
418 b.Add(tr)
419 }
420 }
421 tr.mu.RUnlock()
422
423 // Add a sample of elapsed time as microseconds to the family's timeseries
424 h := new(histogram)
425 h.addMeasurement(elapsed.Nanoseconds() / 1e3)
426 f.LatencyMu.Lock()
427 f.Latency.Add(h)
428 f.LatencyMu.Unlock()
429
430 tr.unref() // matches ref in New
431}
432
433const (
434 bucketsPerFamily = 9
435 tracesPerBucket = 10
436 maxActiveTraces = 20 // Maximum number of active traces to show.
437 maxEventsPerTrace = 10
438 numHistogramBuckets = 38
439)
440
441var (
442 // The active traces.
443 activeMu sync.RWMutex
444 activeTraces = make(map[string]*traceSet) // family -> traces
445
446 // Families of completed traces.
447 completedMu sync.RWMutex
448 completedTraces = make(map[string]*family) // family -> traces
449)
450
451type traceSet struct {
452 mu sync.RWMutex
453 m map[*trace]bool
454
455 // We could avoid the entire map scan in FirstN by having a slice of all the traces
456 // ordered by start time, and an index into that from the trace struct, with a periodic
457 // repack of the slice after enough traces finish; we could also use a skip list or similar.
458 // However, that would shift some of the expense from /debug/requests time to RPC time,
459 // which is probably the wrong trade-off.
460}
461
462func (ts *traceSet) Len() int {
463 ts.mu.RLock()
464 defer ts.mu.RUnlock()
465 return len(ts.m)
466}
467
468func (ts *traceSet) Add(tr *trace) {
469 ts.mu.Lock()
470 if ts.m == nil {
471 ts.m = make(map[*trace]bool)
472 }
473 ts.m[tr] = true
474 ts.mu.Unlock()
475}
476
477func (ts *traceSet) Remove(tr *trace) {
478 ts.mu.Lock()
479 delete(ts.m, tr)
480 ts.mu.Unlock()
481}
482
483// FirstN returns the first n traces ordered by time.
484func (ts *traceSet) FirstN(n int) traceList {
485 ts.mu.RLock()
486 defer ts.mu.RUnlock()
487
488 if n > len(ts.m) {
489 n = len(ts.m)
490 }
491 trl := make(traceList, 0, n)
492
493 // Fast path for when no selectivity is needed.
494 if n == len(ts.m) {
495 for tr := range ts.m {
496 tr.ref()
497 trl = append(trl, tr)
498 }
499 sort.Sort(trl)
500 return trl
501 }
502
503 // Pick the oldest n traces.
504 // This is inefficient. See the comment in the traceSet struct.
505 for tr := range ts.m {
506 // Put the first n traces into trl in the order they occur.
507 // When we have n, sort trl, and thereafter maintain its order.
508 if len(trl) < n {
509 tr.ref()
510 trl = append(trl, tr)
511 if len(trl) == n {
512 // This is guaranteed to happen exactly once during this loop.
513 sort.Sort(trl)
514 }
515 continue
516 }
517 if tr.Start.After(trl[n-1].Start) {
518 continue
519 }
520
521 // Find where to insert this one.
522 tr.ref()
523 i := sort.Search(n, func(i int) bool { return trl[i].Start.After(tr.Start) })
524 trl[n-1].unref()
525 copy(trl[i+1:], trl[i:])
526 trl[i] = tr
527 }
528
529 return trl
530}
531
532func getActiveTraces(fam string) traceList {
533 activeMu.RLock()
534 s := activeTraces[fam]
535 activeMu.RUnlock()
536 if s == nil {
537 return nil
538 }
539 return s.FirstN(maxActiveTraces)
540}
541
542func getFamily(fam string, allocNew bool) *family {
543 completedMu.RLock()
544 f := completedTraces[fam]
545 completedMu.RUnlock()
546 if f == nil && allocNew {
547 f = allocFamily(fam)
548 }
549 return f
550}
551
552func allocFamily(fam string) *family {
553 completedMu.Lock()
554 defer completedMu.Unlock()
555 f := completedTraces[fam]
556 if f == nil {
557 f = newFamily()
558 completedTraces[fam] = f
559 }
560 return f
561}
562
563// family represents a set of trace buckets and associated latency information.
564type family struct {
565 // traces may occur in multiple buckets.
566 Buckets [bucketsPerFamily]*traceBucket
567
568 // latency time series
569 LatencyMu sync.RWMutex
570 Latency *timeseries.MinuteHourSeries
571}
572
573func newFamily() *family {
574 return &family{
575 Buckets: [bucketsPerFamily]*traceBucket{
576 {Cond: minCond(0)},
577 {Cond: minCond(50 * time.Millisecond)},
578 {Cond: minCond(100 * time.Millisecond)},
579 {Cond: minCond(200 * time.Millisecond)},
580 {Cond: minCond(500 * time.Millisecond)},
581 {Cond: minCond(1 * time.Second)},
582 {Cond: minCond(10 * time.Second)},
583 {Cond: minCond(100 * time.Second)},
584 {Cond: errorCond{}},
585 },
586 Latency: timeseries.NewMinuteHourSeries(func() timeseries.Observable { return new(histogram) }),
587 }
588}
589
590// traceBucket represents a size-capped bucket of historic traces,
591// along with a condition for a trace to belong to the bucket.
592type traceBucket struct {
593 Cond cond
594
595 // Ring buffer implementation of a fixed-size FIFO queue.
596 mu sync.RWMutex
597 buf [tracesPerBucket]*trace
598 start int // < tracesPerBucket
599 length int // <= tracesPerBucket
600}
601
602func (b *traceBucket) Add(tr *trace) {
603 b.mu.Lock()
604 defer b.mu.Unlock()
605
606 i := b.start + b.length
607 if i >= tracesPerBucket {
608 i -= tracesPerBucket
609 }
610 if b.length == tracesPerBucket {
611 // "Remove" an element from the bucket.
612 b.buf[i].unref()
613 b.start++
614 if b.start == tracesPerBucket {
615 b.start = 0
616 }
617 }
618 b.buf[i] = tr
619 if b.length < tracesPerBucket {
620 b.length++
621 }
622 tr.ref()
623}
624
625// Copy returns a copy of the traces in the bucket.
626// If tracedOnly is true, only the traces with trace information will be returned.
627// The logs will be ref'd before returning; the caller should call
628// the Free method when it is done with them.
629// TODO(dsymonds): keep track of traced requests in separate buckets.
630func (b *traceBucket) Copy(tracedOnly bool) traceList {
631 b.mu.RLock()
632 defer b.mu.RUnlock()
633
634 trl := make(traceList, 0, b.length)
635 for i, x := 0, b.start; i < b.length; i++ {
636 tr := b.buf[x]
637 if !tracedOnly || tr.spanID != 0 {
638 tr.ref()
639 trl = append(trl, tr)
640 }
641 x++
642 if x == b.length {
643 x = 0
644 }
645 }
646 return trl
647}
648
649func (b *traceBucket) Empty() bool {
650 b.mu.RLock()
651 defer b.mu.RUnlock()
652 return b.length == 0
653}
654
655// cond represents a condition on a trace.
656type cond interface {
657 match(t *trace) bool
658 String() string
659}
660
661type minCond time.Duration
662
663func (m minCond) match(t *trace) bool { return t.Elapsed >= time.Duration(m) }
664func (m minCond) String() string { return fmt.Sprintf("≥%gs", time.Duration(m).Seconds()) }
665
666type errorCond struct{}
667
668func (e errorCond) match(t *trace) bool { return t.IsError }
669func (e errorCond) String() string { return "errors" }
670
671type traceList []*trace
672
673// Free calls unref on each element of the list.
674func (trl traceList) Free() {
675 for _, t := range trl {
676 t.unref()
677 }
678}
679
680// traceList may be sorted in reverse chronological order.
681func (trl traceList) Len() int { return len(trl) }
682func (trl traceList) Less(i, j int) bool { return trl[i].Start.After(trl[j].Start) }
683func (trl traceList) Swap(i, j int) { trl[i], trl[j] = trl[j], trl[i] }
684
685// An event is a timestamped log entry in a trace.
686type event struct {
687 When time.Time
688 Elapsed time.Duration // since previous event in trace
689 NewDay bool // whether this event is on a different day to the previous event
690 Recyclable bool // whether this event was passed via LazyLog
691 Sensitive bool // whether this event contains sensitive information
692 What interface{} // string or fmt.Stringer
693}
694
695// WhenString returns a string representation of the elapsed time of the event.
696// It will include the date if midnight was crossed.
697func (e event) WhenString() string {
698 if e.NewDay {
699 return e.When.Format("2006/01/02 15:04:05.000000")
700 }
701 return e.When.Format("15:04:05.000000")
702}
703
704// discarded represents a number of discarded events.
705// It is stored as *discarded to make it easier to update in-place.
706type discarded int
707
708func (d *discarded) String() string {
709 return fmt.Sprintf("(%d events discarded)", int(*d))
710}
711
712// trace represents an active or complete request,
713// either sent or received by this program.
714type trace struct {
715 // Family is the top-level grouping of traces to which this belongs.
716 Family string
717
718 // Title is the title of this trace.
719 Title string
720
721 // Start time of the this trace.
722 Start time.Time
723
724 mu sync.RWMutex
725 events []event // Append-only sequence of events (modulo discards).
726 maxEvents int
727 recycler func(interface{})
728 IsError bool // Whether this trace resulted in an error.
729 Elapsed time.Duration // Elapsed time for this trace, zero while active.
730 traceID uint64 // Trace information if non-zero.
731 spanID uint64
732
733 refs int32 // how many buckets this is in
734 disc discarded // scratch space to avoid allocation
735
736 finishStack []byte // where finish was called, if DebugUseAfterFinish is set
737
738 eventsBuf [4]event // preallocated buffer in case we only log a few events
739}
740
741func (tr *trace) reset() {
742 // Clear all but the mutex. Mutexes may not be copied, even when unlocked.
743 tr.Family = ""
744 tr.Title = ""
745 tr.Start = time.Time{}
746
747 tr.mu.Lock()
748 tr.Elapsed = 0
749 tr.traceID = 0
750 tr.spanID = 0
751 tr.IsError = false
752 tr.maxEvents = 0
753 tr.events = nil
754 tr.recycler = nil
755 tr.mu.Unlock()
756
757 tr.refs = 0
758 tr.disc = 0
759 tr.finishStack = nil
760 for i := range tr.eventsBuf {
761 tr.eventsBuf[i] = event{}
762 }
763}
764
765// delta returns the elapsed time since the last event or the trace start,
766// and whether it spans midnight.
767// L >= tr.mu
768func (tr *trace) delta(t time.Time) (time.Duration, bool) {
769 if len(tr.events) == 0 {
770 return t.Sub(tr.Start), false
771 }
772 prev := tr.events[len(tr.events)-1].When
773 return t.Sub(prev), prev.Day() != t.Day()
774}
775
776func (tr *trace) addEvent(x interface{}, recyclable, sensitive bool) {
777 if DebugUseAfterFinish && tr.finishStack != nil {
778 buf := make([]byte, 4<<10) // 4 KB should be enough
779 n := runtime.Stack(buf, false)
780 log.Printf("net/trace: trace used after finish:\nFinished at:\n%s\nUsed at:\n%s", tr.finishStack, buf[:n])
781 }
782
783 /*
784 NOTE TO DEBUGGERS
785
786 If you are here because your program panicked in this code,
787 it is almost definitely the fault of code using this package,
788 and very unlikely to be the fault of this code.
789
790 The most likely scenario is that some code elsewhere is using
791 a trace.Trace after its Finish method is called.
792 You can temporarily set the DebugUseAfterFinish var
793 to help discover where that is; do not leave that var set,
794 since it makes this package much less efficient.
795 */
796
797 e := event{When: time.Now(), What: x, Recyclable: recyclable, Sensitive: sensitive}
798 tr.mu.Lock()
799 e.Elapsed, e.NewDay = tr.delta(e.When)
800 if len(tr.events) < tr.maxEvents {
801 tr.events = append(tr.events, e)
802 } else {
803 // Discard the middle events.
804 di := int((tr.maxEvents - 1) / 2)
805 if d, ok := tr.events[di].What.(*discarded); ok {
806 (*d)++
807 } else {
808 // disc starts at two to count for the event it is replacing,
809 // plus the next one that we are about to drop.
810 tr.disc = 2
811 if tr.recycler != nil && tr.events[di].Recyclable {
812 go tr.recycler(tr.events[di].What)
813 }
814 tr.events[di].What = &tr.disc
815 }
816 // The timestamp of the discarded meta-event should be
817 // the time of the last event it is representing.
818 tr.events[di].When = tr.events[di+1].When
819
820 if tr.recycler != nil && tr.events[di+1].Recyclable {
821 go tr.recycler(tr.events[di+1].What)
822 }
823 copy(tr.events[di+1:], tr.events[di+2:])
824 tr.events[tr.maxEvents-1] = e
825 }
826 tr.mu.Unlock()
827}
828
829func (tr *trace) LazyLog(x fmt.Stringer, sensitive bool) {
830 tr.addEvent(x, true, sensitive)
831}
832
833func (tr *trace) LazyPrintf(format string, a ...interface{}) {
834 tr.addEvent(&lazySprintf{format, a}, false, false)
835}
836
837func (tr *trace) SetError() {
838 tr.mu.Lock()
839 tr.IsError = true
840 tr.mu.Unlock()
841}
842
843func (tr *trace) SetRecycler(f func(interface{})) {
844 tr.mu.Lock()
845 tr.recycler = f
846 tr.mu.Unlock()
847}
848
849func (tr *trace) SetTraceInfo(traceID, spanID uint64) {
850 tr.mu.Lock()
851 tr.traceID, tr.spanID = traceID, spanID
852 tr.mu.Unlock()
853}
854
855func (tr *trace) SetMaxEvents(m int) {
856 tr.mu.Lock()
857 // Always keep at least three events: first, discarded count, last.
858 if len(tr.events) == 0 && m > 3 {
859 tr.maxEvents = m
860 }
861 tr.mu.Unlock()
862}
863
864func (tr *trace) ref() {
865 atomic.AddInt32(&tr.refs, 1)
866}
867
868func (tr *trace) unref() {
869 if atomic.AddInt32(&tr.refs, -1) == 0 {
870 tr.mu.RLock()
871 if tr.recycler != nil {
872 // freeTrace clears tr, so we hold tr.recycler and tr.events here.
873 go func(f func(interface{}), es []event) {
874 for _, e := range es {
875 if e.Recyclable {
876 f(e.What)
877 }
878 }
879 }(tr.recycler, tr.events)
880 }
881 tr.mu.RUnlock()
882
883 freeTrace(tr)
884 }
885}
886
887func (tr *trace) When() string {
888 return tr.Start.Format("2006/01/02 15:04:05.000000")
889}
890
891func (tr *trace) ElapsedTime() string {
892 tr.mu.RLock()
893 t := tr.Elapsed
894 tr.mu.RUnlock()
895
896 if t == 0 {
897 // Active trace.
898 t = time.Since(tr.Start)
899 }
900 return fmt.Sprintf("%.6f", t.Seconds())
901}
902
903func (tr *trace) Events() []event {
904 tr.mu.RLock()
905 defer tr.mu.RUnlock()
906 return tr.events
907}
908
909var traceFreeList = make(chan *trace, 1000) // TODO(dsymonds): Use sync.Pool?
910
911// newTrace returns a trace ready to use.
912func newTrace() *trace {
913 select {
914 case tr := <-traceFreeList:
915 return tr
916 default:
917 return new(trace)
918 }
919}
920
921// freeTrace adds tr to traceFreeList if there's room.
922// This is non-blocking.
923func freeTrace(tr *trace) {
924 if DebugUseAfterFinish {
925 return // never reuse
926 }
927 tr.reset()
928 select {
929 case traceFreeList <- tr:
930 default:
931 }
932}
933
934func elapsed(d time.Duration) string {
935 b := []byte(fmt.Sprintf("%.6f", d.Seconds()))
936
937 // For subsecond durations, blank all zeros before decimal point,
938 // and all zeros between the decimal point and the first non-zero digit.
939 if d < time.Second {
940 dot := bytes.IndexByte(b, '.')
941 for i := 0; i < dot; i++ {
942 b[i] = ' '
943 }
944 for i := dot + 1; i < len(b); i++ {
945 if b[i] == '0' {
946 b[i] = ' '
947 } else {
948 break
949 }
950 }
951 }
952
953 return string(b)
954}
955
956var pageTmplCache *template.Template
957var pageTmplOnce sync.Once
958
959func pageTmpl() *template.Template {
960 pageTmplOnce.Do(func() {
961 pageTmplCache = template.Must(template.New("Page").Funcs(template.FuncMap{
962 "elapsed": elapsed,
963 "add": func(a, b int) int { return a + b },
964 }).Parse(pageHTML))
965 })
966 return pageTmplCache
967}
968
969const pageHTML = `
970{{template "Prolog" .}}
971{{template "StatusTable" .}}
972{{template "Epilog" .}}
973
974{{define "Prolog"}}
975<html>
976 <head>
977 <title>/debug/requests</title>
978 <style type="text/css">
979 body {
980 font-family: sans-serif;
981 }
982 table#tr-status td.family {
983 padding-right: 2em;
984 }
985 table#tr-status td.active {
986 padding-right: 1em;
987 }
988 table#tr-status td.latency-first {
989 padding-left: 1em;
990 }
991 table#tr-status td.empty {
992 color: #aaa;
993 }
994 table#reqs {
995 margin-top: 1em;
996 }
997 table#reqs tr.first {
998 {{if $.Expanded}}font-weight: bold;{{end}}
999 }
1000 table#reqs td {
1001 font-family: monospace;
1002 }
1003 table#reqs td.when {
1004 text-align: right;
1005 white-space: nowrap;
1006 }
1007 table#reqs td.elapsed {
1008 padding: 0 0.5em;
1009 text-align: right;
1010 white-space: pre;
1011 width: 10em;
1012 }
1013 address {
1014 font-size: smaller;
1015 margin-top: 5em;
1016 }
1017 </style>
1018 </head>
1019 <body>
1020
1021<h1>/debug/requests</h1>
1022{{end}} {{/* end of Prolog */}}
1023
1024{{define "StatusTable"}}
1025<table id="tr-status">
1026 {{range $fam := .Families}}
1027 <tr>
1028 <td class="family">{{$fam}}</td>
1029
1030 {{$n := index $.ActiveTraceCount $fam}}
1031 <td class="active {{if not $n}}empty{{end}}">
1032 {{if $n}}<a href="?fam={{$fam}}&b=-1{{if $.Expanded}}&exp=1{{end}}">{{end}}
1033 [{{$n}} active]
1034 {{if $n}}</a>{{end}}
1035 </td>
1036
1037 {{$f := index $.CompletedTraces $fam}}
1038 {{range $i, $b := $f.Buckets}}
1039 {{$empty := $b.Empty}}
1040 <td {{if $empty}}class="empty"{{end}}>
1041 {{if not $empty}}<a href="?fam={{$fam}}&b={{$i}}{{if $.Expanded}}&exp=1{{end}}">{{end}}
1042 [{{.Cond}}]
1043 {{if not $empty}}</a>{{end}}
1044 </td>
1045 {{end}}
1046
1047 {{$nb := len $f.Buckets}}
1048 <td class="latency-first">
1049 <a href="?fam={{$fam}}&b={{$nb}}">[minute]</a>
1050 </td>
1051 <td>
1052 <a href="?fam={{$fam}}&b={{add $nb 1}}">[hour]</a>
1053 </td>
1054 <td>
1055 <a href="?fam={{$fam}}&b={{add $nb 2}}">[total]</a>
1056 </td>
1057
1058 </tr>
1059 {{end}}
1060</table>
1061{{end}} {{/* end of StatusTable */}}
1062
1063{{define "Epilog"}}
1064{{if $.Traces}}
1065<hr />
1066<h3>Family: {{$.Family}}</h3>
1067
1068{{if or $.Expanded $.Traced}}
1069 <a href="?fam={{$.Family}}&b={{$.Bucket}}">[Normal/Summary]</a>
1070{{else}}
1071 [Normal/Summary]
1072{{end}}
1073
1074{{if or (not $.Expanded) $.Traced}}
1075 <a href="?fam={{$.Family}}&b={{$.Bucket}}&exp=1">[Normal/Expanded]</a>
1076{{else}}
1077 [Normal/Expanded]
1078{{end}}
1079
1080{{if not $.Active}}
1081 {{if or $.Expanded (not $.Traced)}}
1082 <a href="?fam={{$.Family}}&b={{$.Bucket}}&rtraced=1">[Traced/Summary]</a>
1083 {{else}}
1084 [Traced/Summary]
1085 {{end}}
1086 {{if or (not $.Expanded) (not $.Traced)}}
1087 <a href="?fam={{$.Family}}&b={{$.Bucket}}&exp=1&rtraced=1">[Traced/Expanded]</a>
1088 {{else}}
1089 [Traced/Expanded]
1090 {{end}}
1091{{end}}
1092
1093{{if $.Total}}
1094<p><em>Showing <b>{{len $.Traces}}</b> of <b>{{$.Total}}</b> traces.</em></p>
1095{{end}}
1096
1097<table id="reqs">
1098 <caption>
1099 {{if $.Active}}Active{{else}}Completed{{end}} Requests
1100 </caption>
1101 <tr><th>When</th><th>Elapsed&nbsp;(s)</th></tr>
1102 {{range $tr := $.Traces}}
1103 <tr class="first">
1104 <td class="when">{{$tr.When}}</td>
1105 <td class="elapsed">{{$tr.ElapsedTime}}</td>
1106 <td>{{$tr.Title}}</td>
1107 {{/* TODO: include traceID/spanID */}}
1108 </tr>
1109 {{if $.Expanded}}
1110 {{range $tr.Events}}
1111 <tr>
1112 <td class="when">{{.WhenString}}</td>
1113 <td class="elapsed">{{elapsed .Elapsed}}</td>
1114 <td>{{if or $.ShowSensitive (not .Sensitive)}}... {{.What}}{{else}}<em>[redacted]</em>{{end}}</td>
1115 </tr>
1116 {{end}}
1117 {{end}}
1118 {{end}}
1119</table>
1120{{end}} {{/* if $.Traces */}}
1121
1122{{if $.Histogram}}
1123<h4>Latency (&micro;s) of {{$.Family}} over {{$.HistogramWindow}}</h4>
1124{{$.Histogram}}
1125{{end}} {{/* if $.Histogram */}}
1126
1127 </body>
1128</html>
1129{{end}} {{/* end of Epilog */}}
1130`
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 546605c..b510145 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -44,6 +44,8 @@ github.com/mdlayher/netlink/nlenc
44# github.com/mdlayher/wifi v0.0.0-20190303161829-b1436901ddee 44# github.com/mdlayher/wifi v0.0.0-20190303161829-b1436901ddee
45github.com/mdlayher/wifi 45github.com/mdlayher/wifi
46github.com/mdlayher/wifi/internal/nl80211 46github.com/mdlayher/wifi/internal/nl80211
47# github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223
48github.com/mwitkow/go-conntrack
47# github.com/pkg/errors v0.9.1 49# github.com/pkg/errors v0.9.1
48github.com/pkg/errors 50github.com/pkg/errors
49# github.com/prometheus/client_golang v1.4.1 51# github.com/prometheus/client_golang v1.4.1
@@ -53,6 +55,7 @@ github.com/prometheus/client_golang/prometheus/promhttp
53# github.com/prometheus/client_model v0.2.0 55# github.com/prometheus/client_model v0.2.0
54github.com/prometheus/client_model/go 56github.com/prometheus/client_model/go
55# github.com/prometheus/common v0.9.1 57# github.com/prometheus/common v0.9.1
58github.com/prometheus/common/config
56github.com/prometheus/common/expfmt 59github.com/prometheus/common/expfmt
57github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg 60github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg
58github.com/prometheus/common/model 61github.com/prometheus/common/model
@@ -78,6 +81,9 @@ go.uber.org/atomic
78go.uber.org/multierr 81go.uber.org/multierr
79# go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee 82# go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee
80go.uber.org/tools/update-license 83go.uber.org/tools/update-license
84# golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
85golang.org/x/crypto/bcrypt
86golang.org/x/crypto/blowfish
81# golang.org/x/lint v0.0.0-20200130185559-910be7a94367 87# golang.org/x/lint v0.0.0-20200130185559-910be7a94367
82golang.org/x/lint 88golang.org/x/lint
83golang.org/x/lint/golint 89golang.org/x/lint/golint
@@ -85,7 +91,9 @@ golang.org/x/lint/golint
85golang.org/x/net/bpf 91golang.org/x/net/bpf
86golang.org/x/net/internal/iana 92golang.org/x/net/internal/iana
87golang.org/x/net/internal/socket 93golang.org/x/net/internal/socket
94golang.org/x/net/internal/timeseries
88golang.org/x/net/ipv4 95golang.org/x/net/ipv4
96golang.org/x/net/trace
89# golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e 97# golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
90golang.org/x/sync/errgroup 98golang.org/x/sync/errgroup
91# golang.org/x/sys v0.0.0-20200217220822-9197077df867 99# golang.org/x/sys v0.0.0-20200217220822-9197077df867