Merge pull request #1190 from strukturag/simplify-error-type

Simplify error type checks.
This commit is contained in:
Joachim Bauch 2026-02-05 09:59:33 +01:00 committed by GitHub
commit 0b46f9d17c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 140 additions and 35 deletions

View file

@ -54,6 +54,7 @@ import (
"github.com/strukturag/nextcloud-spreed-signaling/config"
"github.com/strukturag/nextcloud-spreed-signaling/container"
"github.com/strukturag/nextcloud-spreed-signaling/geoip"
"github.com/strukturag/nextcloud-spreed-signaling/internal"
"github.com/strukturag/nextcloud-spreed-signaling/log"
"github.com/strukturag/nextcloud-spreed-signaling/proxy"
"github.com/strukturag/nextcloud-spreed-signaling/session"
@ -1224,8 +1225,7 @@ func (s *ProxyServer) processCommand(ctx context.Context, client *ProxyClient, s
defer cancel()
if err := publisher.PublishRemote(ctx2, session.PublicId(), cmd.Hostname, cmd.Port, cmd.RtcpPort); err != nil {
var je *janusapi.ErrorMsg
if !errors.As(err, &je) || je.Err.Code != janusapi.JANUS_VIDEOROOM_ERROR_ID_EXISTS {
if je, ok := internal.AsErrorType[*janusapi.ErrorMsg](err); !ok || je.Err.Code != janusapi.JANUS_VIDEOROOM_ERROR_ID_EXISTS {
s.logger.Printf("Error publishing %s %s to remote %s (port=%d, rtcpPort=%d): %s", publisher.StreamType(), cmd.ClientId, cmd.Hostname, cmd.Port, cmd.RtcpPort, err)
session.sendMessage(message.NewWrappedErrorServerMessage(err))
return

View file

@ -22,11 +22,12 @@
package config
import (
"errors"
"os"
"regexp"
"github.com/dlintw/goconf"
"github.com/strukturag/nextcloud-spreed-signaling/internal"
)
var (
@ -71,8 +72,7 @@ func GetStringOptions(config *goconf.ConfigFile, section string, ignoreErrors bo
continue
}
var ge goconf.GetError
if errors.As(err, &ge) && ge.Reason == goconf.OptionNotFound {
if ge, ok := internal.AsErrorType[goconf.GetError](err); ok && ge.Reason == goconf.OptionNotFound {
// Skip options from "default" section.
continue
}

50
internal/as_error.go Normal file
View file

@ -0,0 +1,50 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2026 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package internal
import (
"errors"
)
// AsErrorType finds the first error in err's tree that matches the type E,
// and if one is found, returns that error value and true. Otherwise, it
// returns the zero value of E and false.
//
// The tree consists of err itself, followed by the errors obtained by
// repeatedly calling its Unwrap() error or Unwrap() []error method.
// When err wraps multiple errors, AsErrorType examines err followed by a
// depth-first traversal of its children.
//
// An error err matches the type E if the type assertion err.(E) holds,
// or if the error has a method As(any) bool such that err.As(target)
// returns true when target is a non-nil *E. In the latter case, the As
// method is responsible for setting target.
func AsErrorType[E error](err error) (E, bool) {
var e E
if err == nil {
return e, false
} else if errors.As(err, &e) {
return e, true
}
return e, false
}

61
internal/as_error_test.go Normal file
View file

@ -0,0 +1,61 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2026 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package internal
import (
"errors"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
type testError struct{}
func (e testError) Error() string {
return "test error"
}
func TestAsErrorType(t *testing.T) {
t.Parallel()
assert := assert.New(t)
if e, ok := AsErrorType[*testError](nil); assert.False(ok) {
assert.Nil(e)
}
err1 := &testError{}
if e, ok := AsErrorType[*testError](err1); assert.True(ok) {
assert.Same(err1, e)
}
err2 := errors.New("other error")
if e, ok := AsErrorType[*testError](err2); assert.False(ok) {
assert.Nil(e)
}
err3 := fmt.Errorf("wrapped error: %w", err1)
if e, ok := AsErrorType[*testError](err3); assert.True(ok) {
assert.Same(err1, e)
}
}

View file

@ -831,9 +831,10 @@ func (b *BackendServer) startDialout(ctx context.Context, roomid string, backend
response, err := b.startDialoutInSession(ctx, session, roomid, backend, backendUrl, request)
if err != nil {
b.logger.Printf("Error starting dialout request %+v in session %s: %+v", request.Dialout, session.PublicId(), err)
var e *api.Error
if sessionError == nil && errors.As(err, &e) {
sessionError = e
if sessionError == nil {
if e, ok := internal.AsErrorType[*api.Error](err); ok {
sessionError = e
}
}
continue
}

View file

@ -469,8 +469,8 @@ func (c *FederationClient) writePump() {
func (c *FederationClient) closeWithError(err error) {
c.Close()
var e *api.Error
if !errors.As(err, &e) {
e, ok := internal.AsErrorType[*api.Error](err)
if !ok {
e = api.NewError("federation_error", err.Error())
}

View file

@ -1844,39 +1844,31 @@ func (h *Hub) processRoom(sess Session, message *api.ClientMessage) {
if session.UserId() == "" && client == nil {
h.startWaitAnonymousSessionRoom(session)
}
var ae *api.Error
if errors.As(err, &ae) {
if ae, ok := internal.AsErrorType[*api.Error](err); ok {
session.SendMessage(message.NewErrorServerMessage(ae))
return
}
var details any
var ce *tls.CertificateVerificationError
if errors.As(err, &ce) {
if ce, ok := internal.AsErrorType[*tls.CertificateVerificationError](err); ok {
details = map[string]string{
"code": "certificate_verification_error",
"message": ce.Error(),
}
}
var ne net.Error
if details == nil && errors.As(err, &ne) {
} else if ne, ok := internal.AsErrorType[net.Error](err); ok {
details = map[string]string{
"code": "network_error",
"message": ne.Error(),
}
}
if details == nil {
var we websocket.HandshakeError
if errors.Is(err, websocket.ErrBadHandshake) {
details = map[string]string{
"code": "network_error",
"message": err.Error(),
}
} else if errors.As(err, &we) {
details = map[string]string{
"code": "network_error",
"message": we.Error(),
}
} else if errors.Is(err, websocket.ErrBadHandshake) {
details = map[string]string{
"code": "network_error",
"message": err.Error(),
}
} else if we, ok := internal.AsErrorType[websocket.HandshakeError](err); ok {
details = map[string]string{
"code": "network_error",
"message": we.Error(),
}
}

View file

@ -22,19 +22,20 @@
package test
import (
"errors"
"os"
"runtime"
"syscall"
"github.com/strukturag/nextcloud-spreed-signaling/internal"
)
func IsErrorAddressAlreadyInUse(err error) bool {
var eOsSyscall *os.SyscallError
if !errors.As(err, &eOsSyscall) {
eOsSyscall, ok := internal.AsErrorType[*os.SyscallError](err)
if !ok {
return false
}
var errErrno syscall.Errno // doesn't need a "*" (ptr) because it's already a ptr (uintptr)
if !errors.As(eOsSyscall, &errErrno) {
errErrno, ok := internal.AsErrorType[syscall.Errno](eOsSyscall)
if !ok {
return false
}
if errErrno == syscall.EADDRINUSE {