UNPKG

dns-query

Version:

Node & Browser tested, Non-JSON DNS over HTTPS fetching with minimal dependencies.

174 lines (137 loc) 5.17 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.loadJSON = loadJSON; exports.processResolvers = processResolvers; exports.queryDns = queryDns; exports.request = request; var utf8Codec = _interopRequireWildcard(require("utf8-codec"), true); var _base64Codec = require("@leichtgewicht/base64-codec"); var _common = require("./common.js"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /* global XMLHttpRequest, localStorage */ const contentType = 'application/dns-message'; function noop() {} function queryDns() { throw new Error('Only "doh" endpoints are supported in the browser'); } async function loadJSON(url, cache, timeout, abortSignal) { const cacheKey = cache ? cache.localStoragePrefix + cache.name : null; if (cacheKey) { try { const cached = JSON.parse(localStorage.getItem(cacheKey)); if (cached && cached.time > cache.maxTime) { return cached; } } catch (err) {} } const { data } = await requestRaw(url, 'GET', null, timeout, abortSignal); const result = { time: Date.now(), data: JSON.parse(utf8Codec.decode(data)) }; if (cacheKey) { try { localStorage.setItem(cacheKey, JSON.stringify(result)); } catch (err) { result.time = null; } } return result; } function requestRaw(url, method, data, timeout, abortSignal) { return new Promise((resolve, reject) => { const target = new _common.URL(url); if (method === 'GET' && data) { target.search = '?dns=' + _base64Codec.base64URL.decode(data); } const uri = target.toString(); const xhr = new XMLHttpRequest(); xhr.open(method, uri, true); xhr.setRequestHeader('Accept', contentType); if (method === 'POST') { xhr.setRequestHeader('Content-Type', contentType); } xhr.responseType = 'arraybuffer'; xhr.timeout = timeout; xhr.ontimeout = ontimeout; xhr.onreadystatechange = onreadystatechange; xhr.onerror = onerror; xhr.onload = onload; if (method === 'POST') { xhr.send(data); } else { xhr.send(); } if (abortSignal) { abortSignal.addEventListener('abort', onabort); } function ontimeout() { finish(new _common.TimeoutError(timeout)); try { xhr.abort(); } catch (e) {} } function onload() { if (xhr.status !== 200) { finish(new _common.HTTPStatusError(uri, xhr.status, method)); } else { let buf; if (typeof xhr.response === 'string') { buf = utf8Codec.encode(xhr.response); } else if (xhr.response instanceof Uint8Array) { buf = xhr.response; } else if (Array.isArray(xhr.response) || xhr.response instanceof ArrayBuffer) { buf = new Uint8Array(xhr.response); } else { throw new Error('Unprocessable response ' + xhr.response); } finish(null, buf); } } function onreadystatechange() { if (xhr.readyState > 1 && xhr.status !== 200 && xhr.status !== 0) { finish(new _common.HTTPStatusError(uri, xhr.status, method)); try { xhr.abort(); } catch (e) {} } } let finish = function (error, data) { finish = noop; if (abortSignal) { abortSignal.removeEventListener('abort', onabort); } if (error) { resolve({ error, response: xhr }); } else { resolve({ data, response: xhr }); } }; function onerror() { finish(xhr.status === 200 ? new Error('Inexplicable XHR Error') : new _common.HTTPStatusError(uri, xhr.status, method)); } function onabort() { finish(new _common.AbortError()); try { xhr.abort(); } catch (e) {} } }); } function request(url, method, packet, timeout, abortSignal) { return requestRaw(url, method, packet, timeout, abortSignal); } function processResolvers(resolvers) { return resolvers.filter(resolver => resolver.cors || resolver.endpoint.cors); }