Skip to content

Commit

Permalink
Fix: Unhandled promise rejections when self-signed in jsdom
Browse files Browse the repository at this point in the history
Close #3128
  • Loading branch information
sarvaje authored and antross committed Oct 18, 2019
1 parent 456294f commit 81d5f84
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 6 deletions.
5 changes: 4 additions & 1 deletion packages/connector-jsdom/src/connector.ts
Expand Up @@ -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(() => {
Expand Down
64 changes: 64 additions & 0 deletions 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<Buffer | null> {
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<Buffer>(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;
}
}
41 changes: 36 additions & 5 deletions packages/connector-jsdom/src/evaluate-runner.ts
Expand Up @@ -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'
Expand All @@ -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
});

Expand Down

0 comments on commit 81d5f84

Please sign in to comment.