From e2df7d19beaa96b2e50f7211620db45f4546b5b5 Mon Sep 17 00:00:00 2001 From: Evgeny Abramovich Date: Sat, 30 Dec 2023 12:41:07 -0400 Subject: [PATCH] Improved error page --- go.mod | 1 + go.sum | 2 ++ internal/infra/http_error.go | 26 +++++++++++++++++++++++--- internal/infra/http_error_test.go | 19 ++++++++++++++++++- 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 4a24e310..ec77b28c 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( ) require ( + github.com/dustin/go-humanize v1.0.1 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect diff --git a/go.sum b/go.sum index d5ad94fe..54f5981e 100644 --- a/go.sum +++ b/go.sum @@ -30,6 +30,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= diff --git a/internal/infra/http_error.go b/internal/infra/http_error.go index 72f1c80c..d9a9c45e 100644 --- a/internal/infra/http_error.go +++ b/internal/infra/http_error.go @@ -2,7 +2,10 @@ package infra import ( "net/http" + "runtime" + "runtime/debug" + "github.com/dustin/go-humanize" "github.com/evg4b/uncors/internal/helpers" "github.com/go-http-utils/headers" "github.com/pterm/pterm" @@ -14,18 +17,35 @@ var style = pterm.Style{} func HTTPError(writer http.ResponseWriter, err error) { header := writer.Header() header.Set(headers.ContentType, "text/plain; charset=utf-8") + header.Set(headers.ContentEncoding, "identity") + header.Set(headers.CacheControl, "no-cache, no-store, max-age=0, must-revalidate") + header.Set(headers.Pragma, "no-cache") header.Set(headers.XContentTypeOptions, "nosniff") + header.Del(headers.SetCookie) + writer.WriteHeader(http.StatusInternalServerError) - message := helpers.Sprintf("%d Error", http.StatusInternalServerError) + pageHeader := helpers.Sprintf("%d Error", http.StatusInternalServerError) helpers.FPrintln(writer) - helpers.FPrintln(writer, pageHeader(message)) + helpers.FPrintln(writer, pageHeaderFormatter(pageHeader)) helpers.FPrintln(writer) helpers.FPrintln(writer, helpers.Sprintf("Occurred error: %s", err)) + helpers.FPrintln(writer) + + helpers.FPrint(writer, "Stack trace: ") + helpers.FPrintln(writer, string(debug.Stack())) + + var memStats runtime.MemStats + runtime.ReadMemStats(&memStats) + helpers.FPrintln(writer, "Memory usage:") + helpers.FPrintf(writer, "Alloc = %v\n", humanize.Bytes(memStats.Alloc)) + helpers.FPrintf(writer, "TotalAlloc = %v\n", humanize.Bytes(memStats.TotalAlloc)) + helpers.FPrintf(writer, "Sys = %v\n", humanize.Bytes(memStats.Sys)) + helpers.FPrintf(writer, "NumGC = %v\n", memStats.NumGC) } -func pageHeader(message string) string { +func pageHeaderFormatter(message string) string { letters := putils.LettersFromStringWithStyle(message, &style) text, err := pterm.DefaultBigText.WithLetters(letters).Srender() if err != nil { diff --git a/internal/infra/http_error_test.go b/internal/infra/http_error_test.go index 5f6e14dc..d518a2ba 100644 --- a/internal/infra/http_error_test.go +++ b/internal/infra/http_error_test.go @@ -25,15 +25,32 @@ Occurred error: net/http: abort Handler func TestHttpError(t *testing.T) { recorder := httptest.NewRecorder() infra.HTTPError(recorder, http.ErrAbortHandler) + body := testutils.ReadBody(t, recorder) t.Run("write correct page", func(t *testing.T) { - assert.Equal(t, expectedPage, testutils.ReadBody(t, recorder)) + assert.Contains(t, body, expectedPage) }) t.Run("write correct headers", func(t *testing.T) { header := recorder.Header() assert.NotNil(t, header[headers.ContentType]) + assert.NotNil(t, header[headers.ContentEncoding]) + assert.NotNil(t, header[headers.CacheControl]) + assert.NotNil(t, header[headers.Pragma]) assert.NotNil(t, header[headers.XContentTypeOptions]) + assert.Nil(t, header[headers.SetCookie]) + }) + + t.Run("Should include stack trace", func(t *testing.T) { + assert.Regexp(t, "Stack trace: goroutine \\d+ \\[running\\]:", body) + }) + + t.Run("Should include memory usage", func(t *testing.T) { + assert.Contains(t, body, "Memory usage:") + assert.Regexp(t, "Alloc = [\\d\\.]+ [Mk]B", body) + assert.Regexp(t, "TotalAlloc = [\\d\\.]+ [Mk]B", body) + assert.Regexp(t, "Sys = [\\d\\.]+ [Mk]B", body) + assert.Regexp(t, "NumGC = [\\d\\.]+", body) }) }