/
handler.go
172 lines (147 loc) · 5.47 KB
/
handler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package debugstatus
import (
"net/http"
"golang.org/x/net/trace"
"gopkg.in/errgo.v1"
pprof "github.com/juju/httpprof"
"golang.org/x/net/context"
"gopkg.in/httprequest.v1"
)
// Version describes the current version of the code being run.
type Version struct {
GitCommit string
Version string
}
// Handler implements a type that can be used with httprequest.Handlers
// to serve a standard set of /debug endpoints, including
// the version of the system, its current health status
// the runtime profiling information.
type Handler struct {
// Check will be called to obtain the current health of the
// system. It should return a map as returned from the
// Check function. If this is nil, an empty result will
// always be returned from /debug/status.
Check func(context.Context) map[string]CheckResult
// Version should hold the current version
// of the binary running the server, served
// from the /debug/info endpoint.
Version Version
// CheckPprofAllowed will be used to check whether the
// given pprof request should be allowed.
// It should return an error if not, which will not be masked.
// If this is nil, no access will be allowed to any
// of the endpoints under /debug/pprof - the
// error returned will be ErrNoPprofConfigured.
CheckPprofAllowed func(req *http.Request) error
// CheckTraceAllowed will be used to check whether the given
// trace request should be allowed. It should return an error if
// not, which will not be masked. If this is nil, no access will
// be allowed to either /debug/events or /debug/requests - the
// error returned will be ErrNoTraceConfigured. If access is
// allowed, the sensitive value specifies whether sensitive trace
// events will be shown.
CheckTraceAllowed func(req *http.Request) (sensitive bool, err error)
}
// DebugStatusRequest describes the /debug/status endpoint.
type DebugStatusRequest struct {
httprequest.Route `httprequest:"GET /debug/status"`
}
// DebugStatus returns the current status of the server.
func (h *Handler) DebugStatus(p httprequest.Params, _ *DebugStatusRequest) (map[string]CheckResult, error) {
if h.Check == nil {
return map[string]CheckResult{}, nil
}
return h.Check(p.Context), nil
}
// DebugInfoRequest describes the /debug/info endpoint.
type DebugInfoRequest struct {
httprequest.Route `httprequest:"GET /debug/info"`
}
// DebugInfo returns version information on the current server.
func (h *Handler) DebugInfo(*DebugInfoRequest) (Version, error) {
return h.Version, nil
}
// DebugPprofRequest describes the /debug/pprof/ endpoint.
type DebugPprofRequest struct {
httprequest.Route `httprequest:"GET /debug/pprof/"`
}
// DebugPprof serves index information on the available pprof endpoints.
func (h *Handler) DebugPprof(p httprequest.Params, _ *DebugPprofRequest) error {
if err := h.checkPprofAllowed(p.Request); err != nil {
return err
}
pprof.Index(p.Response, p.Request)
return nil
}
// DebugPprofEndpointsRequest describes the endpoints under /debug/prof.
type DebugPprofEndpointsRequest struct {
httprequest.Route `httprequest:"GET /debug/pprof/:name"`
Name string `httprequest:"name,path"`
}
// DebugPprofEndpoints serves all the endpoints under DebugPprof.
func (h *Handler) DebugPprofEndpoints(p httprequest.Params, r *DebugPprofEndpointsRequest) error {
if err := h.checkPprofAllowed(p.Request); err != nil {
return err
}
switch r.Name {
case "cmdline":
pprof.Cmdline(p.Response, p.Request)
case "profile":
pprof.Profile(p.Response, p.Request)
case "symbol":
pprof.Symbol(p.Response, p.Request)
default:
pprof.Handler(r.Name).ServeHTTP(p.Response, p.Request)
}
return nil
}
// ErrNoPprofConfigured is the error returned on access
// to endpoints when Handler.CheckPprofAllowed is nil.
var ErrNoPprofConfigured = errgo.New("no pprof access configured")
// checkPprofAllowed is used instead of h.CheckPprofAllowed
// so that we don't panic if that is nil.
func (h *Handler) checkPprofAllowed(req *http.Request) error {
if h.CheckPprofAllowed == nil {
return ErrNoPprofConfigured
}
return h.CheckPprofAllowed(req)
}
// DebugEventsRequest describes the /debug/events endpoint.
type DebugEventsRequest struct {
httprequest.Route `httprequest:"GET /debug/events"`
}
// DebugEvents serves the /debug/events endpoint.
func (h *Handler) DebugEvents(p httprequest.Params, r *DebugEventsRequest) error {
sensitive, err := h.checkTraceAllowed(p.Request)
if err != nil {
return errgo.Mask(err, errgo.Any)
}
trace.RenderEvents(p.Response, p.Request, sensitive)
return nil
}
// DebugRequestsRequest describes the /debug/requests endpoint.
type DebugRequestsRequest struct {
httprequest.Route `httprequest:"GET /debug/requests"`
}
// DebugRequests serves the /debug/requests endpoint.
func (h *Handler) DebugRequests(p httprequest.Params, r *DebugRequestsRequest) error {
sensitive, err := h.checkTraceAllowed(p.Request)
if err != nil {
return errgo.Mask(err, errgo.Any)
}
trace.Render(p.Response, p.Request, sensitive)
return nil
}
// ErrNoTraceConfigured is the error returned on access
// to endpoints when Handler.CheckTraceAllowed is nil.
var ErrNoTraceConfigured = errgo.New("no trace access configured")
// checkTraceAllowed is used instead of h.CheckTraceAllowed
// so that we don't panic if that is nil.
func (h *Handler) checkTraceAllowed(req *http.Request) (bool, error) {
if h.CheckTraceAllowed == nil {
return false, ErrNoTraceConfigured
}
return h.CheckTraceAllowed(req)
}