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;