114 lines
3.3 KiB
JavaScript
114 lines
3.3 KiB
JavaScript
import express from 'express';
|
||
import fetch from 'node-fetch';
|
||
import { URL } from 'url';
|
||
|
||
const app = express();
|
||
const port = 3000;
|
||
|
||
// 允许跨域请求
|
||
app.use((req, res, next) => {
|
||
res.header('Access-Control-Allow-Origin', '*');
|
||
res.header('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS');
|
||
res.header('Access-Control-Allow-Headers', 'Range');
|
||
next();
|
||
});
|
||
|
||
// 通用代理路由
|
||
app.get('/proxy', async (req, res) => {
|
||
console.log('Received Range header:', req.headers.range);
|
||
// 获取目标 URL
|
||
const targetUrl = req.query.url;
|
||
console.log('Fetching data from:', targetUrl);
|
||
if (!targetUrl) {
|
||
return res.status(400).json({ error: 'URL parameter is required' });
|
||
}
|
||
|
||
try {
|
||
// 设置请求头
|
||
const headers = {
|
||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
||
};
|
||
if (req.headers.range) {
|
||
headers['Range'] = req.headers.range; // 转发 Range 请求头,支持视频分段加载
|
||
}
|
||
|
||
// 向目标 URL 发起请求
|
||
const response = await fetch(targetUrl, { headers });
|
||
|
||
// 检查响应状态
|
||
if (!response.ok) {
|
||
return res.status(response.status).json({
|
||
error: `Failed to fetch: ${response.statusText}`,
|
||
status: response.status
|
||
});
|
||
}
|
||
|
||
// 设置响应头
|
||
response.headers.forEach((value, key) => {
|
||
// 跳过一些可能导致问题的头信息
|
||
if (key.toLowerCase() !== 'content-encoding' &&
|
||
key.toLowerCase() !== 'transfer-encoding' &&
|
||
key.toLowerCase() !== 'connection') {
|
||
res.setHeader(key, value);
|
||
}
|
||
});
|
||
|
||
// 特殊处理 Transfer-Encoding 和 Content-Length
|
||
if (response.headers.get('transfer-encoding') === 'chunked') {
|
||
res.removeHeader('Content-Length');
|
||
}
|
||
|
||
// 将响应体直接流式传输给客户端
|
||
if (response.body) {
|
||
response.body.pipe(res, { end: true });
|
||
|
||
// 错误处理
|
||
response.body.on('error', (err) => {
|
||
console.error('Error during data transfer:', err);
|
||
if (!res.headersSent) {
|
||
res.status(500).json({ error: 'Error during data transfer' });
|
||
}
|
||
});
|
||
} else {
|
||
res.status(500).json({ error: 'No response body' });
|
||
}
|
||
} catch (error) {
|
||
console.error('Error fetching data:', error);
|
||
|
||
// 根据错误类型返回更具体的错误信息
|
||
let errorMessage = 'Error fetching data';
|
||
let statusCode = 500;
|
||
let hostname = '';
|
||
|
||
try {
|
||
hostname = new URL(targetUrl).hostname;
|
||
} catch (e) {
|
||
hostname = 'unknown';
|
||
}
|
||
|
||
if (error.code === 'ENOTFOUND') {
|
||
errorMessage = `DNS解析失败:无法解析域名 "${hostname}"。请检查URL是否正确,或网络连接是否正常。`;
|
||
statusCode = 502;
|
||
} else if (error.code === 'ECONNREFUSED') {
|
||
errorMessage = `连接被拒绝:无法连接到目标服务器 "${hostname}"。`;
|
||
statusCode = 502;
|
||
} else if (error.code === 'ETIMEDOUT') {
|
||
errorMessage = `请求超时:目标服务器 "${hostname}" 响应时间过长。`;
|
||
statusCode = 504;
|
||
}
|
||
|
||
if (!res.headersSent) {
|
||
res.status(statusCode).json({
|
||
error: errorMessage,
|
||
code: error.code,
|
||
targetUrl: targetUrl
|
||
});
|
||
}
|
||
}
|
||
});
|
||
|
||
app.listen(port, () => {
|
||
console.log(`Proxy server running at http://localhost:${port}`);
|
||
});
|
||
|