rpc: add method to test for subscription support (#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 <fjl@twurst.com>
This commit is contained in:
parent
8bbaf882a6
commit
6f08c2f3f1
@ -538,6 +538,13 @@ func (c *Client) Subscribe(ctx context.Context, namespace string, channel interf
|
||||
return op.sub, nil
|
||||
}
|
||||
|
||||
// SupportsSubscriptions reports whether subscriptions are supported by the client
|
||||
// transport. When this returns false, Subscribe and related methods will return
|
||||
// ErrNotificationsUnsupported.
|
||||
func (c *Client) SupportsSubscriptions() bool {
|
||||
return !c.isHTTP
|
||||
}
|
||||
|
||||
func (c *Client) newMessage(method string, paramsIn ...interface{}) (*jsonrpcMessage, error) {
|
||||
msg := &jsonrpcMessage{Version: vsn, ID: c.nextID(), Method: method}
|
||||
if paramsIn != nil { // prevent sending "params":null
|
||||
|
@ -58,12 +58,13 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
errcodeDefault = -32000
|
||||
errcodeNotificationsUnsupported = -32001
|
||||
errcodeTimeout = -32002
|
||||
errcodeResponseTooLarge = -32003
|
||||
errcodePanic = -32603
|
||||
errcodeMarshalError = -32603
|
||||
errcodeDefault = -32000
|
||||
errcodeTimeout = -32002
|
||||
errcodeResponseTooLarge = -32003
|
||||
errcodePanic = -32603
|
||||
errcodeMarshalError = -32603
|
||||
|
||||
legacyErrcodeNotificationsUnsupported = -32001
|
||||
)
|
||||
|
||||
const (
|
||||
@ -80,6 +81,34 @@ func (e *methodNotFoundError) Error() string {
|
||||
return fmt.Sprintf("the method %s does not exist/is not available", e.method)
|
||||
}
|
||||
|
||||
type notificationsUnsupportedError struct{}
|
||||
|
||||
func (e notificationsUnsupportedError) Error() string {
|
||||
return "notifications not supported"
|
||||
}
|
||||
|
||||
func (e notificationsUnsupportedError) ErrorCode() int { return -32601 }
|
||||
|
||||
// Is checks for equivalence to another error. Here we define that all errors with code
|
||||
// -32601 (method not found) are equivalent to notificationsUnsupportedError. This is
|
||||
// done to enable the following pattern:
|
||||
//
|
||||
// sub, err := client.Subscribe(...)
|
||||
// if errors.Is(err, rpc.ErrNotificationsUnsupported) {
|
||||
// // server doesn't support subscriptions
|
||||
// }
|
||||
func (e notificationsUnsupportedError) Is(other error) bool {
|
||||
if other == (notificationsUnsupportedError{}) {
|
||||
return true
|
||||
}
|
||||
rpcErr, ok := other.(Error)
|
||||
if ok {
|
||||
code := rpcErr.ErrorCode()
|
||||
return code == -32601 || code == legacyErrcodeNotificationsUnsupported
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type subscriptionNotFoundError struct{ namespace, subscription string }
|
||||
|
||||
func (e *subscriptionNotFoundError) ErrorCode() int { return -32601 }
|
||||
|
@ -530,10 +530,7 @@ func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage
|
||||
// handleSubscribe processes *_subscribe method calls.
|
||||
func (h *handler) handleSubscribe(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage {
|
||||
if !h.allowSubscribe {
|
||||
return msg.errorResponse(&internalServerError{
|
||||
code: errcodeNotificationsUnsupported,
|
||||
message: ErrNotificationsUnsupported.Error(),
|
||||
})
|
||||
return msg.errorResponse(ErrNotificationsUnsupported)
|
||||
}
|
||||
|
||||
// Subscription method name is first argument.
|
||||
|
@ -32,8 +32,17 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotificationsUnsupported is returned when the connection doesn't support notifications
|
||||
ErrNotificationsUnsupported = errors.New("notifications not supported")
|
||||
// ErrNotificationsUnsupported is returned by the client when the connection doesn't
|
||||
// support notifications. You can use this error value to check for subscription
|
||||
// support like this:
|
||||
//
|
||||
// sub, err := client.EthSubscribe(ctx, channel, "newHeads", true)
|
||||
// if errors.Is(err, rpc.ErrNotificationsUnsupported) {
|
||||
// // Server does not support subscriptions, fall back to polling.
|
||||
// }
|
||||
//
|
||||
ErrNotificationsUnsupported = notificationsUnsupportedError{}
|
||||
|
||||
// ErrSubscriptionNotFound is returned when the notification for the given id is not found
|
||||
ErrSubscriptionNotFound = errors.New("subscription not found")
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user