Skip to content

Commit

Permalink
Merge pull request #200 from mhilton/000-add-trace-endpoints-to-debug
Browse files Browse the repository at this point in the history
Add /debug/events and /debug/requests

Add the golang.org/x/net/trace endpoints to the standard debugstatus handler.

(Review request: http://reviews.vapour.ws/r/4230/)
  • Loading branch information
jujubot committed Mar 30, 2016
2 parents d1440e8 + eb3fa39 commit 55eebc9
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 1 deletion.
53 changes: 53 additions & 0 deletions debugstatus/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package debugstatus
import (
"net/http"

"golang.org/x/net/trace"
"gopkg.in/errgo.v1"

pprof "github.com/juju/httpprof"
Expand Down Expand Up @@ -41,6 +42,15 @@ type Handler struct {
// 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.
Expand Down Expand Up @@ -116,3 +126,46 @@ func (h *Handler) checkPprofAllowed(req *http.Request) error {
}
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)
}
53 changes: 52 additions & 1 deletion debugstatus/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ var errUnauthorized = errgo.New("you shall not pass!")
func newHTTPHandler(h *debugstatus.Handler) http.Handler {
errMapper := httprequest.ErrorMapper(func(err error) (httpStatus int, errorBody interface{}) {
code, status := "", http.StatusInternalServerError
switch err {
switch errgo.Cause(err) {
case errUnauthorized:
code, status = "unauthorized", http.StatusUnauthorized
case debugstatus.ErrNoPprofConfigured:
code, status = "forbidden", http.StatusForbidden
case debugstatus.ErrNoTraceConfigured:
code, status = "forbidden", http.StatusForbidden
}
return status, httprequest.RemoteError{
Code: code,
Expand Down Expand Up @@ -159,3 +161,52 @@ func (s *handlerSuite) TestDebugPprofForbiddenWhenNotConfigured(c *gc.C) {
},
})
}

var debugTracePaths = []string{
"/debug/events",
"/debug/requests",
}

func (s *handlerSuite) TestServeTraceEvents(c *gc.C) {
httpHandler := newHTTPHandler(&debugstatus.Handler{
CheckTraceAllowed: func(req *http.Request) (bool, error) {
if req.Header.Get("Authorization") == "" {
return false, errUnauthorized
}
return false, nil
},
})
authHeader := make(http.Header)
authHeader.Set("Authorization", "let me in")
for i, path := range debugTracePaths {
c.Logf("%d. %s", i, path)
httptesting.AssertJSONCall(c, httptesting.JSONCallParams{
Handler: httpHandler,
URL: path,
ExpectStatus: http.StatusUnauthorized,
ExpectBody: httprequest.RemoteError{
Code: "unauthorized",
Message: "you shall not pass!",
},
})
rr := httptesting.DoRequest(c, httptesting.DoRequestParams{
Handler: httpHandler,
URL: path,
Header: authHeader,
})
c.Assert(rr.Code, gc.Equals, http.StatusOK)
}
}

func (s *handlerSuite) TestDebugEventsForbiddenWhenNotConfigured(c *gc.C) {
httpHandler := newHTTPHandler(&debugstatus.Handler{})
httptesting.AssertJSONCall(c, httptesting.JSONCallParams{
Handler: httpHandler,
URL: "/debug/events",
ExpectStatus: http.StatusForbidden,
ExpectBody: httprequest.RemoteError{
Code: "forbidden",
Message: "no trace access configured",
},
})
}

0 comments on commit 55eebc9

Please sign in to comment.