mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-21 22:21:41 +00:00
tsweb: add a Handler type.
Handler is like http.Handler, but returns errors. ErrHandler converts back to an http.Handler, with added error handling and logging. Signed-off-by: David Anderson <dave@natulte.net>
This commit is contained in:
parent
f8d67bb591
commit
98eceae55e
@ -43,7 +43,9 @@ func (m Msg) String() string {
|
|||||||
return strings.TrimRight(buf.String(), "\n")
|
return strings.TrimRight(buf.String(), "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPError is an error with embedded HTTP response information.
|
// HTTPError is an error with embedded HTTP response information. When
|
||||||
|
// received by an ErrHandler, the Code and Msg are sent to the client,
|
||||||
|
// while Err is logged on the server.
|
||||||
type HTTPError struct {
|
type HTTPError struct {
|
||||||
Code int
|
Code int
|
||||||
Msg string // sent to the end-user
|
Msg string // sent to the end-user
|
||||||
|
@ -2,11 +2,12 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Package tsweb contains code between various Tailscale webservers.
|
// Package tsweb contains code used in various Tailscale webservers.
|
||||||
package tsweb
|
package tsweb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"expvar"
|
"expvar"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -22,6 +23,7 @@ import (
|
|||||||
|
|
||||||
"tailscale.com/metrics"
|
"tailscale.com/metrics"
|
||||||
"tailscale.com/net/interfaces"
|
"tailscale.com/net/interfaces"
|
||||||
|
"tailscale.com/types/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DevMode controls whether extra output in shown, for when the binary is being run in dev mode.
|
// DevMode controls whether extra output in shown, for when the binary is being run in dev mode.
|
||||||
@ -30,12 +32,12 @@ var DevMode bool
|
|||||||
// NewMux returns a new ServeMux with debugHandler registered (and protected) at /debug/.
|
// NewMux returns a new ServeMux with debugHandler registered (and protected) at /debug/.
|
||||||
func NewMux(debugHandler http.Handler) *http.ServeMux {
|
func NewMux(debugHandler http.Handler) *http.ServeMux {
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
RegisterCommonDebug(mux)
|
registerCommonDebug(mux)
|
||||||
mux.Handle("/debug/", Protected(debugHandler))
|
mux.Handle("/debug/", Protected(debugHandler))
|
||||||
return mux
|
return mux
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterCommonDebug(mux *http.ServeMux) {
|
func registerCommonDebug(mux *http.ServeMux) {
|
||||||
expvar.Publish("counter_uptime_sec", expvar.Func(func() interface{} { return int64(Uptime().Seconds()) }))
|
expvar.Publish("counter_uptime_sec", expvar.Func(func() interface{} { return int64(Uptime().Seconds()) }))
|
||||||
mux.Handle("/debug/pprof/", Protected(http.DefaultServeMux)) // to net/http/pprof
|
mux.Handle("/debug/pprof/", Protected(http.DefaultServeMux)) // to net/http/pprof
|
||||||
mux.Handle("/debug/vars", Protected(http.DefaultServeMux)) // to expvar
|
mux.Handle("/debug/vars", Protected(http.DefaultServeMux)) // to expvar
|
||||||
@ -137,6 +139,61 @@ func stripPort(hostport string) string {
|
|||||||
return net.JoinHostPort(host, "443")
|
return net.JoinHostPort(host, "443")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handler is like net/http.Handler, but the handler can return an
|
||||||
|
// error.
|
||||||
|
type Handler interface {
|
||||||
|
ServeHTTP(http.ResponseWriter, *http.Request) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// handler is an http.Handler that wraps a Handler and handles errors.
|
||||||
|
type handler struct {
|
||||||
|
h Handler
|
||||||
|
logf logger.Logf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
msg := Msg{
|
||||||
|
Where: "http",
|
||||||
|
When: time.Now(),
|
||||||
|
HTTP: &MsgHTTP{
|
||||||
|
Path: r.URL.Path,
|
||||||
|
RemoteAddr: r.RemoteAddr,
|
||||||
|
UserAgent: r.UserAgent(),
|
||||||
|
Referer: r.Referer(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := h.h.ServeHTTP(w, r)
|
||||||
|
if err == context.Canceled {
|
||||||
|
// No need to inform client, but still log the
|
||||||
|
// cancellation.
|
||||||
|
msg.HTTP.Code = 499 // nginx convention: Client Closed Request
|
||||||
|
msg.Err = err
|
||||||
|
} else if hErr, ok := err.(HTTPError); ok {
|
||||||
|
msg.HTTP.Code = hErr.Code
|
||||||
|
msg.Err = hErr.Err
|
||||||
|
msg.Msg = hErr.Msg
|
||||||
|
http.Error(w, hErr.Msg, hErr.Code)
|
||||||
|
} else if err != nil {
|
||||||
|
msg.HTTP.Code = 500
|
||||||
|
msg.Err = err
|
||||||
|
msg.Msg = "internal server error"
|
||||||
|
http.Error(w, msg.Msg, msg.HTTP.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(danderson): needed? Copied from existing code, but
|
||||||
|
// doesn't HTTPServer do this by itself?
|
||||||
|
if f, _ := w.(http.Flusher); f != nil {
|
||||||
|
f.Flush()
|
||||||
|
}
|
||||||
|
h.logf("%s", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrHandler returns an http.Handler that logs requests and handles
|
||||||
|
// errors from the inner Handler.
|
||||||
|
func ErrHandler(h Handler, logf logger.Logf) http.Handler {
|
||||||
|
return handler{h, logf}
|
||||||
|
}
|
||||||
|
|
||||||
// varzHandler is an HTTP handler to write expvar values into the
|
// varzHandler is an HTTP handler to write expvar values into the
|
||||||
// prometheus export format:
|
// prometheus export format:
|
||||||
//
|
//
|
||||||
|
Loading…
x
Reference in New Issue
Block a user