|
|
|
|
@ -8,11 +8,13 @@ import ( |
|
|
|
|
"context" |
|
|
|
|
"errors" |
|
|
|
|
"fmt" |
|
|
|
|
"io" |
|
|
|
|
"net" |
|
|
|
|
"net/http" |
|
|
|
|
"net/http/httptest" |
|
|
|
|
"net/url" |
|
|
|
|
"strings" |
|
|
|
|
"sync/atomic" |
|
|
|
|
"testing" |
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
@ -527,6 +529,82 @@ func TestStdHandler(t *testing.T) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func TestStdHandler_Panic(t *testing.T) { |
|
|
|
|
var r AccessLogRecord |
|
|
|
|
h := StdHandler( |
|
|
|
|
ReturnHandlerFunc(func(w http.ResponseWriter, r *http.Request) error { |
|
|
|
|
panicElsewhere() |
|
|
|
|
return nil |
|
|
|
|
}), |
|
|
|
|
HandlerOptions{ |
|
|
|
|
Logf: t.Logf, |
|
|
|
|
OnCompletion: func(_ *http.Request, alr AccessLogRecord) { |
|
|
|
|
r = alr |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
// Run our panicking handler in a http.Server which catches and rethrows
|
|
|
|
|
// any panics.
|
|
|
|
|
var recovered atomic.Value |
|
|
|
|
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
defer func() { |
|
|
|
|
if r := recovered.Load(); r != nil { |
|
|
|
|
panic(r) |
|
|
|
|
} |
|
|
|
|
}() |
|
|
|
|
defer func() { |
|
|
|
|
recovered.Store(recover()) |
|
|
|
|
}() |
|
|
|
|
h.ServeHTTP(w, r) |
|
|
|
|
})) |
|
|
|
|
t.Cleanup(s.Close) |
|
|
|
|
|
|
|
|
|
// Send a request to our server.
|
|
|
|
|
res, err := http.Get(s.URL) |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatal(err) |
|
|
|
|
} |
|
|
|
|
if recovered.Load() == nil { |
|
|
|
|
t.Fatal("expected panic but saw none") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Check that the log message contained the stack trace in the error.
|
|
|
|
|
var logerr bool |
|
|
|
|
if p := "panic: panicked elsewhere\n\ngoroutine "; !strings.HasPrefix(r.Err, p) { |
|
|
|
|
t.Errorf("got error prefix %q, want %q", r.Err[:min(len(r.Err), len(p))], p) |
|
|
|
|
logerr = true |
|
|
|
|
} |
|
|
|
|
if s := "\ntailscale.com/tsweb.panicElsewhere("; !strings.Contains(r.Err, s) { |
|
|
|
|
t.Errorf("want substr %q, not found", s) |
|
|
|
|
logerr = true |
|
|
|
|
} |
|
|
|
|
if logerr { |
|
|
|
|
t.Logf("logger got error: (quoted) %q\n\n(verbatim)\n%s", r.Err, r.Err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
t.Run("check_response", func(t *testing.T) { |
|
|
|
|
// TODO(icio): Swallow panics? tailscale/tailscale#12784
|
|
|
|
|
t.SkipNow() |
|
|
|
|
|
|
|
|
|
// Check that the server sent an error response.
|
|
|
|
|
if res.StatusCode != 500 { |
|
|
|
|
t.Errorf("got status code %d, want %d", res.StatusCode, 500) |
|
|
|
|
} |
|
|
|
|
body, err := io.ReadAll(res.Body) |
|
|
|
|
if err != nil { |
|
|
|
|
t.Errorf("error reading body: %s", err) |
|
|
|
|
} else if want := "internal server error\n"; string(body) != want { |
|
|
|
|
t.Errorf("got body %q, want %q", body, want) |
|
|
|
|
} |
|
|
|
|
res.Body.Close() |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func panicElsewhere() { |
|
|
|
|
panic("panicked elsewhere") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func BenchmarkLogNot200(b *testing.B) { |
|
|
|
|
b.ReportAllocs() |
|
|
|
|
rh := handlerFunc(func(w http.ResponseWriter, r *http.Request) error { |
|
|
|
|
|