From efaedcc6b2bd83e78254897f58b86a469eadba8a Mon Sep 17 00:00:00 2001 From: antanst Date: Thu, 27 Feb 2025 09:20:22 +0200 Subject: [PATCH] Improvements in error handling & descriptions --- common/text/text.go | 9 +++++++++ gemini/errors.go | 21 +++++++++++++++------ gemini/network.go | 3 ++- gemini/processing.go | 5 +++-- gopher/network.go | 9 +++------ 5 files changed, 32 insertions(+), 15 deletions(-) create mode 100644 common/text/text.go diff --git a/common/text/text.go b/common/text/text.go new file mode 100644 index 0000000..f977122 --- /dev/null +++ b/common/text/text.go @@ -0,0 +1,9 @@ +package text + +import "strings" + +// RemoveNullChars removes all null characters from the input string. +func RemoveNullChars(input string) string { + // Replace all null characters with an empty string + return strings.ReplaceAll(input, "\u0000", "") +} diff --git a/gemini/errors.go b/gemini/errors.go index 06f2756..29b1e70 100644 --- a/gemini/errors.go +++ b/gemini/errors.go @@ -20,21 +20,29 @@ func (e *GeminiError) Error() string { return fmt.Sprintf("gemini error: code %d %s", e.Code, e.Msg) } +// NewGeminiError creates a new GeminiError based on the status code and header. +// Status codes are based on the Gemini protocol specification: +// - 1x: Input required +// - 2x: Success (not handled as errors) +// - 3x: Redirect +// - 4x: Temporary failure +// - 5x: Permanent failure +// - 6x: Client certificate required/rejected func NewGeminiError(code int, header string) error { var msg string switch { case code >= 10 && code < 20: - msg = "needs input" + msg = fmt.Sprintf("input required: %s", header) case code >= 30 && code < 40: - msg = "redirect" + msg = fmt.Sprintf("redirect: %s", header) case code >= 40 && code < 50: - msg = "bad request" + msg = fmt.Sprintf("request failed: %s", header) case code >= 50 && code < 60: - msg = "server error" + msg = fmt.Sprintf("server error: %s", header) case code >= 60 && code < 70: - msg = "TLS error" + msg = fmt.Sprintf("TLS error: %s", header) default: - msg = "unexpected Status code" + msg = fmt.Sprintf("unexpected status code %d: %s", code, header) } return &GeminiError{ Msg: msg, @@ -43,6 +51,7 @@ func NewGeminiError(code int, header string) error { } } +// IsGeminiError checks if the given error is a GeminiError. func IsGeminiError(err error) bool { if err == nil { return false diff --git a/gemini/network.go b/gemini/network.go index b2cd280..384f6b1 100644 --- a/gemini/network.go +++ b/gemini/network.go @@ -17,6 +17,7 @@ import ( _url "gemini-grc/common/url" "gemini-grc/config" "gemini-grc/logging" + "github.com/antanst/go_errors" "github.com/guregu/null/v5" ) @@ -182,7 +183,7 @@ func processData(s snapshot.Snapshot, data []byte) (*snapshot.Snapshot, error) { if mimeType == "text/gemini" { validBody, err := BytesToValidUTF8(body) if err != nil { - return nil, go_errors.NewError(err) + return nil, err } s.GemText = null.StringFrom(validBody) } else { diff --git a/gemini/processing.go b/gemini/processing.go index d631778..0d86758 100644 --- a/gemini/processing.go +++ b/gemini/processing.go @@ -7,6 +7,7 @@ import ( "io" "unicode/utf8" + "github.com/antanst/go_errors" "golang.org/x/text/encoding/charmap" "golang.org/x/text/encoding/japanese" "golang.org/x/text/encoding/korean" @@ -24,7 +25,7 @@ func BytesToValidUTF8(input []byte) (string, error) { } const maxSize = 10 * 1024 * 1024 // 10MB if len(input) > maxSize { - return "", fmt.Errorf("%w: %d bytes (max %d)", ErrInputTooLarge, len(input), maxSize) + return "", go_errors.NewError(fmt.Errorf("%w: %d bytes (max %d)", ErrInputTooLarge, len(input), maxSize)) } // remove NULL byte 0x00 (ReplaceAll accepts slices) inputNoNull := bytes.ReplaceAll(input, []byte{byte(0)}, []byte{}) @@ -55,5 +56,5 @@ func BytesToValidUTF8(input []byte) (string, error) { } } - return "", fmt.Errorf("%w (tried %d encodings): %w", ErrUTF8Conversion, len(encodings), lastErr) + return "", go_errors.NewError(fmt.Errorf("%w (tried %d encodings): %w", ErrUTF8Conversion, len(encodings), lastErr)) } diff --git a/gopher/network.go b/gopher/network.go index cd31a5f..e6c8c22 100644 --- a/gopher/network.go +++ b/gopher/network.go @@ -13,9 +13,11 @@ import ( errors2 "gemini-grc/common/errors" "gemini-grc/common/linkList" "gemini-grc/common/snapshot" + "gemini-grc/common/text" _url "gemini-grc/common/url" "gemini-grc/config" "gemini-grc/logging" + "github.com/antanst/go_errors" "github.com/guregu/null/v5" ) @@ -85,7 +87,7 @@ func Visit(url string) (*snapshot.Snapshot, error) { isValidUTF8 := utf8.ValidString(string(data)) if isValidUTF8 { - s.GemText = null.StringFrom(removeNullChars(string(data))) + s.GemText = null.StringFrom(text.RemoveNullChars(string(data))) } else { s.Data = null.ValueFrom(data) } @@ -276,8 +278,3 @@ func getGopherPageLinks(content string) []string { return links } - -func removeNullChars(input string) string { - // Replace all null characters with an empty string - return strings.ReplaceAll(input, "\u0000", "") -}