somratpro commited on
Commit
d68b341
·
1 Parent(s): 6851577

fix: use AsyncLocalStorage for robust recursion guard

Browse files
Files changed (1) hide show
  1. dns-fix.js +37 -46
dns-fix.js CHANGED
@@ -14,9 +14,14 @@ function isBlocked(hostname) {
14
  const _origHttpRequest = http.request;
15
  const _origHttpsRequest = https.request;
16
 
17
- const IN_PROXY = Symbol("IN_PROXY");
 
18
 
19
  function transparentProxyRequest(protocol, origFn, ...args) {
 
 
 
 
20
  let options = args[0];
21
  let callback = args[1];
22
 
@@ -33,17 +38,11 @@ function transparentProxyRequest(protocol, origFn, ...args) {
33
  options = { ...options };
34
  }
35
 
36
- // RECURSION GUARD
37
- if (options[IN_PROXY]) {
38
- return origFn.apply(this, args);
39
- }
40
-
41
  if (typeof callback !== "function" && typeof args[2] === "function") {
42
  callback = args[2];
43
  }
44
 
45
  const hostname = options.hostname || options.host;
46
- const port = options.port || (protocol === "https:" ? 443 : 80);
47
  const path = options.path || options.pathname || "/";
48
 
49
  if (isBlocked(hostname)) {
@@ -52,54 +51,51 @@ function transparentProxyRequest(protocol, origFn, ...args) {
52
  const requestUrl = `${protocol}//${hostname}${path}`;
53
  const requestHeaders = { ...options.headers };
54
 
55
- // We need to return a ClientRequest-like object (a Writable stream)
56
  const reqStream = new PassThrough();
57
  const resStream = new PassThrough();
58
 
59
- // Buffer the request body
60
  let requestBody = Buffer.alloc(0);
61
  reqStream.on("data", (chunk) => {
62
  requestBody = Buffer.concat([requestBody, chunk]);
63
  });
64
 
65
- reqStream.on("finish", async () => {
66
- try {
67
- // Ensure fetch itself doesn't trigger our monkey-patches (if undici uses them)
68
- const fetchRes = await fetch(requestUrl, {
69
- method: options.method || "GET",
70
- headers: requestHeaders,
71
- body: options.method !== "GET" && options.method !== "HEAD" ? requestBody : undefined,
72
- redirect: "manual",
73
- });
74
-
75
- // ... existing logic ...
76
- resStream.statusCode = fetchRes.status;
77
- resStream.statusMessage = fetchRes.statusText;
78
- resStream.headers = Object.fromEntries(fetchRes.headers.entries());
79
- resStream.rawHeaders = [];
80
- for (const [k, v] of fetchRes.headers.entries()) {
81
- resStream.rawHeaders.push(k, v);
82
- }
83
 
84
- if (callback) callback(resStream);
85
- reqStream.emit("response", resStream);
86
 
87
- if (fetchRes.body) {
88
- const reader = fetchRes.body.getReader();
89
- while (true) {
90
- const { done, value } = await reader.read();
91
- if (done) break;
92
- resStream.write(value);
 
93
  }
 
 
 
 
94
  }
95
- resStream.end();
96
- } catch (err) {
97
- console.error(`[DNS-FIX] Proxy error for ${requestUrl}: ${err.message}`);
98
- reqStream.emit("error", err);
99
- }
100
  });
101
 
102
- // Mock ClientRequest methods
103
  reqStream.abort = () => reqStream.destroy();
104
  reqStream.end = (chunk) => {
105
  if (chunk) reqStream.write(chunk);
@@ -123,18 +119,13 @@ https.request = function (...args) {
123
  return transparentProxyRequest("https:", _origHttpsRequest, ...args);
124
  };
125
 
126
- // Also patch http.get and https.get as they often bypass request
127
  http.get = function (...args) {
128
- const options = typeof args[0] === 'string' ? new URL(args[0]) : args[0];
129
- if (options) options[IN_PROXY] = true;
130
  const req = http.request(...args);
131
  req.end();
132
  return req;
133
  };
134
 
135
  https.get = function (...args) {
136
- const options = typeof args[0] === 'string' ? new URL(args[0]) : args[0];
137
- if (options) options[IN_PROXY] = true;
138
  const req = https.request(...args);
139
  req.end();
140
  return req;
 
14
  const _origHttpRequest = http.request;
15
  const _origHttpsRequest = https.request;
16
 
17
+ const { AsyncLocalStorage } = require("async_hooks");
18
+ const asyncStorage = new AsyncLocalStorage();
19
 
20
  function transparentProxyRequest(protocol, origFn, ...args) {
21
+ if (asyncStorage.getStore()) {
22
+ return origFn.apply(this, args);
23
+ }
24
+
25
  let options = args[0];
26
  let callback = args[1];
27
 
 
38
  options = { ...options };
39
  }
40
 
 
 
 
 
 
41
  if (typeof callback !== "function" && typeof args[2] === "function") {
42
  callback = args[2];
43
  }
44
 
45
  const hostname = options.hostname || options.host;
 
46
  const path = options.path || options.pathname || "/";
47
 
48
  if (isBlocked(hostname)) {
 
51
  const requestUrl = `${protocol}//${hostname}${path}`;
52
  const requestHeaders = { ...options.headers };
53
 
 
54
  const reqStream = new PassThrough();
55
  const resStream = new PassThrough();
56
 
 
57
  let requestBody = Buffer.alloc(0);
58
  reqStream.on("data", (chunk) => {
59
  requestBody = Buffer.concat([requestBody, chunk]);
60
  });
61
 
62
+ reqStream.on("finish", () => {
63
+ asyncStorage.run(true, async () => {
64
+ try {
65
+ const fetchRes = await fetch(requestUrl, {
66
+ method: options.method || "GET",
67
+ headers: requestHeaders,
68
+ body: options.method !== "GET" && options.method !== "HEAD" ? requestBody : undefined,
69
+ redirect: "manual",
70
+ });
71
+
72
+ resStream.statusCode = fetchRes.status;
73
+ resStream.statusMessage = fetchRes.statusText;
74
+ resStream.headers = Object.fromEntries(fetchRes.headers.entries());
75
+ resStream.rawHeaders = [];
76
+ for (const [k, v] of fetchRes.headers.entries()) {
77
+ resStream.rawHeaders.push(k, v);
78
+ }
 
79
 
80
+ if (callback) callback(resStream);
81
+ reqStream.emit("response", resStream);
82
 
83
+ if (fetchRes.body) {
84
+ const reader = fetchRes.body.getReader();
85
+ while (true) {
86
+ const { done, value } = await reader.read();
87
+ if (done) break;
88
+ resStream.write(value);
89
+ }
90
  }
91
+ resStream.end();
92
+ } catch (err) {
93
+ console.error(`[DNS-FIX] Proxy error for ${requestUrl}: ${err.message}`);
94
+ reqStream.emit("error", err);
95
  }
96
+ });
 
 
 
 
97
  });
98
 
 
99
  reqStream.abort = () => reqStream.destroy();
100
  reqStream.end = (chunk) => {
101
  if (chunk) reqStream.write(chunk);
 
119
  return transparentProxyRequest("https:", _origHttpsRequest, ...args);
120
  };
121
 
 
122
  http.get = function (...args) {
 
 
123
  const req = http.request(...args);
124
  req.end();
125
  return req;
126
  };
127
 
128
  https.get = function (...args) {
 
 
129
  const req = https.request(...args);
130
  req.end();
131
  return req;