WalleGriffkinder commited on
Commit
f8a9810
·
verified ·
1 Parent(s): 85a98e1

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +59 -51
index.js CHANGED
@@ -2,6 +2,8 @@ import express from 'express';
2
  import fetch from 'node-fetch';
3
 
4
  const app = express();
 
 
5
  app.use(express.raw({type: '*/*'}));
6
 
7
  // Hello World на главной
@@ -11,49 +13,52 @@ app.get('/', (req, res) => {
11
 
12
  // Прокси для всех остальных путей
13
  app.all('*', async (req, res) => {
14
- // Этот if (req.url === '/') вероятно, не нужен, если app.get('/') обрабатывает '/',
15
- // но оставим для безопасности, если app.all('*') почему-то получит запрос к '/'
16
  if (req.url === '/') {
17
- // Если это корневой путь, и он не был обработан app.get('/'),
18
- // то здесь можно вернуть ошибку или специфический ответ.
19
- // В данном случае, лучше ничего не делать, так как app.get('/') должен его поймать.
20
- return;
21
  }
22
 
23
  try {
24
- // Извлекаем целевой URL из пути запроса
25
- // req.url будет выглядеть как "/https://anotherurl.com?param=value"
26
- // Мы удаляем первый символ '/', чтобы получить "https://anotherurl.com?param=value"
27
- const targetUrl = req.url.substring(1);
28
 
29
- // Проверяем, не пустой ли targetUrl после удаления '/', на всякий случай
30
- if (!targetUrl) {
31
  res.status(400).send('Target URL is missing in the path.');
32
  return;
33
  }
 
 
 
 
 
 
 
 
 
34
 
35
- // Копируем и фильтруем заголовки
36
- const headers = {...req.headers};
37
- delete headers['host']; // host будет установлен fetch на основе targetUrl
38
- delete headers['content-length']; // content-length будет пересчитан fetch
 
 
39
 
40
- const response = await fetch(targetUrl, { // Используем извлеченный targetUrl
41
  method: req.method,
42
- headers,
43
- // Отправляем тело запроса, если это не GET или HEAD запрос
44
  body: (req.method !== 'GET' && req.method !== 'HEAD') ? req.body : undefined,
45
- // Важно для проксирования: redirect: 'manual' или 'follow' в зависимости от нужного поведения
46
- // 'follow' (по умолчанию) будет автоматически следовать редиректам от целевого сервера
47
- // 'manual' позволит прокси вернуть редирект клиенту как есть
48
- redirect: 'manual' // Или 'follow', если хотите, чтобы прокси сам следовал редиректам
 
 
49
  });
50
 
51
  // Копируем все заголовки из ответа целевого сервера
 
52
  Object.entries(response.headers.raw()).forEach(([key, value]) => {
53
- // Некоторые заголовки, такие как 'content-encoding', могут быть проблемой,
54
- // если тело ответа изменяется (например, автоматическое распаковывание gzip fetch'ем)
55
- // Однако, если мы стримим или передаем buffer, это должно быть в порядке.
56
- // Для безопасности, можно добавить фильтрацию специфичных заголовков здесь при необходимости.
57
  if (value) {
58
  res.setHeader(key, value.length === 1 ? value[0] : value);
59
  }
@@ -61,16 +66,7 @@ app.all('*', async (req, res) => {
61
 
62
  res.status(response.status);
63
 
64
- // Streaming response для text/event-stream
65
- if (response.headers.get('content-type')?.includes('text/event-stream')) {
66
- // res.setHeader('Content-Type', 'text/event-stream'); // Уже установлено циклом выше
67
- response.body.pipe(res);
68
- return;
69
- }
70
-
71
- // Regular response
72
- // Используем response.body.pipe(res) для более эффективной передачи больших ответов
73
- // response.buffer() загружает весь ответ в память перед отправкой.
74
  if (response.body) {
75
  response.body.pipe(res);
76
  } else {
@@ -78,22 +74,34 @@ app.all('*', async (req, res) => {
78
  }
79
 
80
  } catch (error) {
81
- console.error('Proxy error:', error);
82
- // Отправляем более информативное сообщение об ошибке, если это возможно
83
- // Избегайте отправки error.message напрямую клиенту в продакшене без санации,
84
- // так как оно может содержать чувствительную информацию.
85
- if (error.code === 'ENOTFOUND' || error.message.includes('invalid URL')) {
86
- res.status(400).send(`Invalid target URL or host not found: ${req.url.substring(1)}`);
87
- } else if (error.message.includes('ECONNREFUSED')) {
88
- res.status(502).send(`Bad Gateway: Could not connect to target server at ${req.url.substring(1)}`);
89
- }
90
- else {
91
- res.status(500).send('Proxy error occurred.');
 
 
 
 
 
 
 
 
 
92
  }
93
  }
94
  });
95
 
96
- app.listen(7860, '0.0.0.0', () => {
97
- console.log('Server running on port 7860. Proxying requests from path to the specified URL.');
98
- console.log('Example: http://localhost:7860/https://www.google.com will proxy to https://www.google.com');
 
 
 
99
  });
 
2
  import fetch from 'node-fetch';
3
 
4
  const app = express();
5
+ // Увеличим лимит размера тела запроса, если необходимо (по умолчанию 100kb)
6
+ // express.raw({type: '*/*', limit: '10mb'})
7
  app.use(express.raw({type: '*/*'}));
8
 
9
  // Hello World на главной
 
13
 
14
  // Прокси для всех остальных путей
15
  app.all('*', async (req, res) => {
 
 
16
  if (req.url === '/') {
17
+ return; // Уже обработано app.get('/')
 
 
 
18
  }
19
 
20
  try {
21
+ const targetUrlString = req.url.substring(1);
 
 
 
22
 
23
+ if (!targetUrlString) {
 
24
  res.status(400).send('Target URL is missing in the path.');
25
  return;
26
  }
27
+
28
+ // Проверка на валидность URL перед использованием
29
+ let targetUrl;
30
+ try {
31
+ targetUrl = new URL(targetUrlString);
32
+ } catch (e) {
33
+ res.status(400).send(`Invalid target URL provided in path: ${targetUrlString}`);
34
+ return;
35
+ }
36
 
37
+ const requestHeaders = {...req.headers};
38
+ delete requestHeaders['host']; // host будет установлен fetch на основе targetUrl
39
+ // content-length будет пересчитан fetch из req.body, если req.body - Buffer или строка
40
+ // req.body здесь - это Buffer из express.raw(), так что fetch установит Content-Length
41
+ delete requestHeaders['content-length'];
42
+
43
 
44
+ const response = await fetch(targetUrl.toString(), { // Используем targetUrl.toString()
45
  method: req.method,
46
+ headers: requestHeaders,
 
47
  body: (req.method !== 'GET' && req.method !== 'HEAD') ? req.body : undefined,
48
+ redirect: 'manual',
49
+ // ---- КЛЮЧЕВОЕ ИЗМЕНЕНИЕ ----
50
+ // Говорим node-fetch не раскодировать ответ автоматически.
51
+ // Прокси будет передавать сжатый поток как есть.
52
+ compress: false
53
+ // --------------------------
54
  });
55
 
56
  // Копируем все заголовки из ответа целевого сервера
57
+ // Теперь, когда node-fetch не раскодирует, мы можем безопасно копировать Content-Encoding и Content-Length
58
  Object.entries(response.headers.raw()).forEach(([key, value]) => {
59
+ // Некоторые "hop-by-hop" заголовки не должны копироваться,
60
+ // но для большинства это безопасно, особенно Connection, который Node.js обработает.
61
+ // Transfer-Encoding также будет управляться Node.js/Express при пайпинге.
 
62
  if (value) {
63
  res.setHeader(key, value.length === 1 ? value[0] : value);
64
  }
 
66
 
67
  res.status(response.status);
68
 
69
+ // Потоковая передача тела ответа
 
 
 
 
 
 
 
 
 
70
  if (response.body) {
71
  response.body.pipe(res);
72
  } else {
 
74
  }
75
 
76
  } catch (error) {
77
+ console.error(`[${new Date().toISOString()}] Proxy error for ${req.method} ${req.url}:`, error);
78
+ if (!res.headersSent) {
79
+ if (error.code === 'ENOTFOUND') {
80
+ res.status(404).send(`Target host not found: ${req.url.substring(1)}`);
81
+ } else if (error.message && error.message.includes('Invalid URL')) { // Проверка может быть улучшена
82
+ res.status(400).send(`Invalid target URL in path: ${req.url.substring(1)}`);
83
+ } else if (error.code === 'ECONNREFUSED') {
84
+ res.status(502).send(`Bad Gateway: Could not connect to target server at ${req.url.substring(1)}`);
85
+ } else if (error.code === 'ERR_INVALID_URL') { // от new URL(targetUrlString)
86
+ res.status(400).send(`Invalid target URL format in path: ${req.url.substring(1)}`);
87
+ }
88
+ else {
89
+ res.status(500).send('Proxy error occurred.');
90
+ }
91
+ } else {
92
+ // Если заголовки уже отправлены, но ошибка произошла при стриминге тела,
93
+ // мы можем только завершить соединение или попытаться записать ошибку в лог.
94
+ // response.body.pipe(res) уже могло начать отправку.
95
+ console.error(`[${new Date().toISOString()}] Proxy error after headers sent for ${req.method} ${req.url}.`);
96
+ res.end(); // Пытаемся корректно завершить ответ, если это возможно.
97
  }
98
  }
99
  });
100
 
101
+ const PORT = process.env.PORT || 7860;
102
+ const HOST = '0.0.0.0';
103
+
104
+ app.listen(PORT, HOST, () => {
105
+ console.log(`Server running on http://${HOST}:${PORT}`);
106
+ console.log(`Proxying requests. Example: http://${HOST === '0.0.0.0' ? 'localhost' : HOST}:${PORT}/https://www.google.com`);
107
  });