diff --git a/packages/connector-jsdom/src/connector.ts b/packages/connector-jsdom/src/connector.ts index 2151a4d7fb5..1bff70fdc7e 100644 --- a/packages/connector-jsdom/src/connector.ts +++ b/packages/connector-jsdom/src/connector.ts @@ -398,7 +398,10 @@ export default class JSDOMConnector implements IConnector { return resolve(result.evaluate); }); - runner.send({ source }); + runner.send({ + options: this._options, + source + }); /* istanbul ignore next */ timeoutId = setTimeout(() => { diff --git a/packages/connector-jsdom/src/evaluate-resource-loader.ts b/packages/connector-jsdom/src/evaluate-resource-loader.ts new file mode 100644 index 00000000000..7a893621884 --- /dev/null +++ b/packages/connector-jsdom/src/evaluate-resource-loader.ts @@ -0,0 +1,64 @@ +import { Requester } from '@hint/utils-connector-tools'; +import { ResourceLoader } from 'jsdom'; +import { NetworkData } from 'hint'; +import { debug as d } from '@hint/utils'; + +const debug: debug.IDebugger = d(__filename); + +export class EvaluateCustomResourceLoader extends ResourceLoader { + private _requester: Requester; + private _baseUrl: string; + + public constructor(options: any, url: string) { + super(); + + this._requester = new Requester(options); + this._baseUrl = url; + } + + public async fetch(url: string): Promise { + if (!url) { + const promise = Promise.resolve(null); + + (promise as any).abort = () => { }; + + return await promise; + } + + const urlAsUrl = new URL(url); + let resourceUrl: string = urlAsUrl.href; + + if (!urlAsUrl.protocol) { + resourceUrl = new URL(resourceUrl, this._baseUrl).href; + } + + let abort: Function; + + const promise = new Promise(async (resolve, reject) => { + abort = reject; + + try { + const resourceNetworkData: NetworkData = await this._requester.get(resourceUrl); + + debug(`resource ${resourceUrl} fetched`); + + return resolve(resourceNetworkData.response.body.rawContent); + } catch (err) { + return reject(err); + } + }); + + /* + * When jsdom is close (because of an exception) it will try to close + * all the pending connections calling to `abort`. + */ + /* istanbul ignore next */ + (promise as any).abort = () => { + const error = new Error('request canceled by user'); + + abort(error); + }; + + return promise; + } +} diff --git a/packages/connector-jsdom/src/evaluate-runner.ts b/packages/connector-jsdom/src/evaluate-runner.ts index d9cea53d300..f10e8835f73 100644 --- a/packages/connector-jsdom/src/evaluate-runner.ts +++ b/packages/connector-jsdom/src/evaluate-runner.ts @@ -4,13 +4,24 @@ import { JSDOM, VirtualConsole } from 'jsdom'; import * as jsdomutils from 'jsdom/lib/jsdom/living/generated/utils'; import { debug as d } from '@hint/utils'; +import { Requester } from '@hint/utils-connector-tools'; import { beforeParse } from './before-parse'; +import { NetworkData } from 'hint'; +import { EvaluateCustomResourceLoader } from './evaluate-resource-loader'; const debug: debug.IDebugger = d(__filename); -const run = async (data: { source: string }) => { - const { source } = data; +const run = async (data: { options: any; source: string }) => { + const { options = {}, source } = data; + const requesterOptions = { + rejectUnauthorized: !options.ignoreHTTPSErrors, + strictSSL: !options.ignoreHTTPSErrors, + ...options.requestOptions + }; + + const requester = new Requester(requesterOptions); + const result = { error: null as Error | null, evaluate: 'result' @@ -28,11 +39,31 @@ const run = async (data: { source: string }) => { debug(err); }); - const jsdom = await JSDOM.fromURL(url, { - beforeParse: beforeParse(url), + let html = ''; + let networkData: NetworkData; + + try { + /* + * we need to request the HTML separately instead of + * just using `JSDOM.fromURL` in order to respect + * user options to ignore HTTPS errors. + */ + networkData = await requester.get(url); + + html = networkData.response.body.content; + } catch (error) { + process.send!({ error }); + + return; + } + const finalUrl = networkData.response.url; + + const jsdom = new JSDOM(html, { + beforeParse: beforeParse(finalUrl), pretendToBeVisual: true, - resources: 'usable', + resources: new EvaluateCustomResourceLoader(requesterOptions, finalUrl), runScripts: 'dangerously', + url: finalUrl, virtualConsole });