Skip to content

Commit 89ddb17

Browse files
committed
better error handling
1 parent a1b7a89 commit 89ddb17

9 files changed

Lines changed: 124 additions & 25 deletions

File tree

bridge_north.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package ronykit
22

3+
import (
4+
"github.com/clubpay/ronykit/internal/errors"
5+
)
6+
37
// Gateway is main component of the EdgeServer. Without Gateway, the EdgeServer is not functional. You can use
48
// some standard bundles in std/bundle path. However, if you need special handling of communication
59
// between your server and the clients you are free to implement your own Gateway.
@@ -52,10 +56,18 @@ func (n *northBridge) OnMessage(conn Conn, msg []byte) {
5256

5357
arg, err := n.gw.Dispatch(ctx, msg)
5458
if err != nil {
55-
n.eh(ctx, err)
59+
n.eh(ctx, errors.Wrap(ErrDispatchFailed, err))
5660
} else {
5761
ctx.execute(arg, n.cr(arg.ContractID))
5862
}
5963

6064
n.releaseCtx(ctx)
6165
}
66+
67+
var (
68+
ErrNoHandler = errors.New("handler is not set for request")
69+
ErrWriteToClosedConn = errors.New("write to closed connection")
70+
ErrDecodeIncomingMessageFailed = errors.New("decoding the incoming message failed")
71+
ErrEncodeOutgoingMessageFailed = errors.New("encoding the outgoing message failed")
72+
ErrDispatchFailed = errors.New("dispatch failed")
73+
)

edge.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ import (
1717
"github.com/jedib0t/go-pretty/v6/text"
1818
)
1919

20-
var errServiceAlreadyRegistered errors.ErrFunc = func(v ...interface{}) error {
21-
return fmt.Errorf("service %s already registered", v...)
22-
}
23-
2420
type contractResolver func(contractID string) Contract
2521

2622
// EdgeServer is the main component of the ronykit. It glues all other components of the
@@ -89,7 +85,7 @@ func (s *EdgeServer) RegisterBundle(b Bundle) *EdgeServer {
8985
// RouteSelector in each desc.Contract.
9086
func (s *EdgeServer) RegisterService(svc Service) *EdgeServer {
9187
if _, ok := s.contracts[svc.Name()]; ok {
92-
panic(errServiceAlreadyRegistered(svc.Name()))
88+
panic(errors.New("service already registered: %s", svc.Name()))
9389
}
9490

9591
s.svc = append(s.svc, svc)

options.go renamed to edge_options.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ func RegisterService(services ...Service) Option {
2727
}
2828
}
2929

30+
// WithErrorHandler registers a global error handler to catch any error that
31+
// happens before EdgeServer can deliver incoming message to the handler, or delivering
32+
// the outgoing message to the client.
33+
// Internal errors are usually wrapped and could be checked for better error handling.
34+
// You can check with errors.Is function to see if the error is one of the following:
35+
// ErrDispatchFailed, ErrWriteToClosedConn, ErrNoHandler
36+
// ErrDecodeIncomingMessageFailed,, ErrEncodeOutgoingMessageFailed
3037
func WithErrorHandler(h ErrHandler) Option {
3138
return func(s *EdgeServer) {
3239
s.eh = h

exmples/simple-rest-server/stub/sampleservice.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/errors/error.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,42 @@
11
package errors
22

3-
type ErrFunc func(v ...interface{}) error
3+
import (
4+
"errors"
5+
"fmt"
6+
)
7+
8+
type qError struct {
9+
top error
10+
down error
11+
}
12+
13+
func (e qError) Error() string {
14+
if e.down == nil {
15+
return e.top.Error()
16+
}
17+
18+
return fmt.Sprintf("%s: %s", e.top, e.down)
19+
}
20+
21+
func (e qError) Is(err error) bool {
22+
return errors.Is(e.top, err)
23+
}
24+
25+
func (e qError) Unwrap() error {
26+
return e.down
27+
}
28+
29+
func New(format string, v ...interface{}) error {
30+
return fmt.Errorf(format, v...)
31+
}
32+
33+
func Wrap(top, down error) error {
34+
if top == nil {
35+
return down
36+
}
37+
38+
return qError{
39+
top: top,
40+
down: down,
41+
}
42+
}

internal/errors/error_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package errors_test
2+
3+
import (
4+
builtinErr "errors"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/clubpay/ronykit/internal/errors"
9+
. "github.com/onsi/ginkgo/v2"
10+
. "github.com/onsi/gomega"
11+
)
12+
13+
var (
14+
errInternal = builtinErr.New("internal error")
15+
errRuntime = builtinErr.New("runtime error")
16+
errEndOfFile = builtinErr.New("end of file")
17+
18+
errWrappedInternal = fmt.Errorf("wrapped error: %w", errInternal)
19+
)
20+
21+
func TestError(t *testing.T) {
22+
RegisterFailHandler(Fail)
23+
RunSpecs(t, "Ronykit Suite")
24+
}
25+
26+
var _ = Describe("WrapError", func() {
27+
we := errors.Wrap(errRuntime, errEndOfFile)
28+
29+
It("Wrap 1", func() {
30+
Expect(builtinErr.Is(we, errRuntime)).To(BeTrue())
31+
Expect(builtinErr.Is(we, errEndOfFile)).To(BeTrue())
32+
Expect(we.Error()).To(BeEquivalentTo("runtime error: end of file"))
33+
})
34+
35+
It("Wrap 2", func() {
36+
wwe := errors.Wrap(errInternal, we)
37+
Expect(builtinErr.Is(wwe, errInternal)).To(BeTrue())
38+
Expect(builtinErr.Is(wwe, errRuntime)).To(BeTrue())
39+
Expect(builtinErr.Is(wwe, errEndOfFile)).To(BeTrue())
40+
Expect(wwe.Error()).To(BeEquivalentTo("internal error: runtime error: end of file"))
41+
})
42+
43+
It("Wrap 3", func() {
44+
wwi := errors.Wrap(errWrappedInternal, we)
45+
Expect(builtinErr.Is(wwi, errWrappedInternal)).To(BeTrue())
46+
Expect(builtinErr.Is(wwi, errInternal)).To(BeTrue())
47+
Expect(builtinErr.Is(wwi, errRuntime)).To(BeTrue())
48+
Expect(builtinErr.Is(wwi, errEndOfFile)).To(BeTrue())
49+
Expect(wwi.Error()).To(BeEquivalentTo("wrapped error: internal error: runtime error: end of file"))
50+
})
51+
})

std/gateway/fasthttp/bundle.go

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/clubpay/ronykit"
1111
"github.com/clubpay/ronykit/internal/common"
12+
"github.com/clubpay/ronykit/internal/errors"
1213
"github.com/clubpay/ronykit/internal/httpmux"
1314
"github.com/clubpay/ronykit/utils"
1415
"github.com/clubpay/ronykit/utils/buf"
@@ -213,13 +214,13 @@ func (b *bundle) wsDispatch(ctx *ronykit.Context, in []byte) (ronykit.ExecuteArg
213214

214215
routeData := b.wsRoutes[inputMsgContainer.GetHdr(b.predicateKey)]
215216
if routeData == nil {
216-
return ronykit.NoExecuteArg, errNoHandler
217+
return ronykit.NoExecuteArg, ronykit.ErrNoHandler
217218
}
218219

219220
msg := routeData.Factory()
220221
err = inputMsgContainer.Fill(msg)
221222
if err != nil {
222-
return ronykit.NoExecuteArg, err
223+
return ronykit.NoExecuteArg, errors.Wrap(ronykit.ErrDecodeIncomingMessageFailed, err)
223224
}
224225

225226
ctx.In().
@@ -278,7 +279,7 @@ func (b *bundle) httpDispatch(ctx *ronykit.Context, in []byte) (ronykit.ExecuteA
278279
b.cors.handle(conn, routeData != nil)
279280

280281
if routeData == nil {
281-
return ronykit.NoExecuteArg, errRouteNotFound
282+
return ronykit.NoExecuteArg, ronykit.ErrNoHandler
282283
}
283284

284285
// Walk over all the query params
@@ -307,7 +308,7 @@ func (b *bundle) httpDispatch(ctx *ronykit.Context, in []byte) (ronykit.ExecuteA
307308

308309
m, err := routeData.Decoder(params, in)
309310
if err != nil {
310-
return ronykit.NoExecuteArg, err
311+
return ronykit.NoExecuteArg, errors.Wrap(ronykit.ErrDecodeIncomingMessageFailed, err)
311312
}
312313

313314
ctx.In().
@@ -375,9 +376,3 @@ func (b *bundle) Shutdown(_ context.Context) error {
375376
func (b *bundle) Subscribe(d ronykit.GatewayDelegate) {
376377
b.d = d
377378
}
378-
379-
var (
380-
errRouteNotFound = fmt.Errorf("route not found")
381-
errNoHandler = fmt.Errorf("no handler for request")
382-
errConnectionClosed = fmt.Errorf("connection closed")
383-
)

std/gateway/fasthttp/conn_ws.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func (w *wsConn) Write(data []byte) (int, error) {
3737
if w.c != nil {
3838
err = w.c.WriteMessage(websocket.TextMessage, data)
3939
} else {
40-
err = errConnectionClosed
40+
err = ronykit.ErrWriteToClosedConn
4141
}
4242
w.Unlock()
4343
if err != nil {

std/gateway/fastws/bundle.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ package fastws
22

33
import (
44
"context"
5-
"fmt"
65
"time"
76

87
"github.com/clubpay/ronykit"
98
"github.com/clubpay/ronykit/internal/common"
9+
"github.com/clubpay/ronykit/internal/errors"
1010
"github.com/panjf2000/gnet/v2"
1111
)
1212

@@ -79,18 +79,18 @@ func (b *bundle) Dispatch(ctx *ronykit.Context, in []byte) (ronykit.ExecuteArg,
7979
inputMsgContainer := b.rpcInFactory()
8080
err := inputMsgContainer.Unmarshal(in)
8181
if err != nil {
82-
return ronykit.NoExecuteArg, err
82+
return ronykit.NoExecuteArg, errors.Wrap(ronykit.ErrDecodeIncomingMessageFailed, err)
8383
}
8484

8585
routeData := b.routes[inputMsgContainer.GetHdr(b.predicateKey)]
8686
if routeData == nil {
87-
return ronykit.NoExecuteArg, errNoHandler
87+
return ronykit.NoExecuteArg, ronykit.ErrNoHandler
8888
}
8989

9090
msg := routeData.Factory()
9191
err = inputMsgContainer.Fill(msg)
9292
if err != nil {
93-
return ronykit.NoExecuteArg, err
93+
return ronykit.NoExecuteArg, errors.Wrap(ronykit.ErrDecodeIncomingMessageFailed, err)
9494
}
9595

9696
ctx.In().
@@ -116,7 +116,7 @@ func (b *bundle) writeFunc(conn ronykit.Conn, e ronykit.Envelope) error {
116116

117117
data, err := outputMsgContainer.Marshal()
118118
if err != nil {
119-
return err
119+
return errors.Wrap(ronykit.ErrEncodeOutgoingMessageFailed, err)
120120
}
121121

122122
_, err = conn.Write(data)
@@ -153,5 +153,3 @@ func (b *bundle) Shutdown(ctx context.Context) error {
153153
func (b *bundle) Subscribe(d ronykit.GatewayDelegate) {
154154
b.d = d
155155
}
156-
157-
var errNoHandler = fmt.Errorf("no handler for request")

0 commit comments

Comments
 (0)