Skip to content

Commit 80189e6

Browse files
committed
v1.2.6
1 parent 2d3390a commit 80189e6

File tree

8 files changed

+154
-194
lines changed

8 files changed

+154
-194
lines changed

changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
# Change log
2+
### [1.2.6] - 2020-06-18
3+
#### Changes
4+
- Updated for Puppeteer's v4.0.0 [breaking changes](https://github.com/puppeteer/puppeteer/releases/tag/v4.0.0) ([#22](https://github.com/Cuadrix/puppeteer-page-proxy/issues/22), [#23](https://github.com/Cuadrix/puppeteer-page-proxy/issues/23))
5+
- Modified cookie handling to fix ([#20](https://github.com/Cuadrix/puppeteer-page-proxy/issues/20)) among other cookie related errors
26
### [1.2.5] - 2020-05-21
37
#### Changes
48
- Added ability to override requests

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "puppeteer-page-proxy",
33
"description": "Additional Node.js module to use with 'puppeteer' for setting proxies per page basis.",
4-
"version": "1.2.5",
4+
"version": "1.2.6",
55
"author": "Cuadrix <[email protected]> (https://github.com/Cuadrix)",
66
"homepage": "https://github.com/Cuadrix/puppeteer-page-proxy",
77
"main": "./src/index.js",
@@ -22,7 +22,7 @@
2222
],
2323
"license": "MIT",
2424
"dependencies": {
25-
"got": "^11.1.4",
25+
"got": "^11.3.0",
2626
"http-proxy-agent": "^4.0.1",
2727
"https-proxy-agent": "^5.0.0",
2828
"socks-proxy-agent": "^5.0.0",

src/core/lookup.js

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,39 @@
11
const lookup = async (page, lookupService = "https://api.ipify.org?format=json", isJSON = true, timeout = 30000) => {
2-
const XMLHttpRequest = async () => {
2+
const doLookup = async () => {
33
return await page.evaluate((lookupService, timeout, isJSON) => {
4-
return new Promise(resolve => {
5-
var req = new XMLHttpRequest();
6-
req.open("GET", lookupService, true);
4+
return new Promise((resolve) => {
5+
const req = new XMLHttpRequest();
76
req.timeout = timeout;
87
req.onload = () => {
98
if (req.status >= 200 && req.status <= 299) {
109
resolve(isJSON ? JSON.parse(req.responseText) : req.responseText);
1110
} else {
12-
resolve(xhrFailed(`Request from [${window.location.href.slice(0, -1)}] to [${lookupService}] failed with status code ${req.status}`));
11+
resolve(onLookupFailed(`Request from ${window.location.href} to ${lookupService} failed with status code ${req.status}`));
1312
}
1413
};
15-
req.ontimeout = e => {
16-
resolve(xhrFailed(`Request from [${window.location.href.slice(0, -1)}] to [${lookupService}] timed out -> ${req.timeout} ms`));
14+
req.ontimeout = (error) => {
15+
resolve(onLookupFailed(`Request from ${window.location.href} to ${lookupService} timed out -> ${req.timeout} ms`));
1716
};
17+
req.open("GET", lookupService, true);
1818
req.send();
1919
});
2020
}, lookupService, timeout, isJSON);
2121
};
2222
try {
2323
await page.setBypassCSP(true);
24-
const errName = "xhrFailed";
25-
if (!page._pageBindings.has(errName)) {
26-
await page.exposeFunction(errName, reason => {
24+
const functionName = "onLookupFailed";
25+
if (!page._pageBindings.has(functionName)) {
26+
await page.exposeFunction(functionName, (reason) => {
2727
console.error(reason);
2828
return;
2929
});
3030
}
31-
return await XMLHttpRequest();
31+
return await doLookup();
3232
} catch(error) {
33-
if (error.message === "Execution context was destroyed, most likely because of a navigation." || error.message === "Execution context was destroyed.")
34-
return await XMLHttpRequest();
33+
if (error.message.startsWith("Execution context was destroyed")) {
34+
return await doLookup();
35+
}
3536
}
3637
};
38+
3739
module.exports = lookup;

src/core/proxy.js

Lines changed: 73 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,81 @@
11
const request = require("got");
2-
const {type} = require("../lib/types");
3-
const {getCookies, cookieStore} = require("../lib/cookies");
2+
const type = require("../lib/types");
3+
const cookieJar = require("../lib/cookies");
44
const {setOverrides, setHeaders, setAgent} = require("../lib/options");
55

6-
const useProxy = async (target, proxy) => {
7-
// Listener responsible for applying proxy
8-
const $puppeteerPageProxyHandler = async req => {
9-
endpoint = req._client._connection._url;
10-
targetId = req._frame._id;
11-
const cookieJar = cookieStore(
12-
await getCookies(endpoint, targetId)
13-
);
14-
const options = {
15-
cookieJar,
16-
method: req.method(),
17-
body: req.postData(),
18-
headers: setHeaders(req),
19-
agent: setAgent(proxy),
20-
responseType: "buffer",
21-
throwHttpErrors: false
22-
};
23-
try {
24-
const res = await request(req.url(), options);
25-
await req.respond({
26-
status: res.statusCode,
27-
headers: res.headers,
28-
body: res.body
29-
});
30-
} catch(error) {
31-
await req.abort();
32-
}
33-
};
34-
// Remove existing listener for reassigning proxy of current page
35-
const removeRequestListener = (page, listenerName) => {
36-
const listeners = page.listeners("request");
37-
for (let i = 0; i < listeners.length; i++) {
38-
if (listeners[i].name === listenerName) {
39-
page.removeListener("request", listeners[i]);
40-
}
41-
}
6+
// Responsible for applying proxy
7+
const proxyHandler = async (req, proxy) => {
8+
const options = {
9+
cookieJar,
10+
method: req.method(),
11+
body: req.postData(),
12+
headers: setHeaders(req),
13+
agent: setAgent(proxy),
14+
responseType: "buffer",
15+
maxRedirects: 15,
16+
throwHttpErrors: false
4217
};
43-
// Proxy per request
44-
if (target.constructor.name.indexOf("Request")!=-1) {
45-
if (type(proxy) == "object") {
46-
target = setOverrides(target, proxy);
47-
proxy = proxy.proxy;
48-
}
49-
await $puppeteerPageProxyHandler(target);
50-
// Page-wide proxy
51-
} else if (target.constructor.name === "Page") {
52-
if (type(proxy) == "object") {
53-
proxy = proxy.proxy;
18+
try {
19+
const res = await request(req.url(), options);
20+
await req.respond({
21+
status: res.statusCode,
22+
headers: res.headers,
23+
body: res.body
24+
});
25+
} catch(error) {await req.abort()}
26+
};
27+
28+
// For reassigning proxy of page
29+
const removeRequestListener = (page, listenerName) => {
30+
const eventName = "request";
31+
const listeners = page.eventsMap.get(eventName);
32+
if (listeners) {
33+
const i = listeners.findIndex((listener) => {
34+
return listener.name === listenerName
35+
});
36+
listeners.splice(i, 1);
37+
if (!listeners.length) {
38+
page.eventsMap.delete(eventName);
5439
}
55-
await target.setRequestInterception(true);
56-
removeRequestListener(target, "$puppeteerPageProxyHandler");
57-
if (proxy) {
58-
target.on("request", $puppeteerPageProxyHandler);
59-
} else {
60-
await target.setRequestInterception(false);
40+
}
41+
};
42+
43+
// Calls this if request object passed
44+
const proxyPerRequest = async (req, data) => {
45+
let proxy, overrides;
46+
// Separate proxy and overrides
47+
if (type(data) === "object") {
48+
if (Object.keys(data).length !== 0) {
49+
proxy = data.proxy;
50+
delete data.proxy;
51+
overrides = data;
6152
}
53+
} else {proxy = data}
54+
req = setOverrides(req, overrides);
55+
// Skip request if proxy omitted
56+
if (proxy) {await proxyHandler(req, proxy)}
57+
else {req.continue(overrides)}
58+
};
59+
60+
// Calls this if page object passed
61+
const proxyPerPage = async (page, proxy) => {
62+
await page.setRequestInterception(true);
63+
removeRequestListener(page, "$ppp");
64+
if (proxy) {
65+
page.on("request", $ppp = async (req) => {
66+
await proxyHandler(req, proxy);
67+
});
68+
} else {await page.setRequestInterception(false)}
69+
};
70+
71+
// Main function
72+
const useProxy = async (target, data) => {
73+
const targetType = target.constructor.name;
74+
if (targetType === "HTTPRequest") {
75+
await proxyPerRequest(target, data);
76+
} else if (targetType === "Page") {
77+
await proxyPerPage(target, data)
6278
}
6379
};
64-
module.exports = useProxy;
80+
81+
module.exports = useProxy;

src/lib/cdp.js

Lines changed: 0 additions & 38 deletions
This file was deleted.

src/lib/cookies.js

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,3 @@
1-
const WebSocket = require("ws");
21
const {CookieJar} = require("tough-cookie");
3-
const {Target, Network} = require("./cdp");
42

5-
const cookies = {
6-
async getCookies(endpoint, targetId) {
7-
const ws = new WebSocket(endpoint, {
8-
perMessageDeflate: false,
9-
maxPayload: 180 * 4096 // 0.73728Mb
10-
});
11-
await new Promise(resolve => ws.once("open", resolve));
12-
// Attach to target then get cookies
13-
const sessionId = await Target.attachToTarget(ws, targetId);
14-
return await Network.getCookies(ws, sessionId);
15-
},
16-
cookieStore(cookies) {
17-
if (!cookies)
18-
return;
19-
return CookieJar.deserializeSync({
20-
version: '[email protected]',
21-
storeType: 'MemoryCookieStore',
22-
rejectPublicSuffixes: true,
23-
cookies: cookies.map((cookie) => {
24-
return {
25-
key: cookie.name,
26-
value: cookie.value,
27-
expires: cookie.expires === -1 ? Infinity : new Date(cookie.expires * 1000).toISOString(),
28-
domain: cookie.domain.replace(/^\./, ""),
29-
path: cookie.path,
30-
secure: cookie.secure,
31-
httpOnly: cookie.httpOnly,
32-
sameSite: cookie.sameSite,
33-
creation: new Date().toISOString(),
34-
hostOnly: !(/^\./).test(cookie.domain)
35-
};
36-
})
37-
});
38-
}
39-
};
40-
module.exports = cookies;
3+
module.exports = new CookieJar();

0 commit comments

Comments
 (0)