Skip to content

Commit 6f08c2f

Browse files
zhiqiangxufjl
andauthored
rpc: add method to test for subscription support (ethereum#25942)
This adds two ways to check for subscription support. First, one can now check whether the transport method (HTTP/WS/etc.) is capable of subscriptions using the new Client.SupportsSubscriptions method. Second, the error returned by Subscribe can now reliably be tested using this pattern: sub, err := client.Subscribe(...) if errors.Is(err, rpc.ErrNotificationsUnsupported) { // no subscription support } --------- Co-authored-by: Felix Lange <[email protected]>
1 parent 8bbaf88 commit 6f08c2f

File tree

4 files changed

+54
-12
lines changed

4 files changed

+54
-12
lines changed

rpc/client.go

+7
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,13 @@ func (c *Client) Subscribe(ctx context.Context, namespace string, channel interf
538538
return op.sub, nil
539539
}
540540

541+
// SupportsSubscriptions reports whether subscriptions are supported by the client
542+
// transport. When this returns false, Subscribe and related methods will return
543+
// ErrNotificationsUnsupported.
544+
func (c *Client) SupportsSubscriptions() bool {
545+
return !c.isHTTP
546+
}
547+
541548
func (c *Client) newMessage(method string, paramsIn ...interface{}) (*jsonrpcMessage, error) {
542549
msg := &jsonrpcMessage{Version: vsn, ID: c.nextID(), Method: method}
543550
if paramsIn != nil { // prevent sending "params":null

rpc/errors.go

+35-6
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,13 @@ var (
5858
)
5959

6060
const (
61-
errcodeDefault = -32000
62-
errcodeNotificationsUnsupported = -32001
63-
errcodeTimeout = -32002
64-
errcodeResponseTooLarge = -32003
65-
errcodePanic = -32603
66-
errcodeMarshalError = -32603
61+
errcodeDefault = -32000
62+
errcodeTimeout = -32002
63+
errcodeResponseTooLarge = -32003
64+
errcodePanic = -32603
65+
errcodeMarshalError = -32603
66+
67+
legacyErrcodeNotificationsUnsupported = -32001
6768
)
6869

6970
const (
@@ -80,6 +81,34 @@ func (e *methodNotFoundError) Error() string {
8081
return fmt.Sprintf("the method %s does not exist/is not available", e.method)
8182
}
8283

84+
type notificationsUnsupportedError struct{}
85+
86+
func (e notificationsUnsupportedError) Error() string {
87+
return "notifications not supported"
88+
}
89+
90+
func (e notificationsUnsupportedError) ErrorCode() int { return -32601 }
91+
92+
// Is checks for equivalence to another error. Here we define that all errors with code
93+
// -32601 (method not found) are equivalent to notificationsUnsupportedError. This is
94+
// done to enable the following pattern:
95+
//
96+
// sub, err := client.Subscribe(...)
97+
// if errors.Is(err, rpc.ErrNotificationsUnsupported) {
98+
// // server doesn't support subscriptions
99+
// }
100+
func (e notificationsUnsupportedError) Is(other error) bool {
101+
if other == (notificationsUnsupportedError{}) {
102+
return true
103+
}
104+
rpcErr, ok := other.(Error)
105+
if ok {
106+
code := rpcErr.ErrorCode()
107+
return code == -32601 || code == legacyErrcodeNotificationsUnsupported
108+
}
109+
return false
110+
}
111+
83112
type subscriptionNotFoundError struct{ namespace, subscription string }
84113

85114
func (e *subscriptionNotFoundError) ErrorCode() int { return -32601 }

rpc/handler.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -530,10 +530,7 @@ func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage
530530
// handleSubscribe processes *_subscribe method calls.
531531
func (h *handler) handleSubscribe(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage {
532532
if !h.allowSubscribe {
533-
return msg.errorResponse(&internalServerError{
534-
code: errcodeNotificationsUnsupported,
535-
message: ErrNotificationsUnsupported.Error(),
536-
})
533+
return msg.errorResponse(ErrNotificationsUnsupported)
537534
}
538535

539536
// Subscription method name is first argument.

rpc/subscription.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,17 @@ import (
3232
)
3333

3434
var (
35-
// ErrNotificationsUnsupported is returned when the connection doesn't support notifications
36-
ErrNotificationsUnsupported = errors.New("notifications not supported")
35+
// ErrNotificationsUnsupported is returned by the client when the connection doesn't
36+
// support notifications. You can use this error value to check for subscription
37+
// support like this:
38+
//
39+
// sub, err := client.EthSubscribe(ctx, channel, "newHeads", true)
40+
// if errors.Is(err, rpc.ErrNotificationsUnsupported) {
41+
// // Server does not support subscriptions, fall back to polling.
42+
// }
43+
//
44+
ErrNotificationsUnsupported = notificationsUnsupportedError{}
45+
3746
// ErrSubscriptionNotFound is returned when the notification for the given id is not found
3847
ErrSubscriptionNotFound = errors.New("subscription not found")
3948
)

0 commit comments

Comments
 (0)