forked from ipfs/js-ipfs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: limit concurrent HTTP requests in browser
Adds limit of concurrent HTTP requests sent to remote API by dns and preload calls when running in web browser contexts. Browsers limit connections per host (~6). This change mitigates the problem of expensive and long running calls of one type exhausting connection pool for other uses. It additionally limits the number of DNS lookup calls by introducing time-bound cache with eviction rules following what browser already do. This is similar to: libp2p/js-libp2p-delegated-content-routing#12 License: MIT Signed-off-by: Marcin Rataj <lidel@lidel.org>
- Loading branch information
Showing
3 changed files
with
42 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,57 @@ | ||
/* global self */ | ||
/* eslint-env browser */ | ||
'use strict' | ||
|
||
const TLRU = require('../../utils/tlru') | ||
const { default: PQueue } = require('p-queue') | ||
|
||
// Avoid sending multiple queries for the same hostname by caching results | ||
const cache = new TLRU(1000) | ||
// TODO: /api/v0/dns does not return TTL yet: https://github.com/ipfs/go-ipfs/issues/5884 | ||
// However we know browsers themselves cache DNS records for at least 1 minute, | ||
// which acts a provisional default ttl: https://stackoverflow.com/a/36917902/11518426 | ||
const ttl = 60 * 1000 | ||
|
||
// browsers limit concurrent connections per host, | ||
// we don't want preload calls to exhaust the limit (~6) | ||
const _httpQueue = new PQueue({ concurrency: 4 }) | ||
|
||
function unpackResponse (domain, response, callback) { | ||
if (response.Path) { | ||
return callback(null, response.Path) | ||
} else { | ||
const err = new Error(response.Message) | ||
return callback(err) | ||
} | ||
} | ||
|
||
module.exports = (domain, opts, callback) => { | ||
if (typeof opts === 'function') { | ||
callback = opts | ||
opts = {} | ||
} | ||
|
||
opts = opts || {} | ||
|
||
domain = encodeURIComponent(domain) | ||
let url = `https://ipfs.io/api/v0/dns?arg=${domain}` | ||
|
||
if (cache.has(domain)) { | ||
const response = cache.get(domain) | ||
return unpackResponse(domain, response, callback) | ||
} | ||
|
||
let url = `https://ipfs.io/api/v0/dns?arg=${domain}` | ||
Object.keys(opts).forEach(prop => { | ||
url += `&${encodeURIComponent(prop)}=${encodeURIComponent(opts[prop])}` | ||
}) | ||
|
||
self.fetch(url, { mode: 'cors' }) | ||
_httpQueue.add(() => fetch(url, { mode: 'cors' }) | ||
.then((response) => { | ||
return response.json() | ||
}) | ||
.then((response) => { | ||
if (response.Path) { | ||
return callback(null, response.Path) | ||
} else { | ||
return callback(new Error(response.Message)) | ||
} | ||
cache.set(domain, response, ttl) | ||
return unpackResponse(domain, response, callback) | ||
}) | ||
.catch((error) => { | ||
callback(error) | ||
}) | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters