Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/controller-utils/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Allow overriding `isServiceFailure` in `createServicePolicy` ([#9123](https://github.com/MetaMask/core/pull/9123))

### Changed

- Bump `@metamask/utils` from `^11.9.0` to `^11.11.0` ([#9074](https://github.com/MetaMask/core/pull/9074))
Expand Down
7 changes: 6 additions & 1 deletion packages/controller-utils/src/create-service-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export type CreateServicePolicyOptions = {
* regarded as degraded (affecting when `onDegraded` is called).
*/
degradedThreshold?: number;
/**
* Predicate function for when an error should be considered a service failure.
*/
isServiceFailure?: (error: unknown) => boolean;
/**
* The maximum number of times that the service is allowed to fail before
* pausing further retries.
Expand Down Expand Up @@ -189,7 +193,7 @@ export const DEFAULT_CIRCUIT_BREAK_DURATION = 30 * 60 * 1000;
*/
export const DEFAULT_DEGRADED_THRESHOLD = 5_000;

const isServiceFailure = (error: unknown): boolean => {
const defaultIsServiceFailure = (error: unknown): boolean => {
if (
typeof error === 'object' &&
error !== null &&
Expand Down Expand Up @@ -283,6 +287,7 @@ export function createServicePolicy(
circuitBreakDuration = DEFAULT_CIRCUIT_BREAK_DURATION,
degradedThreshold = DEFAULT_DEGRADED_THRESHOLD,
backoff = new ExponentialBackoff(),
isServiceFailure = defaultIsServiceFailure,
} = options;

let availabilityStatus: AvailabilityStatus = AVAILABILITY_STATUSES.Unknown;
Expand Down
1 change: 1 addition & 0 deletions packages/network-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The constructor argument `isRpcFailoverEnabled` is no longer available.
- `RemoteFeatureFlagController:stateChange` and `RemoteFeatureFlagController:getState` are now required.
- Drop `async-mutex` dependency, which was no longer used in source ([#9064](https://github.com/MetaMask/core/pull/9064))
- Consider all Infura HTTP errors as service failures except `400` and `429` ([#9123](https://github.com/MetaMask/core/pull/9123))

### Removed

Expand Down
36 changes: 33 additions & 3 deletions packages/network-controller/src/rpc-service/rpc-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,13 @@
*/
logger?: Pick<Logger, 'warn'>;
/**
* Options to pass to `createServicePolicy`. Note that `retryFilterPolicy` is
* not accepted, as it is overwritten. See {@link createServicePolicy}.
* Options to pass to `createServicePolicy`. Note that `retryFilterPolicy` and `isServiceFailure`
* are not accepted, as they are overwritten. See {@link createServicePolicy}.
*/
policyOptions?: Omit<CreateServicePolicyOptions, 'retryFilterPolicy'>;
policyOptions?: Omit<
CreateServicePolicyOptions,
'retryFilterPolicy' | 'isServiceFailure'
>;
/**
* A function that checks if the user is currently offline. If it returns true,
* connection errors will not be retried, preventing degraded and break
Expand Down Expand Up @@ -284,6 +287,30 @@
return strippedUrl;
}

const INFURA_NON_FAILURE_HTTP_STATUS_CODES = [400, 429];

/**
* Predicate function that determines if an error from Infura is treated as a service failure.
*
* @param error - The error.
* @returns True if the error should be treated as a service policy failure. Most errors are treated like failures,
* with the exception of certain HTTP status codes.
*/
function isServiceFailureInfura(error: unknown) {

Check failure on line 299 in packages/network-controller/src/rpc-service/rpc-service.ts

View workflow job for this annotation

GitHub Actions / Lint, build, and test / Lint (lint:eslint)

Missing return type on function
if (
typeof error === 'object' &&
error !== null &&
hasProperty(error, 'httpStatus') &&
typeof error.httpStatus === 'number'
) {
return !INFURA_NON_FAILURE_HTTP_STATUS_CODES.includes(error.httpStatus);
}

// If the error is not an object, or doesn't have a numeric code property,
// consider it a service failure (e.g., network errors, timeouts, etc.)
return true;
}

/**
* This class is responsible for making a request to an endpoint that implements
* the JSON-RPC protocol. It is designed to gracefully handle network and server
Expand Down Expand Up @@ -368,10 +395,13 @@
this.endpointUrl = stripCredentialsFromUrl(normalizedUrl);
this.#logger = logger;

const isInfura = normalizedUrl.hostname.endsWith('infura.io');

Check failure

Code scanning / CodeQL

Incomplete URL substring sanitization High

'
infura.io
' may be preceded by an arbitrary host name.
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

this.#policy = createServicePolicy({
maxRetries: DEFAULT_MAX_RETRIES,
maxConsecutiveFailures: DEFAULT_MAX_CONSECUTIVE_FAILURES,
...policyOptions,
isServiceFailure: isInfura ? isServiceFailureInfura : undefined,
retryFilterPolicy: handleWhen((error) => {
// If user is offline, don't retry any errors
// This prevents degraded/break callbacks from being triggered
Expand Down
Loading