somratpro commited on
Commit
91a84e2
·
1 Parent(s): 0afb9bd

feat: refactor undici patching logic and redirect logging to stderr to prevent stdout interference

Browse files
Files changed (1) hide show
  1. cloudflare-proxy.js +88 -81
cloudflare-proxy.js CHANGED
@@ -6,8 +6,10 @@
6
  */
7
  "use strict";
8
 
9
- // Always log that we are loaded
10
- console.log("🦞 Cloudflare Proxy: Transparent redirector active");
 
 
11
 
12
  const https = require("https");
13
  const http = require("http");
@@ -23,7 +25,6 @@ if (
23
 
24
  const DEBUG = process.env.CLOUDFLARE_PROXY_DEBUG === "true" || true;
25
  const PROXY_SHARED_SECRET = (process.env.CLOUDFLARE_PROXY_SECRET || "").trim();
26
- // Default to wildcard mode to ensure all blocked external traffic is caught
27
  const PROXY_DOMAINS = process.env.CLOUDFLARE_PROXY_DOMAINS || "*";
28
  const BLOCKED_DOMAINS = PROXY_DOMAINS.split(",")
29
  .map((domain) => domain.trim())
@@ -91,7 +92,7 @@ if (PROXY_URL) {
91
 
92
  if (shouldProxy && !alreadyProxied && !hasTargetHeader) {
93
  if (DEBUG) {
94
- console.log(
95
  `[cloudflare-proxy] Redirecting ${originalModuleName}://${hostname}${path} -> ${proxy.hostname}`,
96
  );
97
  }
@@ -129,7 +130,6 @@ if (PROXY_URL) {
129
  https.request = patch(originalHttpsRequest, "https");
130
  http.request = patch(originalHttpRequest, "http");
131
 
132
- // Patch global fetch
133
  if (originalFetch) {
134
  globalThis.fetch = async function patchedFetch(input, init) {
135
  const request = input instanceof Request ? input : null;
@@ -160,7 +160,7 @@ if (PROXY_URL) {
160
  }
161
 
162
  if (DEBUG) {
163
- console.log(
164
  `[cloudflare-proxy] Redirecting fetch://${hostname}${url.pathname}${url.search} -> ${proxy.hostname}`,
165
  );
166
  }
@@ -190,92 +190,99 @@ if (PROXY_URL) {
190
  };
191
  }
192
 
193
- // Comprehensive undici patching via require interception
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  const Module = require("module");
195
  const originalRequire = Module.prototype.require;
196
  Module.prototype.require = function (id) {
197
  const exports = originalRequire.apply(this, arguments);
198
- if (id === "undici" || id.endsWith("/undici/index.js") || id.endsWith("/undici/lib/index.js")) {
199
- // Patch undici.fetch
200
- if (exports.fetch && !exports.fetch._patched) {
201
- const origUndiciFetch = exports.fetch;
202
- exports.fetch = async function (input, init) {
203
- return globalThis.fetch(input, init);
204
- };
205
- exports.fetch._patched = true;
206
- if (DEBUG) console.log("[cloudflare-proxy] Patched undici.fetch");
207
- }
208
-
209
- // Patch undici.request
210
- if (exports.request && !exports.request._patched) {
211
- const origUndiciRequest = exports.request;
212
- exports.request = async function(url, options) {
213
- let parsedUrl;
214
- try {
215
- parsedUrl = new URL(url);
216
- } catch(e) {
217
- return origUndiciRequest.apply(this, arguments);
218
- }
219
-
220
- if (shouldProxyHost(parsedUrl.hostname)) {
221
- if (DEBUG) console.log(`[cloudflare-proxy] Redirecting undici.request://${parsedUrl.hostname} -> ${proxy.hostname}`);
222
- const headers = options?.headers || {};
223
- headers["x-target-host"] = parsedUrl.hostname;
224
- if (PROXY_SHARED_SECRET) headers["x-proxy-key"] = PROXY_SHARED_SECRET;
225
-
226
- const proxiedUrl = new URL(parsedUrl.pathname + parsedUrl.search, proxy);
227
- return origUndiciRequest.call(this, proxiedUrl, {
228
- ...options,
229
- headers
230
- });
231
- }
232
- return origUndiciRequest.apply(this, arguments);
233
- };
234
- exports.request._patched = true;
235
- if (DEBUG) console.log("[cloudflare-proxy] Patched undici.request");
236
- }
237
-
238
- // Patch undici.Dispatcher to catch low-level calls (Pool, Client, etc.)
239
- if (exports.Dispatcher && exports.Dispatcher.prototype.dispatch && !exports.Dispatcher.prototype.dispatch._patched) {
240
- const origDispatch = exports.Dispatcher.prototype.dispatch;
241
- exports.Dispatcher.prototype.dispatch = function(options, handler) {
242
- const origin = options.origin ? String(options.origin) : "";
243
- let hostname = "";
244
- try {
245
- hostname = new URL(origin).hostname;
246
- } catch(e) {
247
- hostname = origin.split(':')[0];
248
- }
249
-
250
- if (shouldProxyHost(hostname)) {
251
- if (DEBUG) console.log(`[cloudflare-proxy] Redirecting undici dispatch: ${hostname}${options.path} -> ${proxy.hostname}`);
252
-
253
- if (Array.isArray(options.headers)) {
254
- options.headers.push("x-target-host", hostname);
255
- if (PROXY_SHARED_SECRET) options.headers.push("x-proxy-key", PROXY_SHARED_SECRET);
256
- } else {
257
- options.headers = options.headers || {};
258
- options.headers["x-target-host"] = hostname;
259
- if (PROXY_SHARED_SECRET) options.headers["x-proxy-key"] = PROXY_SHARED_SECRET;
260
- }
261
-
262
- options.origin = `https://${proxy.hostname}`;
263
- }
264
- return origDispatch.call(this, options, handler);
265
- };
266
- exports.Dispatcher.prototype.dispatch._patched = true;
267
- if (DEBUG) console.log("[cloudflare-proxy] Patched undici.Dispatcher.prototype.dispatch");
268
- }
269
  }
270
  return exports;
271
  };
272
 
273
  if (DEBUG) {
274
- console.log(`[cloudflare-proxy] Target proxy: ${proxy.hostname}`);
275
- console.log(`[cloudflare-proxy] Proxying: ${PROXY_ALL ? "ALL (except internal)" : BLOCKED_DOMAINS.join(", ")}`);
276
  }
277
  } catch (error) {
278
- console.error(`[cloudflare-proxy] Failed to initialize: ${error.message}`);
279
  }
280
  }
281
 
 
 
6
  */
7
  "use strict";
8
 
9
+ // Use stderr for logs to avoid breaking child processes that communicate via stdout JSON
10
+ const log = (...args) => console.error(...args);
11
+
12
+ log("🦞 Cloudflare Proxy: Transparent redirector active");
13
 
14
  const https = require("https");
15
  const http = require("http");
 
25
 
26
  const DEBUG = process.env.CLOUDFLARE_PROXY_DEBUG === "true" || true;
27
  const PROXY_SHARED_SECRET = (process.env.CLOUDFLARE_PROXY_SECRET || "").trim();
 
28
  const PROXY_DOMAINS = process.env.CLOUDFLARE_PROXY_DOMAINS || "*";
29
  const BLOCKED_DOMAINS = PROXY_DOMAINS.split(",")
30
  .map((domain) => domain.trim())
 
92
 
93
  if (shouldProxy && !alreadyProxied && !hasTargetHeader) {
94
  if (DEBUG) {
95
+ log(
96
  `[cloudflare-proxy] Redirecting ${originalModuleName}://${hostname}${path} -> ${proxy.hostname}`,
97
  );
98
  }
 
130
  https.request = patch(originalHttpsRequest, "https");
131
  http.request = patch(originalHttpRequest, "http");
132
 
 
133
  if (originalFetch) {
134
  globalThis.fetch = async function patchedFetch(input, init) {
135
  const request = input instanceof Request ? input : null;
 
160
  }
161
 
162
  if (DEBUG) {
163
+ log(
164
  `[cloudflare-proxy] Redirecting fetch://${hostname}${url.pathname}${url.search} -> ${proxy.hostname}`,
165
  );
166
  }
 
190
  };
191
  }
192
 
193
+ const patchUndiciInstance = (exports) => {
194
+ if (!exports) return;
195
+ if (exports.fetch && !exports.fetch._patched) {
196
+ exports.fetch = async function (input, init) {
197
+ return globalThis.fetch(input, init);
198
+ };
199
+ exports.fetch._patched = true;
200
+ if (DEBUG) log("[cloudflare-proxy] Patched undici.fetch");
201
+ }
202
+
203
+ if (exports.request && !exports.request._patched) {
204
+ const origUndiciRequest = exports.request;
205
+ exports.request = async function(url, options) {
206
+ let parsedUrl;
207
+ try {
208
+ parsedUrl = new URL(url);
209
+ } catch(e) {
210
+ return origUndiciRequest.apply(this, arguments);
211
+ }
212
+ if (shouldProxyHost(parsedUrl.hostname)) {
213
+ if (DEBUG) log(`[cloudflare-proxy] Redirecting undici.request://${parsedUrl.hostname} -> ${proxy.hostname}`);
214
+ const headers = options?.headers || {};
215
+ headers["x-target-host"] = parsedUrl.hostname;
216
+ if (PROXY_SHARED_SECRET) headers["x-proxy-key"] = PROXY_SHARED_SECRET;
217
+ const proxiedUrl = new URL(parsedUrl.pathname + parsedUrl.search, proxy);
218
+ return origUndiciRequest.call(this, proxiedUrl, { ...options, headers });
219
+ }
220
+ return origUndiciRequest.apply(this, arguments);
221
+ };
222
+ exports.request._patched = true;
223
+ if (DEBUG) log("[cloudflare-proxy] Patched undici.request");
224
+ }
225
+
226
+ if (exports.Dispatcher && exports.Dispatcher.prototype.dispatch && !exports.Dispatcher.prototype.dispatch._patched) {
227
+ const origDispatch = exports.Dispatcher.prototype.dispatch;
228
+ exports.Dispatcher.prototype.dispatch = function(options, handler) {
229
+ const origin = options.origin ? String(options.origin) : "";
230
+ let hostname = "";
231
+ try {
232
+ hostname = new URL(origin).hostname;
233
+ } catch(e) {
234
+ hostname = origin.split(':')[0];
235
+ }
236
+ if (shouldProxyHost(hostname)) {
237
+ if (DEBUG) log(`[cloudflare-proxy] Redirecting undici dispatch: ${hostname}${options.path} -> ${proxy.hostname}`);
238
+ if (Array.isArray(options.headers)) {
239
+ options.headers.push("x-target-host", hostname);
240
+ if (PROXY_SHARED_SECRET) options.headers.push("x-proxy-key", PROXY_SHARED_SECRET);
241
+ } else {
242
+ options.headers = options.headers || {};
243
+ options.headers["x-target-host"] = hostname;
244
+ if (PROXY_SHARED_SECRET) options.headers["x-proxy-key"] = PROXY_SHARED_SECRET;
245
+ }
246
+ options.origin = `https://${proxy.hostname}`;
247
+ }
248
+ return origDispatch.call(this, options, handler);
249
+ };
250
+ exports.Dispatcher.prototype.dispatch._patched = true;
251
+ if (DEBUG) log("[cloudflare-proxy] Patched undici.Dispatcher.prototype.dispatch");
252
+ }
253
+ };
254
+
255
+ // Patch already loaded undici instances
256
+ for (const key in require.cache) {
257
+ if (key.includes('/undici/')) {
258
+ try { patchUndiciInstance(require.cache[key].exports); } catch (e) {}
259
+ }
260
+ }
261
+
262
+ // Try to require undici immediately to patch it before others use it
263
+ try {
264
+ const undici = require("undici");
265
+ patchUndiciInstance(undici);
266
+ } catch (e) {}
267
+
268
+ // Intercept future undici requirements
269
  const Module = require("module");
270
  const originalRequire = Module.prototype.require;
271
  Module.prototype.require = function (id) {
272
  const exports = originalRequire.apply(this, arguments);
273
+ if (id === "undici" || id.includes("/undici/")) {
274
+ patchUndiciInstance(exports);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  }
276
  return exports;
277
  };
278
 
279
  if (DEBUG) {
280
+ log(`[cloudflare-proxy] Target proxy: ${proxy.hostname}`);
281
+ log(`[cloudflare-proxy] Proxying: ${PROXY_ALL ? "ALL (except internal)" : BLOCKED_DOMAINS.join(", ")}`);
282
  }
283
  } catch (error) {
284
+ log(`[cloudflare-proxy] Failed to initialize: ${error.message}`);
285
  }
286
  }
287
 
288
+