tsweb: add StdHandlerOpts that accepts an options struct

I'm about to add yet another StdHandler option.
Time to refactor.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
This commit is contained in:
Josh Bleecher Snyder 2020-10-29 13:55:42 -07:00 committed by Josh Bleecher Snyder
parent eab6e9ea4e
commit e98f2c57d6
2 changed files with 29 additions and 17 deletions

View File

@ -157,11 +157,17 @@ type ReturnHandler interface {
ServeHTTPReturn(http.ResponseWriter, *http.Request) error
}
type HandlerOptions struct {
Quiet200s bool // if set, do not log successfully handled HTTP requests
Logf logger.Logf
Now func() time.Time // if nil, defaults to time.Now
}
// StdHandler converts a ReturnHandler into a standard http.Handler.
// Handled requests are logged using logf, as are any errors. Errors
// are handled as specified by the Handler interface.
func StdHandler(h ReturnHandler, logf logger.Logf) http.Handler {
return stdHandler(h, logf, time.Now, true)
return StdHandlerOpts(h, HandlerOptions{Logf: logf, Now: time.Now})
}
// ReturnHandlerFunc is an adapter to allow the use of ordinary
@ -178,27 +184,32 @@ func (f ReturnHandlerFunc) ServeHTTPReturn(w http.ResponseWriter, r *http.Reques
// StdHandlerNo200s is like StdHandler, but successfully handled HTTP
// requests don't write an access log entry to logf.
//
// TODO(danderson): quick stopgap, probably want ...Options on StdHandler instead?
// TODO(josharian): eliminate this and StdHandler in favor of StdHandlerOpts,
// rename StdHandlerOpts to StdHandler. Will be a breaking API change.
func StdHandlerNo200s(h ReturnHandler, logf logger.Logf) http.Handler {
return stdHandler(h, logf, time.Now, false)
return StdHandlerOpts(h, HandlerOptions{Logf: logf, Now: time.Now, Quiet200s: true})
}
func stdHandler(h ReturnHandler, logf logger.Logf, now func() time.Time, log200s bool) http.Handler {
return retHandler{h, logf, now, log200s}
// StdHandlerOpts converts a ReturnHandler into a standard http.Handler.
// Handled requests are logged using opts.Logf, as are any errors.
// Errors are handled as specified by the Handler interface.
func StdHandlerOpts(h ReturnHandler, opts HandlerOptions) http.Handler {
if opts.Now == nil {
opts.Now = time.Now
}
return retHandler{h, opts}
}
// retHandler is an http.Handler that wraps a Handler and handles errors.
type retHandler struct {
rh ReturnHandler
logf logger.Logf
timeNow func() time.Time
log200s bool
rh ReturnHandler
opts HandlerOptions
}
// ServeHTTP implements the http.Handler interface.
func (h retHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
msg := AccessLogRecord{
When: h.timeNow(),
When: h.opts.Now(),
RemoteAddr: r.RemoteAddr,
Proto: r.Proto,
TLS: r.TLS != nil,
@ -209,7 +220,7 @@ func (h retHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Referer: r.Referer(),
}
lw := &loggingResponseWriter{ResponseWriter: w, logf: h.logf}
lw := &loggingResponseWriter{ResponseWriter: w, logf: h.opts.Logf}
err := h.rh.ServeHTTPReturn(lw, r)
hErr, hErrOK := err.(HTTPError)
@ -219,7 +230,7 @@ func (h retHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
lw.code = 200
}
msg.Seconds = h.timeNow().Sub(msg.When).Seconds()
msg.Seconds = h.opts.Now().Sub(msg.When).Seconds()
msg.Code = lw.code
msg.Bytes = lw.bytes
@ -245,12 +256,12 @@ func (h retHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
if lw.code != 0 {
h.logf("[unexpected] handler returned HTTPError %v, but already sent a response with code %d", hErr, lw.code)
h.opts.Logf("[unexpected] handler returned HTTPError %v, but already sent a response with code %d", hErr, lw.code)
break
}
msg.Code = hErr.Code
if msg.Code == 0 {
h.logf("[unexpected] HTTPError %v did not contain an HTTP status code, sending internal server error", hErr)
h.opts.Logf("[unexpected] HTTPError %v did not contain an HTTP status code, sending internal server error", hErr)
msg.Code = http.StatusInternalServerError
}
http.Error(lw, hErr.Msg, msg.Code)
@ -264,8 +275,9 @@ func (h retHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
if msg.Code != 200 || h.log200s {
h.logf("%s", msg)
if msg.Code != 200 || !h.opts.Quiet200s {
h.opts.Logf("%s", msg)
}
}
}

View File

@ -248,7 +248,7 @@ func TestStdHandler(t *testing.T) {
clock.Reset()
rec := noopHijacker{httptest.NewRecorder(), false}
h := stdHandler(test.rh, logf, clock.Now, true)
h := StdHandlerOpts(test.rh, HandlerOptions{Logf: logf, Now: clock.Now})
h.ServeHTTP(&rec, test.r)
res := rec.Result()
if res.StatusCode != test.wantCode {