edge-core-js
Version:
Edge account & wallet management library
111 lines (91 loc) • 3.01 kB
JavaScript
function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }import { makeLocalStorageDisklet } from 'disklet'
import { scrypt } from '../../util/crypto/scrypt'
import { fetchCorsProxy } from './fetch-cors-proxy'
// Only try CORS proxy/bridge techniques up to 5 times
const MAX_CORS_FAILURE_COUNT = 5
// A map of domains that failed CORS and succeeded via the CORS proxy server
const hostnameProxyWhitelist = new Set()
// A map of domains that failed all CORS techniques and should not re-attempt CORS techniques
const hostnameCorsProxyBlacklist = new Map()
/**
* Extracts the io functions we need from the browser.
*/
export function makeBrowserIo() {
if (typeof window === 'undefined') {
throw new Error('No `window` object')
}
if (window.crypto == null || window.crypto.getRandomValues == null) {
throw new Error('No secure random number generator in this browser')
}
const io = {
// Crypto:
random: size => {
const out = new Uint8Array(size)
window.crypto.getRandomValues(out)
return out
},
scrypt,
// Local io:
disklet: makeLocalStorageDisklet(window.localStorage, {
prefix: 'airbitz'
}),
// Networking:
async fetch(
uri,
opts
) {
const { corsBypass = 'auto' } = _nullishCoalesce(opts, () => ( {}))
if (corsBypass === 'always') {
return await fetchCorsProxy(uri, opts)
}
if (corsBypass === 'never') {
return await window.fetch(uri, opts)
}
const { hostname } = new URL(uri)
const corsFailureCount = _nullishCoalesce(hostnameCorsProxyBlacklist.get(hostname), () => ( 0))
let doFetch = true
const doFetchCors = true
if (
corsFailureCount < MAX_CORS_FAILURE_COUNT &&
hostnameProxyWhitelist.has(hostname)
) {
// Proactively use fetchCorsProxy for any hostnames added to whitelist:
doFetch = false
}
let errorToThrow
if (doFetch) {
try {
// Attempt regular fetch:
return await window.fetch(uri, opts)
} catch (error) {
// If we exhaust attempts to use CORS-safe fetch, then throw the error:
if (corsFailureCount >= MAX_CORS_FAILURE_COUNT) {
throw error
}
errorToThrow = error
}
}
if (doFetchCors) {
try {
const response = await fetchCorsProxy(uri, opts)
hostnameProxyWhitelist.add(hostname)
return response
} catch (error) {
if (errorToThrow == null) errorToThrow = error
}
}
// We failed all CORS techniques, so track attempts
hostnameCorsProxyBlacklist.set(hostname, corsFailureCount + 1)
// Throw the error from the first fetch instead of the one from
// proxy server.
throw errorToThrow
},
async fetchCors(
uri,
opts
) {
return await io.fetch(uri, opts)
}
}
return io
}