import http from 'http'; const PORT = process.env.PORT || 7860; // Anti-crash global obligatoire process.on('uncaughtException', (err) => console.error('CRASH ÉVITÉ:', err)); process.on('unhandledRejection', (err) => console.error('PROMESSE REFUSÉE:', err)); const LANDING_PAGE = ` Universal Smart Proxy

Proxy Universel

Entre n'importe quelle URL (Ex: http://gamingserver.fr:25578 ou https://google.com)

`; function parseCookies(cookieHeader) { const list = {}; if (!cookieHeader) return list; cookieHeader.split(';').forEach(cookie => { const parts = cookie.split('='); list[parts.shift().trim()] = decodeURIComponent(parts.join('=')); }); return list; } function resolveTarget(req, reqUrl) { let target = reqUrl.searchParams.get('url'); if (target) return { source: 'query', url: target }; if (req.headers.referer) { try { const refUrl = new URL(req.headers.referer); let refTarget = refUrl.searchParams.get('url'); if (refTarget) { const base = new URL(refTarget); return { source: 'referer', url: new URL(req.url, base.origin).href }; } } catch (e) {} } const cookies = parseCookies(req.headers.cookie); if (cookies.proxy_last_origin) { try { const baseOrigin = decodeURIComponent(cookies.proxy_last_origin); return { source: 'cookie', url: new URL(req.url, baseOrigin).href }; } catch (e) {} } return null; } const server = http.createServer(async (req, res) => { const host = req.headers.host || '127.0.0.1'; if (req.url === '/favicon.ico') { res.writeHead(204); return res.end(); } const reqUrl = new URL(req.url, `http://${host}`); const targetData = resolveTarget(req, reqUrl); if (!targetData) { res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); return res.end(LANDING_PAGE); } let targetUrlStr = targetData.url; try { if (!targetUrlStr.startsWith('http://') && !targetUrlStr.startsWith('https://')) { targetUrlStr = 'https://' + targetUrlStr; } const targetUrl = new URL(targetUrlStr); const headers = new Headers(); for (const [key, value] of Object.entries(req.headers)) { if (!['host', 'cookie', 'accept-encoding', 'connection', 'origin', 'referer'].includes(key.toLowerCase())) { headers.set(key, value); } } headers.set('Host', targetUrl.host); headers.set('User-Agent', req.headers['user-agent'] || 'Mozilla/5.0'); if (req.headers['origin']) headers.set('Origin', targetUrl.origin); if (req.headers['referer']) headers.set('Referer', targetUrl.origin); if (req.headers.cookie) { const cleanCookies = req.headers.cookie.split(';') .map(c => c.trim()) .filter(c => !c.startsWith('proxy_last_origin=')) .join('; '); if (cleanCookies) headers.set('Cookie', cleanCookies); } let body = null; if (req.method !== 'GET' && req.method !== 'HEAD') { const buffers = []; for await (const chunk of req) { buffers.push(chunk); } body = Buffer.concat(buffers); } const response = await fetch(targetUrl.href, { method: req.method, headers: headers, body: body, redirect: 'manual' }); const contentType = response.headers.get('content-type') || ''; const isHtml = contentType.includes('text/html'); // Base des en-têtes système à nettoyer const stripHeaders = [ 'connection', 'keep-alive', 'transfer-encoding', 'content-encoding', 'set-cookie', 'location', 'content-security-policy', 'strict-transport-security' ]; const resHeaders = {}; response.headers.forEach((value, key) => { const lowerKey = key.toLowerCase(); if (!stripHeaders.includes(lowerKey)) { // CORRECTION FIX TÉLÉCHARGEMENT : On ne supprime la taille QUE si c'est du HTML modifié if (lowerKey === 'content-length' && isHtml) return; resHeaders[key] = value; } }); const setCookies = [`proxy_last_origin=${encodeURIComponent(targetUrl.origin)}; Path=/; HttpOnly; SameSite=Lax`]; const originalSetCookies = response.headers.getSetCookie ? response.headers.getSetCookie() : []; originalSetCookies.forEach(c => setCookies.push(c)); resHeaders['Set-Cookie'] = setCookies; if (response.status >= 300 && response.status < 400) { let location = response.headers.get('location'); if (location) { const absoluteLocation = new URL(location, targetUrl.href).href; resHeaders['Location'] = `/?url=${encodeURIComponent(absoluteLocation)}`; } res.writeHead(response.status, resHeaders); return res.end(); } if (isHtml) { let text = await response.text(); text = text.replace(/(href|src)=["'](https?:\/\/[^"']+)["']/g, (match, attr, url) => { return `${attr}="/?url=${encodeURIComponent(url)}"`; }); const bodyBuffer = Buffer.from(text, 'utf-8'); resHeaders['content-length'] = bodyBuffer.length; res.writeHead(response.status, resHeaders); return res.end(bodyBuffer); } // CORRECTION FLUX : Boucle d'écriture directe paquet par paquet pour éviter les pertes sur les fichiers/téléchargements res.writeHead(response.status, resHeaders); if (response.body) { try { for await (const chunk of response.body) { res.write(chunk); } } catch (streamErr) { console.error("Erreur durant le transfert du fichier:", streamErr.message); } } res.end(); } catch (err) { console.error(`[Erreur Cible] ${targetUrlStr}:`, err.message); if (!res.headersSent) { res.writeHead(502, { 'Content-Type': 'text/plain; charset=utf-8' }); res.end(`Erreur Proxy : Impossible de joindre la cible.`); } } }); server.on('upgrade', (req, socket, head) => { const host = req.headers.host || '127.0.0.1'; const reqUrl = new URL(req.url, `http://${host}`); const targetData = resolveTarget(req, reqUrl); if (!targetData) { socket.end(); return; } const target = new URL(targetData.url); console.log(`[WS Universel] Connexion injectée vers : ${target.host}`); const options = { hostname: target.hostname, port: target.port || (target.protocol === 'https:' ? 443 : 80), method: req.method, path: req.url, headers: { ...req.headers } }; options.headers['host'] = target.host; if (options.headers['origin']) options.headers['origin'] = target.origin; const proxyReq = http.request(options); proxyReq.on('upgrade', (proxyRes, proxySocket, proxyHead) => { socket.write('HTTP/1.1 101 Switching Protocols\r\n'); for (const [key, value] of Object.entries(proxyRes.headers)) { socket.write(`${key}: ${value}\r\n`); } socket.write('\r\n'); proxySocket.write(proxyHead); proxySocket.pipe(socket); socket.pipe(proxySocket); }); proxyReq.on('error', () => socket.end()); proxyReq.end(); }); server.listen(PORT, '0.0.0.0', () => { console.log(`Proxy Multi-Site Intelligent & Téléchargements corrigés sur le port ${PORT}`); });