-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathproxy.js
150 lines (129 loc) · 5.51 KB
/
proxy.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
const axios = require('axios');
const express = require('express');
const cheerio = require('cheerio');
const app = express();
app.use(express.json());
function rewriteUrls(content, baseUrl, contentType) {
if (contentType && contentType.includes('text/html')) {
const $ = cheerio.load(content);
$('script[src], link[href], img[src], a[href], form[action]').each((_, element) => {
const srcAttr = $(element).attr('src');
const hrefAttr = $(element).attr('href');
const actionAttr = $(element).attr('action');
const fixUrl = (url) => {
if (url && !/^https?:\/\//i.test(url)) {
return new URL(url, baseUrl).href.replace(/^https?:\/\//, '/');
}
return url.replace(/^https?:\/\//, '/');
};
if (srcAttr) {
$(element).attr('src', fixUrl(srcAttr));
}
if (hrefAttr) {
$(element).attr('href', fixUrl(hrefAttr));
}
if (actionAttr) {
$(element).attr('action', fixUrl(actionAttr));
}
});
// Handle favicon
if (!$('link[rel="icon"]').length) {
$('head').append(`<link rel="icon" href="${baseUrl}/favicon.ico">`);
}
return $.html();
} else if (contentType && contentType.includes('application/javascript')) {
let updatedContent = content.toString('utf8');
const baseUrl = new URL(baseUrl).origin.replace(/^https?:\/\//, '/');
updatedContent = updatedContent.replace(/(src|href|url)\s*=\s*['"]([^'"]+)['"]/g, (match, p1, p2) => {
if (!/^https?:\/\//i.test(p2)) {
const fixedUrl = new URL(p2, baseUrl).href.replace(/^https?:\/\//, '/');
return `${p1}="${fixedUrl}"`;
}
return match;
});
return updatedContent;
}
return content;
}
async function proxyHandler(req, res) {
let targetUrl = req.url.slice(1);
if (!targetUrl) {
res.status(400).send({ error: 'Cannot get without URL' });
return;
}
try {
// Decode the URL and add https if it doesn't start with http or https
targetUrl = decodeURIComponent(targetUrl);
if (!/^https?:\/\//i.test(targetUrl)) {
targetUrl = `https://${targetUrl}`;
}
// Get the base URL of the server dynamically
const baseUrl = `${req.protocol}://${req.get('host')}`;
// Check if the target URL contains the base URL of the server
if (targetUrl.includes(baseUrl)) {
res.status(400).send({ error: 'Recursive proxy request detected' });
return;
}
console.log(`Proxying request to: ${targetUrl}`);
const response = await axios({
method: req.method,
url: targetUrl,
headers: {
...req.headers,
host: new URL(targetUrl).host,
'User-Agent': req.headers['user-agent'] || 'Mozilla/5.0 (compatible; ProxyServer/1.0)'
},
data: req.method === 'POST' ? req.body : undefined,
responseType: 'arraybuffer',
validateStatus: null,
maxRedirects: 0,
});
console.log(`Received response with status: ${response.status}`);
// Handle CORS headers
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (response.status === 302 || response.status === 301) {
const redirectUrl = response.headers.location;
if (redirectUrl) {
const absoluteRedirectUrl = new URL(redirectUrl, targetUrl).toString();
const proxyRedirectUrl = absoluteRedirectUrl.replace(/^https?:\/\//, '/');
console.log(`Redirecting to: ${proxyRedirectUrl}`);
res.redirect(proxyRedirectUrl);
return;
}
}
const contentType = response.headers['content-type'];
const encoding = response.headers['content-encoding'];
if (contentType && (contentType.includes('text/html') || contentType.includes('application/javascript'))) {
let content = response.data;
if (encoding) {
res.set('Content-Encoding', encoding);
}
const baseUrl = `https://${new URL(targetUrl).host}`;
const modifiedContent = rewriteUrls(content, baseUrl, contentType);
res.set('Content-Type', contentType);
res.status(response.status).send(modifiedContent);
} else {
res.set('Content-Type', contentType);
res.status(response.status).send(response.data);
}
} catch (error) {
console.error('Proxy error:', error.message);
if (error.response) {
console.error('Response error status:', error.response.status);
res.status(error.response.status).send(error.response.data);
} else if (error.request) {
console.error('No response received');
res.status(502).send({ error: 'Bad Gateway' });
} else {
console.error('Request setup error');
res.status(400).send({ error: 'Cannot get without URL' });
}
}
}
app.all('/*', proxyHandler);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});