Improvements in error handling & descriptions
This commit is contained in:
9
common/text/text.go
Normal file
9
common/text/text.go
Normal file
@@ -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", "")
|
||||||
|
}
|
||||||
@@ -20,21 +20,29 @@ func (e *GeminiError) Error() string {
|
|||||||
return fmt.Sprintf("gemini error: code %d %s", e.Code, e.Msg)
|
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 {
|
func NewGeminiError(code int, header string) error {
|
||||||
var msg string
|
var msg string
|
||||||
switch {
|
switch {
|
||||||
case code >= 10 && code < 20:
|
case code >= 10 && code < 20:
|
||||||
msg = "needs input"
|
msg = fmt.Sprintf("input required: %s", header)
|
||||||
case code >= 30 && code < 40:
|
case code >= 30 && code < 40:
|
||||||
msg = "redirect"
|
msg = fmt.Sprintf("redirect: %s", header)
|
||||||
case code >= 40 && code < 50:
|
case code >= 40 && code < 50:
|
||||||
msg = "bad request"
|
msg = fmt.Sprintf("request failed: %s", header)
|
||||||
case code >= 50 && code < 60:
|
case code >= 50 && code < 60:
|
||||||
msg = "server error"
|
msg = fmt.Sprintf("server error: %s", header)
|
||||||
case code >= 60 && code < 70:
|
case code >= 60 && code < 70:
|
||||||
msg = "TLS error"
|
msg = fmt.Sprintf("TLS error: %s", header)
|
||||||
default:
|
default:
|
||||||
msg = "unexpected Status code"
|
msg = fmt.Sprintf("unexpected status code %d: %s", code, header)
|
||||||
}
|
}
|
||||||
return &GeminiError{
|
return &GeminiError{
|
||||||
Msg: msg,
|
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 {
|
func IsGeminiError(err error) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
_url "gemini-grc/common/url"
|
_url "gemini-grc/common/url"
|
||||||
"gemini-grc/config"
|
"gemini-grc/config"
|
||||||
"gemini-grc/logging"
|
"gemini-grc/logging"
|
||||||
|
|
||||||
"github.com/antanst/go_errors"
|
"github.com/antanst/go_errors"
|
||||||
"github.com/guregu/null/v5"
|
"github.com/guregu/null/v5"
|
||||||
)
|
)
|
||||||
@@ -182,7 +183,7 @@ func processData(s snapshot.Snapshot, data []byte) (*snapshot.Snapshot, error) {
|
|||||||
if mimeType == "text/gemini" {
|
if mimeType == "text/gemini" {
|
||||||
validBody, err := BytesToValidUTF8(body)
|
validBody, err := BytesToValidUTF8(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, go_errors.NewError(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
s.GemText = null.StringFrom(validBody)
|
s.GemText = null.StringFrom(validBody)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/antanst/go_errors"
|
||||||
"golang.org/x/text/encoding/charmap"
|
"golang.org/x/text/encoding/charmap"
|
||||||
"golang.org/x/text/encoding/japanese"
|
"golang.org/x/text/encoding/japanese"
|
||||||
"golang.org/x/text/encoding/korean"
|
"golang.org/x/text/encoding/korean"
|
||||||
@@ -24,7 +25,7 @@ func BytesToValidUTF8(input []byte) (string, error) {
|
|||||||
}
|
}
|
||||||
const maxSize = 10 * 1024 * 1024 // 10MB
|
const maxSize = 10 * 1024 * 1024 // 10MB
|
||||||
if len(input) > maxSize {
|
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)
|
// remove NULL byte 0x00 (ReplaceAll accepts slices)
|
||||||
inputNoNull := bytes.ReplaceAll(input, []byte{byte(0)}, []byte{})
|
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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ import (
|
|||||||
errors2 "gemini-grc/common/errors"
|
errors2 "gemini-grc/common/errors"
|
||||||
"gemini-grc/common/linkList"
|
"gemini-grc/common/linkList"
|
||||||
"gemini-grc/common/snapshot"
|
"gemini-grc/common/snapshot"
|
||||||
|
"gemini-grc/common/text"
|
||||||
_url "gemini-grc/common/url"
|
_url "gemini-grc/common/url"
|
||||||
"gemini-grc/config"
|
"gemini-grc/config"
|
||||||
"gemini-grc/logging"
|
"gemini-grc/logging"
|
||||||
|
|
||||||
"github.com/antanst/go_errors"
|
"github.com/antanst/go_errors"
|
||||||
"github.com/guregu/null/v5"
|
"github.com/guregu/null/v5"
|
||||||
)
|
)
|
||||||
@@ -85,7 +87,7 @@ func Visit(url string) (*snapshot.Snapshot, error) {
|
|||||||
|
|
||||||
isValidUTF8 := utf8.ValidString(string(data))
|
isValidUTF8 := utf8.ValidString(string(data))
|
||||||
if isValidUTF8 {
|
if isValidUTF8 {
|
||||||
s.GemText = null.StringFrom(removeNullChars(string(data)))
|
s.GemText = null.StringFrom(text.RemoveNullChars(string(data)))
|
||||||
} else {
|
} else {
|
||||||
s.Data = null.ValueFrom(data)
|
s.Data = null.ValueFrom(data)
|
||||||
}
|
}
|
||||||
@@ -276,8 +278,3 @@ func getGopherPageLinks(content string) []string {
|
|||||||
|
|
||||||
return links
|
return links
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeNullChars(input string) string {
|
|
||||||
// Replace all null characters with an empty string
|
|
||||||
return strings.ReplaceAll(input, "\u0000", "")
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user