1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Fetch with a timeout everywhere (#10101)

* Use fetchWithTimeout everywhere
* Memoize getFetchWithTimeout
* Require specified timeout
This commit is contained in:
Erik Marks 2021-01-19 08:41:57 -08:00 committed by GitHub
parent 28078bf81c
commit 7159dd6867
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 70 additions and 44 deletions

View File

@ -3,7 +3,7 @@ import log from 'loglevel'
import BN from 'bn.js'
import createId from '../lib/random-id'
import { bnToHex } from '../lib/util'
import fetchWithTimeout from '../lib/fetch-with-timeout'
import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout'
import {
TRANSACTION_CATEGORIES,
@ -24,9 +24,7 @@ import {
ROPSTEN_CHAIN_ID,
} from './network/enums'
const fetch = fetchWithTimeout({
timeout: 30000,
})
const fetchWithTimeout = getFetchWithTimeout(30000)
/**
* This controller is responsible for retrieving incoming transactions. Etherscan is polled once every block to check
@ -227,7 +225,7 @@ export default class IncomingTransactionsController {
if (fromBlock) {
url += `&startBlock=${parseInt(fromBlock, 10)}`
}
const response = await fetch(url)
const response = await fetchWithTimeout(url)
const parsedResponse = await response.json()
return {

View File

@ -2,6 +2,9 @@ import { ObservableStore } from '@metamask/obs-store'
import log from 'loglevel'
import { normalize as normalizeAddress } from 'eth-sig-util'
import ethUtil from 'ethereumjs-util'
import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout'
const fetchWithTimeout = getFetchWithTimeout(30000)
// By default, poll every 3 minutes
const DEFAULT_INTERVAL = 180 * 1000
@ -34,7 +37,7 @@ export default class TokenRatesController {
const query = `contract_addresses=${pairs}&vs_currencies=${nativeCurrency}`
if (this._tokens.length > 0) {
try {
const response = await window.fetch(
const response = await fetchWithTimeout(
`https://api.coingecko.com/api/v3/simple/token_price/ethereum?${query}`,
)
const prices = await response.json()

View File

@ -1,6 +1,9 @@
import extension from 'extensionizer'
import getFetchWithTimeout from '../../../../shared/modules/fetch-with-timeout'
import resolveEnsToIpfsContentId from './resolver'
const fetchWithTimeout = getFetchWithTimeout(30000)
const supportedTopLevelDomains = ['eth']
export default function setupEnsIpfsResolver({
@ -55,7 +58,9 @@ export default function setupEnsIpfsResolver({
)}.${ipfsGateway}${pathname}${search || ''}${fragment || ''}`
try {
// check if ipfs gateway has result
const response = await window.fetch(resolvedUrl, { method: 'HEAD' })
const response = await fetchWithTimeout(resolvedUrl, {
method: 'HEAD',
})
if (response.status === 200) {
url = resolvedUrl
}

View File

@ -1,4 +1,7 @@
import log from 'loglevel'
import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout'
const fetchWithTimeout = getFetchWithTimeout(30000)
const FIXTURE_SERVER_HOST = 'localhost'
const FIXTURE_SERVER_PORT = 12345
@ -24,7 +27,7 @@ export default class ReadOnlyNetworkStore {
*/
async _init() {
try {
const response = await window.fetch(FIXTURE_SERVER_URL)
const response = await fetchWithTimeout(FIXTURE_SERVER_URL)
if (response.ok) {
this._state = await response.json()
}

View File

@ -1,4 +1,10 @@
const fetchWithTimeout = ({ timeout = 120000 } = {}) => {
import { memoize } from 'lodash'
const getFetchWithTimeout = memoize((timeout) => {
if (!Number.isInteger(timeout) || timeout < 1) {
throw new Error('Must specify positive integer timeout.')
}
return async function _fetch(url, opts) {
const abortController = new window.AbortController()
const { signal } = abortController
@ -18,6 +24,6 @@ const fetchWithTimeout = ({ timeout = 120000 } = {}) => {
throw e
}
}
}
})
export default fetchWithTimeout
export default getFetchWithTimeout

View File

@ -1,14 +1,16 @@
import assert from 'assert'
import nock from 'nock'
import fetchWithTimeout from '../../../app/scripts/lib/fetch-with-timeout'
import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout'
describe('fetchWithTimeout', function () {
describe('getFetchWithTimeout', function () {
it('fetches a url', async function () {
nock('https://api.infura.io').get('/money').reply(200, '{"hodl": false}')
const fetch = fetchWithTimeout()
const response = await (await fetch('https://api.infura.io/money')).json()
const fetchWithTimeout = getFetchWithTimeout(30000)
const response = await (
await fetchWithTimeout('https://api.infura.io/money')
).json()
assert.deepEqual(response, {
hodl: false,
})
@ -20,12 +22,10 @@ describe('fetchWithTimeout', function () {
.delay(2000)
.reply(200, '{"moon": "2012-12-21T11:11:11Z"}')
const fetch = fetchWithTimeout({
timeout: 123,
})
const fetchWithTimeout = getFetchWithTimeout(123)
try {
await fetch('https://api.infura.io/moon').then((r) => r.json())
await fetchWithTimeout('https://api.infura.io/moon').then((r) => r.json())
assert.fail('Request should throw')
} catch (e) {
assert.ok(e)
@ -38,15 +38,20 @@ describe('fetchWithTimeout', function () {
.delay(2000)
.reply(200, '{"moon": "2012-12-21T11:11:11Z"}')
const fetch = fetchWithTimeout({
timeout: 123,
})
const fetchWithTimeout = getFetchWithTimeout(123)
try {
await fetch('https://api.infura.io/moon').then((r) => r.json())
await fetchWithTimeout('https://api.infura.io/moon').then((r) => r.json())
assert.fail('Request should be aborted')
} catch (e) {
assert.deepEqual(e.message, 'Aborted')
}
})
it('throws on invalid timeout', async function () {
assert.throws(() => getFetchWithTimeout(), 'should throw')
assert.throws(() => getFetchWithTimeout(-1), 'should throw')
assert.throws(() => getFetchWithTimeout({}), 'should throw')
assert.throws(() => getFetchWithTimeout(true), 'should throw')
})
})

View File

@ -1,8 +1,10 @@
import { cloneDeep } from 'lodash'
import BigNumber from 'bignumber.js'
import { getStorageItem, setStorageItem } from '../../../lib/storage-helpers'
import { decGWEIToHexWEI } from '../../helpers/utils/conversions.util'
import getFetchWithTimeout from '../../../../shared/modules/fetch-with-timeout'
const fetchWithTimeout = getFetchWithTimeout(30000)
// Actions
const BASIC_GAS_ESTIMATE_LOADING_FINISHED =
@ -97,7 +99,7 @@ export function basicGasEstimatesLoadingFinished() {
async function basicGasPriceQuery() {
const url = `https://api.metaswap.codefi.network/gasPrices`
return await window.fetch(url, {
return await fetchWithTimeout(url, {
headers: {},
referrer: 'https://api.metaswap.codefi.network/gasPrices',
referrerPolicy: 'no-referrer-when-downgrade',

View File

@ -1,5 +1,5 @@
import { getStorageItem, setStorageItem } from '../../../lib/storage-helpers'
import fetchWithTimeout from '../../../../app/scripts/lib/fetch-with-timeout'
import getFetchWithTimeout from '../../../../shared/modules/fetch-with-timeout'
const fetchWithCache = async (
url,
@ -29,8 +29,8 @@ const fetchWithCache = async (
return cachedResponse
}
fetchOptions.headers.set('Content-Type', 'application/json')
const _fetch = timeout ? fetchWithTimeout({ timeout }) : window.fetch
const response = await _fetch(url, {
const fetchWithTimeout = getFetchWithTimeout(timeout)
const response = await fetchWithTimeout(url, {
referrerPolicy: 'no-referrer-when-downgrade',
body: null,
method: 'GET',

View File

@ -1,9 +1,12 @@
// cross-browser connection to extension i18n API
import React from 'react'
import log from 'loglevel'
import * as Sentry from '@sentry/browser'
import getFetchWithTimeout from '../../../../shared/modules/fetch-with-timeout'
const fetchWithTimeout = getFetchWithTimeout(30000)
const warned = {}
const missingMessageErrors = {}
const missingSubstitutionErrors = {}
@ -95,7 +98,7 @@ export const getMessage = (localeCode, localeMessages, key, substitutions) => {
export async function fetchLocale(localeCode) {
try {
const response = await window.fetch(
const response = await fetchWithTimeout(
`./_locales/${localeCode}/messages.json`,
)
return await response.json()
@ -120,7 +123,7 @@ export async function loadRelativeTimeFormatLocaleData(localeCode) {
}
async function fetchRelativeTimeFormatData(languageTag) {
const response = await window.fetch(
const response = await fetchWithTimeout(
`./intl/${languageTag}/relative-time-format-data.json`,
)
return await response.json()

View File

@ -4,6 +4,9 @@ import BigNumber from 'bignumber.js'
import ethUtil from 'ethereumjs-util'
import { DateTime } from 'luxon'
import { addHexPrefix } from '../../../../app/scripts/lib/util'
import getFetchWithTimeout from '../../../../shared/modules/fetch-with-timeout'
const fetchWithTimeout = getFetchWithTimeout(30000)
// formatData :: ( date: <Unix Timestamp> ) -> String
export function formatDate(date, format = "M/d/y 'at' T") {
@ -478,19 +481,17 @@ export async function jsonRpcRequest(rpcUrl, rpcMethod, rpcParams = []) {
headers.Authorization = `Basic ${encodedAuth}`
fetchUrl = `${origin}${pathname}${search}`
}
const jsonRpcResponse = await window
.fetch(fetchUrl, {
method: 'POST',
body: JSON.stringify({
id: Date.now().toString(),
jsonrpc: '2.0',
method: rpcMethod,
params: rpcParams,
}),
headers,
cache: 'default',
})
.then((httpResponse) => httpResponse.json())
const jsonRpcResponse = await fetchWithTimeout(fetchUrl, {
method: 'POST',
body: JSON.stringify({
id: Date.now().toString(),
jsonrpc: '2.0',
method: rpcMethod,
params: rpcParams,
}),
headers,
cache: 'default',
}).then((httpResponse) => httpResponse.json())
if (
!jsonRpcResponse ||