Skip to content

Commit

Permalink
use errors.Is and errors.As
Browse files Browse the repository at this point in the history
go1.13 added support for this and this patch unwraps some error
selection code.
  • Loading branch information
magiconair committed May 4, 2022
1 parent d8f4dea commit 722c463
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 59 deletions.
52 changes: 23 additions & 29 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ func (c *Client) monitor(ctx context.Context) {
return
}

if err == ua.StatusBadTimeout {
dlog.Print("")
}

// tell the handler the connection is disconnected
c.setState(Disconnected)
dlog.Print("disconnected")
Expand All @@ -268,44 +272,34 @@ func (c *Client) monitor(ctx context.Context) {

dlog.Print("auto-reconnecting")

switch err {
case io.EOF:
switch {
case errors.Is(err, io.EOF):
// the connection has been closed
action = createSecureChannel

case syscall.ECONNREFUSED:
case errors.Is(err, syscall.ECONNREFUSED):
// the connection has been refused by the server
action = abortReconnect

default:
switch x := err.(type) {
case *uacp.Error:
switch ua.StatusCode(x.ErrorCode) {
case ua.StatusBadSecureChannelIDInvalid:
// the secure channel has been rejected by the server
action = createSecureChannel

case ua.StatusBadSessionIDInvalid:
// the session has been rejected by the server
action = recreateSession

case ua.StatusBadSubscriptionIDInvalid:
// the subscription has been rejected by the server
action = transferSubscriptions
case errors.Is(err, ua.StatusBadSecureChannelIDInvalid):
// the secure channel has been rejected by the server
action = createSecureChannel

case ua.StatusBadCertificateInvalid:
// todo(unknownet): recreate server certificate
fallthrough
case errors.Is(err, ua.StatusBadSessionIDInvalid):
// the session has been rejected by the server
action = recreateSession

default:
// unknown error has occured
action = createSecureChannel
}
case errors.Is(err, ua.StatusBadSubscriptionIDInvalid):
// the subscription has been rejected by the server
action = transferSubscriptions

default:
// unknown error has occured
action = createSecureChannel
}
case errors.Is(err, ua.StatusBadCertificateInvalid):
// todo(unknownet): recreate server certificate
fallthrough

default:
// unknown error has occured
action = createSecureChannel
}

c.setState(Disconnected)
Expand Down
13 changes: 5 additions & 8 deletions client_sub.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,18 +135,15 @@ func (c *Client) republishSubscription(ctx context.Context, id uint32, available

debug.Printf("republishing subscription %d", sub.SubscriptionID)
if err := c.sendRepublishRequests(ctx, sub, availableSeq); err != nil {
status, ok := err.(ua.StatusCode)
if !ok {
return err
}

switch status {
case ua.StatusBadSessionIDInvalid:
switch {
case errors.Is(err, ua.StatusBadSessionIDInvalid):
return nil
case ua.StatusBadSubscriptionIDInvalid:
case errors.Is(err, ua.StatusBadSubscriptionIDInvalid):
// todo(fs): do we need to forget the subscription id in this case?
debug.Printf("republish failed since subscription %d is invalid", sub.SubscriptionID)
return errors.Errorf("republish failed since subscription %d is invalid", sub.SubscriptionID)
default:
return err
}
}
return nil
Expand Down
21 changes: 19 additions & 2 deletions errors/errors.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,39 @@
package errors

import (
"errors"

pkg_errors "github.com/pkg/errors"
)

// Prefix is the default error string prefix
const Prefix = "opcua: "

// Errorf is a wrapper for `errors.Errorf`
// Errorf wraps github.com/pig/errors#Errorf`
func Errorf(format string, a ...interface{}) error {
return pkg_errors.Errorf(Prefix+format, a...)
}

// New is a wrapper for `errors.New`
// New wraps github.com/pkg/errors#New
func New(text string) error {
return pkg_errors.New(Prefix + text)
}

// Is wraps errors.Is
func Is(err error, target error) bool {
return errors.Is(err, target)
}

// As wraps errors.As
func As(err error, target interface{}) bool {
return errors.As(err, target)
}

// Unwrap wraps errors.Unwrap
func Unwrap(err error) error {
return errors.Unwrap(err)
}

// Equal returns true if the two errors have the same error message.
//
// todo(fs): the reason we need this function and cannot just use
Expand Down
21 changes: 10 additions & 11 deletions stats/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package stats

import (
"errors"
"expvar"
"io"
"reflect"
Expand Down Expand Up @@ -45,22 +46,20 @@ func (s *Stats) RecordError(err error) {
if err == nil {
return
}
switch err {
case io.EOF:
var code ua.StatusCode
switch {
case errors.Is(err, io.EOF):
s.Error.Add("io.EOF", 1)
case ua.StatusOK:
case errors.Is(err, ua.StatusOK):
s.Error.Add("ua.StatusOK", 1)
case ua.StatusBad:
case errors.Is(err, ua.StatusBad):
s.Error.Add("ua.StatusBad", 1)
case ua.StatusUncertain:
case errors.Is(err, ua.StatusUncertain):
s.Error.Add("ua.StatusUncertain", 1)
case errors.As(err, &code):
s.Error.Add("ua."+ua.StatusCodes[code].Name, 1)
default:
switch x := err.(type) {
case ua.StatusCode:
s.Error.Add("ua."+ua.StatusCodes[x].Name, 1)
default:
s.Error.Add(reflect.TypeOf(err).String(), 1)
}
s.Error.Add(reflect.TypeOf(err).String(), 1)
}
}

Expand Down
4 changes: 3 additions & 1 deletion uacp/conn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"testing"
"time"

"github.com/gopcua/opcua/errors"
"github.com/pascaldekloe/goe/verify"
)

Expand Down Expand Up @@ -56,7 +57,8 @@ func TestConn(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
_, err := Dial(ctx, ep)
if !err.(*net.OpError).Timeout() {
var operr *net.OpError
if errors.As(err, &operr) && !operr.Timeout() {
t.Error(err)
}
})
Expand Down
4 changes: 2 additions & 2 deletions uacp/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ func ResolveEndpoint(endpoint string) (network string, addr *net.TCPAddr, err er

network = "tcp"
addr, err = net.ResolveTCPAddr(network, addrString)
switch err.(type) {
case *net.DNSError:
var dnserr *net.DNSError
if errors.As(err, &dnserr) {
return "", nil, errors.Errorf("could not resolve address %s", addrString)
}
return
Expand Down
5 changes: 5 additions & 0 deletions uacp/uacp.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ func (e *Error) Error() string {
return ua.StatusCode(e.ErrorCode).Error()
}

// Unwrap returns the wrapped error code.
func (e *Error) Unwrap() error {
return ua.StatusCode(e.ErrorCode)
}

type Message struct {
Data []byte
}
Expand Down
3 changes: 2 additions & 1 deletion uasc/secure_channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,8 @@ func (s *SecureChannel) readChunk() (*MessageChunk, error) {
return nil, io.EOF
}
// do not wrap this error since it hides conn error
if _, ok := err.(*uacp.Error); ok {
var uacperr *uacp.Error
if errors.As(err, &uacperr) {
return nil, err
}
if err != nil {
Expand Down
11 changes: 6 additions & 5 deletions uatest/timeout_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"time"

"github.com/gopcua/opcua"
"github.com/gopcua/opcua/errors"
)

const (
Expand Down Expand Up @@ -44,11 +45,11 @@ func connectAndValidate(t *testing.T, c *opcua.Client, ctx context.Context, d ti

elapsed := time.Since(start)

if oe, ok := err.(*net.OpError); ok {
if !oe.Timeout() {
t.Fatalf("got %#v, wanted net.timeoutError", oe.Unwrap())
}
} else {
var oe *net.OpError
switch {
case errors.As(err, &oe) && !oe.Timeout():
t.Fatalf("got %#v, wanted net.timeoutError", oe.Unwrap())
default:
t.Fatalf("got %T, wanted %T", err, net.OpError{})
}

Expand Down

0 comments on commit 722c463

Please sign in to comment.