cf-solver / endpoints /proxyRequest.js
Samuel
update
15f7aec
function proxyRequest({ url, proxy, method = 'GET', body = null, headers = {}, cookies = [], sessionHeaders = {} }) {
return new Promise(async (resolve, reject) => {
if (!url) return reject("Missing url parameter");
const context = await global.browser
.createBrowserContext({
proxyServer: proxy ? `http://${proxy.host}:${proxy.port}` : undefined,
})
.catch(() => null);
if (!context) return reject("Failed to create browser context");
let isResolved = false;
var cl = setTimeout(async () => {
if (!isResolved) {
await context.close();
reject("Timeout Error");
}
}, global.timeOut || 60000);
try {
const page = await context.newPage();
if (proxy?.username && proxy?.password)
await page.authenticate({
username: proxy.username,
password: proxy.password,
});
const targetUrl = new URL(url);
if (cookies && cookies.length > 0) {
const cookiesToSet = cookies.map(cookie => ({
name: cookie.name,
value: cookie.value,
domain: cookie.domain || targetUrl.hostname,
path: cookie.path || '/',
secure: cookie.secure !== undefined ? cookie.secure : targetUrl.protocol === 'https:',
httpOnly: cookie.httpOnly || false,
sameSite: cookie.sameSite || 'Lax'
}));
await page.setCookie(...cookiesToSet);
}
if (sessionHeaders && sessionHeaders['user-agent']) {
await page.setUserAgent(sessionHeaders['user-agent']);
}
const sanitizedHeaders = { ...headers };
const headersToRemove = ['host', 'content-length', 'connection', 'accept-encoding', 'transfer-encoding'];
headersToRemove.forEach(h => {
delete sanitizedHeaders[h];
delete sanitizedHeaders[h.toLowerCase()];
});
if (sessionHeaders) {
if (sessionHeaders['accept-language'] && !sanitizedHeaders['accept-language']) {
sanitizedHeaders['accept-language'] = sessionHeaders['accept-language'];
}
}
await page.goto(targetUrl.origin, { waitUntil: 'domcontentloaded', timeout: 30000 }).catch(() => {});
const result = await page.evaluate(async (options) => {
try {
const fetchOptions = {
method: options.method,
headers: options.headers || {},
credentials: 'include'
};
if (options.body && ['POST', 'PUT', 'PATCH'].includes(options.method.toUpperCase())) {
fetchOptions.body = typeof options.body === 'string'
? options.body
: JSON.stringify(options.body);
if (typeof options.body === 'object' && !fetchOptions.headers['content-type']) {
fetchOptions.headers['content-type'] = 'application/json';
}
}
const response = await fetch(options.url, fetchOptions);
const responseHeaders = {};
response.headers.forEach((value, key) => {
responseHeaders[key] = value;
});
let responseBody;
const contentType = response.headers.get('content-type') || '';
if (contentType.includes('application/json')) {
responseBody = await response.json();
} else {
responseBody = await response.text();
}
return {
status: response.status,
statusText: response.statusText,
headers: responseHeaders,
body: responseBody,
ok: response.ok
};
} catch (e) {
return { error: e.message };
}
}, { url, method, body, headers: sanitizedHeaders });
if (result.error) {
await context.close();
isResolved = true;
clearTimeout(cl);
return reject(result.error);
}
const updatedCookies = await page.cookies();
const browserHeaders = await page.evaluate(() => {
return {
'user-agent': navigator.userAgent,
'accept-language': navigator.language || navigator.languages.join(',')
};
});
await context.close();
isResolved = true;
clearInterval(cl);
resolve({
response: result,
cookies: updatedCookies,
browserHeaders
});
} catch (e) {
if (!isResolved) {
await context.close();
clearTimeout(cl);
reject(e.message);
}
}
});
}
module.exports = proxyRequest;