diff --git a/internal/handler/mock/middleware.go b/internal/handler/mock/middleware.go index f378adb4..5ebcf3b9 100644 --- a/internal/handler/mock/middleware.go +++ b/internal/handler/mock/middleware.go @@ -58,7 +58,7 @@ func (m *Middleware) ServeHTTP(writer http.ResponseWriter, request *http.Request } if err != nil { - http.Error(writer, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + infrastructure.HTTPError(writer, err) return } diff --git a/internal/handler/proxy/middleware.go b/internal/handler/proxy/middleware.go index 071c8c8a..8255bd4d 100644 --- a/internal/handler/proxy/middleware.go +++ b/internal/handler/proxy/middleware.go @@ -5,10 +5,11 @@ import ( "net/http" "strings" + "github.com/evg4b/uncors/internal/infrastructure" + "github.com/evg4b/uncors/internal/contracts" "github.com/evg4b/uncors/internal/helpers" "github.com/evg4b/uncors/internal/urlreplacer" - "github.com/pterm/pterm" ) type Handler struct { @@ -33,9 +34,7 @@ func NewProxyHandler(options ...HandlerOption) *Handler { func (m *Handler) ServeHTTP(response http.ResponseWriter, request *http.Request) { if err := m.handle(response, request); err != nil { - pterm.Error.Printfln("UNCORS error: %v", err) - response.WriteHeader(http.StatusInternalServerError) - fmt.Fprintln(response, "UNCORS error:", err.Error()) + infrastructure.HTTPError(response, err) } } diff --git a/internal/handler/static/middleware.go b/internal/handler/static/middleware.go index e786da0b..db310a09 100644 --- a/internal/handler/static/middleware.go +++ b/internal/handler/static/middleware.go @@ -9,6 +9,8 @@ import ( "path" "strings" + "github.com/evg4b/uncors/internal/infrastructure" + "github.com/evg4b/uncors/internal/contracts" "github.com/spf13/afero" @@ -34,14 +36,6 @@ func NewStaticMiddleware(options ...MiddlewareOption) *Middleware { return middleware } -func toHTTPError(err error) (string, int) { - if errors.Is(err, fs.ErrPermission) { - return http.StatusText(http.StatusForbidden), http.StatusForbidden - } - - return http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError -} - func (m *Middleware) ServeHTTP(writer http.ResponseWriter, request *http.Request) { filePath := m.extractFilePath(request) @@ -56,8 +50,7 @@ func (m *Middleware) ServeHTTP(writer http.ResponseWriter, request *http.Request if errors.Is(err, errNorHandled) { m.next.ServeHTTP(writer, request) } else { - msg, code := toHTTPError(err) - http.Error(writer, msg, code) + infrastructure.HTTPError(writer, err) } return diff --git a/internal/handler/uncors_handler_test.go b/internal/handler/uncors_handler_test.go index 01020994..dc2b6e34 100644 --- a/internal/handler/uncors_handler_test.go +++ b/internal/handler/uncors_handler_test.go @@ -184,7 +184,8 @@ func TestUncorsRequestHandler(t *testing.T) { hand.ServeHTTP(recorder, request) assert.Equal(t, http.StatusInternalServerError, recorder.Code) - assert.Contains(t, testutils.ReadBody(t, recorder), "Internal Server Error") + expectedMessage := "filed to opend index file: open /assets/index.php: file does not exist" + assert.Contains(t, testutils.ReadBody(t, recorder), expectedMessage) }) }) @@ -282,7 +283,8 @@ func TestUncorsRequestHandler(t *testing.T) { hand.ServeHTTP(recorder, request) assert.Equal(t, http.StatusInternalServerError, recorder.Code) - assert.Contains(t, testutils.ReadBody(t, recorder), "Internal Server Error") + expectedMessage := "filed to opent file /unknown.json: open /unknown.json: file does not exist" + assert.Contains(t, testutils.ReadBody(t, recorder), expectedMessage) }) }) } diff --git a/internal/infrastructure/http_error.go b/internal/infrastructure/http_error.go new file mode 100644 index 00000000..d23b7f74 --- /dev/null +++ b/internal/infrastructure/http_error.go @@ -0,0 +1,44 @@ +package infrastructure + +import ( + "fmt" + "io" + "net/http" + + "github.com/go-http-utils/headers" + "github.com/pterm/pterm" + "github.com/pterm/pterm/putils" + "github.com/samber/lo" +) + +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.XContentTypeOptions, "nosniff") + + writer.WriteHeader(http.StatusInternalServerError) + message := fmt.Sprintf("%d Error", http.StatusInternalServerError) + + writeLine(writer) + writeLine(writer, pageHeader(message)) + writeLine(writer) + writeLine(writer, fmt.Sprintf("Occurred error: %s", err)) +} + +func pageHeader(message string) string { + letters := putils.LettersFromStringWithStyle(message, &style) + text, err := pterm.DefaultBigText.WithLetters(letters).Srender() + if err != nil { + panic(err) + } + + return text +} + +func writeLine(writer io.Writer, data ...string) { + if _, err := fmt.Fprintln(writer, lo.ToAnySlice(data)...); err != nil { + panic(err) + } +} diff --git a/internal/infrastructure/http_error_test.go b/internal/infrastructure/http_error_test.go new file mode 100644 index 00000000..f11931df --- /dev/null +++ b/internal/infrastructure/http_error_test.go @@ -0,0 +1,40 @@ +package infrastructure_test + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/go-http-utils/headers" + + "github.com/evg4b/uncors/internal/infrastructure" + "github.com/evg4b/uncors/testing/testutils" + "github.com/stretchr/testify/assert" +) + +const expectedPage = ` +███████ ██████ ██████ ███████ ██████ ██████ ██████ ██████ +██ ██ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ +███████ ██ ██ ██ ██ ██ ██ █████ ██████ ██████ ██ ██ ██████ + ██ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +███████ ██████ ██████ ███████ ██ ██ ██ ██ ██████ ██ ██ + + +Occurred error: net/http: abort Handler +` + +func TestHttpError(t *testing.T) { + recorder := httptest.NewRecorder() + infrastructure.HTTPError(recorder, http.ErrAbortHandler) + + t.Run("write correct page", func(t *testing.T) { + assert.Equal(t, expectedPage, testutils.ReadBody(t, recorder)) + }) + + t.Run("write correct headers", func(t *testing.T) { + header := recorder.Header() + + assert.NotNil(t, header[headers.ContentType]) + assert.NotNil(t, header[headers.XContentTypeOptions]) + }) +}