Files
openfsd/protocol/fsd_error.go
Reese Norris 57d54d6705 v0.1.0-alpha
Changes:
- Implement bootstrapping library for managing several concurrent internal services
- Refactor concurrency model for connections/logical clients and their associated I/O
- Refactor server context singleton
- Refactor error handling
    - Most errors are now gracefully sent to the FSD client directly encoded as an $ER packet,
      enhancing visibility and debugging
    - Most errors are now rightfully treated as non-fatal
- Refactor package/dependency graph
- Refactor calling conventions/interfaces for many packages
- Refactor database package
- Refactor post office

Features:
- Add VATSIM-esque HTTP/JSON "data feed"
- Add ephemeral in-memory database option
- Add user management REST API
- Add improved web interface
- Add MySQL support (drop SQLite support)
2024-10-07 12:50:39 -07:00

121 lines
3.4 KiB
Go

package protocol
import (
"errors"
"fmt"
"github.com/go-playground/validator/v10"
"strconv"
"strings"
)
// FSDError combines an FSD error with a golang error, so they can be used interchangeably
type ErrorCode int
const (
OkError = iota
CallsignInUseError
CallsignInvalidError
AlreadyRegisteredError
SyntaxError
PDUSourceInvalidError
InvalidLogonError
NoSuchCallsignError
NoFlightPlanError
NoWeatherProfileError
InvalidProtocolRevisionError
RequestedLevelTooHighError
ServerFullError
CertificateSuspendedError
InvalidControlError
InvalidPositionForRatingError
UnauthorizedSoftwareError
ClientAuthenticationResponseTimeoutError
)
var genericErrorMessage = map[ErrorCode]string{
OkError: "OK",
CallsignInUseError: "callsign already in use",
CallsignInvalidError: "Invalid callsign",
AlreadyRegisteredError: "Already registered",
SyntaxError: "Syntax error",
PDUSourceInvalidError: "PDU source invalid",
InvalidLogonError: "Invalid login information",
NoSuchCallsignError: "No such callsign",
NoFlightPlanError: "No flight plan filed",
NoWeatherProfileError: "No weather profile",
InvalidProtocolRevisionError: "Invalid protocol revision",
RequestedLevelTooHighError: "Requested level too high",
ServerFullError: "Server full",
CertificateSuspendedError: "Certificate suspended",
InvalidControlError: "Invalid control",
InvalidPositionForRatingError: "Invalid position for rating",
UnauthorizedSoftwareError: "Unauthorized software",
ClientAuthenticationResponseTimeoutError: "Client authentication response timeout",
}
type FSDError struct {
From string
To string
Code ErrorCode
Param string
Message string
}
func (e *FSDError) Error() string {
return e.Serialize()
}
func (e *FSDError) Serialize() string {
return fmt.Sprintf("$ER%s:%s:%03d:%s:%s%s", e.From, e.To, e.Code, e.Param, e.Message, PacketDelimiter)
}
func ParseNetworkErrorPDU(rawPacket string) (*FSDError, error) {
rawPacket = strings.TrimSuffix(rawPacket, PacketDelimiter)
rawPacket = strings.TrimPrefix(rawPacket, "$ER")
fields := strings.SplitN(rawPacket, Delimiter, 5)
if len(fields) < 5 {
return nil, NewGenericFSDError(SyntaxError, "", "invalid field count")
}
errCode, err := strconv.Atoi(fields[2])
if err != nil {
return nil, NewGenericFSDError(SyntaxError, fields[2], "invalid error code")
}
return &FSDError{
From: fields[0],
To: fields[1],
Code: ErrorCode(errCode),
Param: fields[3],
Message: fields[4],
}, nil
}
func NewGenericFSDError(code ErrorCode, param string, messageContext string) *FSDError {
msg := genericErrorMessage[code]
if messageContext != "" {
msg += fmt.Sprintf(": %s", messageContext)
}
return &FSDError{
From: "server",
To: "unknown",
Code: code,
Param: param,
Message: msg,
}
}
func getFSDErrorFromValidatorErrors(err error) *FSDError {
var validationErrors validator.ValidationErrors
if errors.As(err, &validationErrors) {
if len(validationErrors) < 1 {
return nil
}
return NewGenericFSDError(SyntaxError, "", "validation error: "+validationErrors[0].Error())
}
return nil
}