@jonathanleelx/meddle 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/lib/crash-report.js +192 -0
- package/dist/core/connect-tunnel.d.ts.map +1 -1
- package/dist/core/connect-tunnel.js +8 -2
- package/dist/core/connect-tunnel.js.map +1 -1
- package/dist/core/h2-pool.d.ts +2 -0
- package/dist/core/h2-pool.d.ts.map +1 -1
- package/dist/core/h2-pool.js +20 -3
- package/dist/core/h2-pool.js.map +1 -1
- package/index.js +33 -0
- package/package.json +1 -1
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* meddle crash-report — crash dump collection with fingerprinting and dedupe.
|
|
3
|
+
*
|
|
4
|
+
* Pure JS so it runs unmodified in the npm package and in the deno-compiled
|
|
5
|
+
* binary (bin/ is bundled at compile time). Never throws: the crash reporter
|
|
6
|
+
* must not crash while reporting a crash.
|
|
7
|
+
*
|
|
8
|
+
* Layout: <home>/crash/<fingerprint>-<timestamp>.json
|
|
9
|
+
*
|
|
10
|
+
* Privacy: only env KEY names are recorded, never their values; request
|
|
11
|
+
* headers/URLs are scrubbed of common credential patterns before writing.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs')
|
|
15
|
+
const os = require('os')
|
|
16
|
+
const path = require('path')
|
|
17
|
+
|
|
18
|
+
const CRASH_DIR = 'crash'
|
|
19
|
+
const MAX_FILES_PER_FINGERPRINT = 10
|
|
20
|
+
|
|
21
|
+
const SENSITIVE_HEADER_PATTERNS = [
|
|
22
|
+
/authorization/i,
|
|
23
|
+
/cookie/i,
|
|
24
|
+
/set-cookie/i,
|
|
25
|
+
/token/i,
|
|
26
|
+
/session/i,
|
|
27
|
+
/x-api-key/i,
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
const SENSITIVE_QUERY_PATTERNS = [
|
|
31
|
+
/token=/i,
|
|
32
|
+
/key=/i,
|
|
33
|
+
/secret=/i,
|
|
34
|
+
/password=/i,
|
|
35
|
+
/apikey=/i,
|
|
36
|
+
/appid=/i,
|
|
37
|
+
/app_id=/i,
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Normalize a stack frame into a stable token:
|
|
42
|
+
* - file:///.../dist/core/h2-pool.js:182:30 → h2-pool.js
|
|
43
|
+
* - ext:deno_node/net.ts:1:21858 → net.ts
|
|
44
|
+
* - node:internal/timers.mjs:4:1174 → internal/timers.mjs
|
|
45
|
+
* - (file:///x/dist/a.js:10:20) → a.js
|
|
46
|
+
* Line/column numbers and volatile absolute paths are stripped so identical
|
|
47
|
+
* crashes (different addresses/timestamps) share one fingerprint.
|
|
48
|
+
*/
|
|
49
|
+
function normalizeFrame(frame) {
|
|
50
|
+
const line = frame.trim()
|
|
51
|
+
const match = line.match(/\(?(?:file:\/\/)?[^)\s]*[\\/]([^\\/)\s:]+\.(?:js|ts|mjs|cjs))(?::\d+:\d+)?\)?/)
|
|
52
|
+
if (match) return match[1]
|
|
53
|
+
// bare frames like "at f (a.js:10:20)" or "at TCPWrap.onStreamRead"
|
|
54
|
+
const bare = line.match(/at\s+([\w.]+)(?:\s*\(.*\))?$/)
|
|
55
|
+
if (bare) return bare[1]
|
|
56
|
+
return line.slice(0, 80)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Build a stable fingerprint for an error. Prefers the error `code` when
|
|
61
|
+
* present (e.g. ECONNRESET); otherwise the first stack frame chain is
|
|
62
|
+
* normalized to remove addresses, paths and line numbers.
|
|
63
|
+
* @param {Error & { code?: string }} err
|
|
64
|
+
* @returns {string}
|
|
65
|
+
*/
|
|
66
|
+
function crashFingerprint(err) {
|
|
67
|
+
if (err && typeof err.code === 'string' && err.code) return err.code
|
|
68
|
+
if (err && typeof err.stack === 'string') {
|
|
69
|
+
const lines = err.stack.split('\n').slice(0, 5)
|
|
70
|
+
const frames = lines.map(normalizeFrame).filter(Boolean)
|
|
71
|
+
if (frames.length > 0) return frames.join('|').slice(0, 120)
|
|
72
|
+
}
|
|
73
|
+
if (err && err.message) return String(err.message).slice(0, 120)
|
|
74
|
+
return 'unknown'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Strip credential-shaped values from a free-form object (headers, URL query).
|
|
79
|
+
* Returns a new object with sensitive keys replaced by a marker.
|
|
80
|
+
*/
|
|
81
|
+
function scrubSensitive(input) {
|
|
82
|
+
if (!input || typeof input !== 'object') return input
|
|
83
|
+
const out = {}
|
|
84
|
+
for (const [key, value] of Object.entries(input)) {
|
|
85
|
+
const k = String(key)
|
|
86
|
+
const isSensitiveHeader = SENSITIVE_HEADER_PATTERNS.some(p => p.test(k))
|
|
87
|
+
const isSensitiveQuery = SENSITIVE_QUERY_PATTERNS.some(p => p.test(k))
|
|
88
|
+
if (isSensitiveHeader || isSensitiveQuery) {
|
|
89
|
+
out[k] = '[redacted]'
|
|
90
|
+
} else {
|
|
91
|
+
out[k] = value
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return out
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Sanitize crash context before it is written to disk.
|
|
99
|
+
* Only env key names are kept (never values); request metadata is scrubbed.
|
|
100
|
+
* @param {{ envKeys?: string[], requestUrl?: string, headers?: Record<string, string> }} input
|
|
101
|
+
* @returns {Record<string, unknown>}
|
|
102
|
+
*/
|
|
103
|
+
function sanitizeCrashData(input) {
|
|
104
|
+
const out = {}
|
|
105
|
+
if (Array.isArray(input.envKeys)) {
|
|
106
|
+
out.envKeys = input.envKeys.filter(k => typeof k === 'string')
|
|
107
|
+
}
|
|
108
|
+
if (typeof input.requestUrl === 'string') {
|
|
109
|
+
try {
|
|
110
|
+
const u = new URL(input.requestUrl)
|
|
111
|
+
out.requestUrl = u.origin + u.pathname
|
|
112
|
+
if (u.search) {
|
|
113
|
+
const params = new URLSearchParams(u.search)
|
|
114
|
+
const scrubbed = {}
|
|
115
|
+
for (const [k, v] of params) {
|
|
116
|
+
scrubbed[k] = SENSITIVE_QUERY_PATTERNS.some(p => p.test(k + '=')) ? '[redacted]' : v
|
|
117
|
+
}
|
|
118
|
+
out.requestQuery = scrubbed
|
|
119
|
+
}
|
|
120
|
+
} catch (_) {
|
|
121
|
+
out.requestUrl = '[invalid-url]'
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (input.headers && typeof input.headers === 'object') {
|
|
125
|
+
out.headers = scrubSensitive(input.headers)
|
|
126
|
+
}
|
|
127
|
+
return out
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Record a crash dump to <home>/crash/<fingerprint>-<ts>.json.
|
|
132
|
+
* Dedupes by fingerprint (max MAX_FILES_PER_FINGERPRINT). Never throws.
|
|
133
|
+
* @param {{ home: string, error: Error, version?: string, platform?: string,
|
|
134
|
+
* envKeys?: string[], requestUrl?: string, headers?: Record<string, string>,
|
|
135
|
+
* context?: Record<string, unknown>, now?: number }} opts
|
|
136
|
+
* @returns {string} the written file path (or '' when the write failed)
|
|
137
|
+
*/
|
|
138
|
+
function recordCrash(opts) {
|
|
139
|
+
try {
|
|
140
|
+
const home = opts.home
|
|
141
|
+
const error = opts.error || new Error('unknown')
|
|
142
|
+
const now = opts.now !== undefined ? opts.now : Date.now()
|
|
143
|
+
const fingerprint = crashFingerprint(error)
|
|
144
|
+
const crashDir = path.join(home, CRASH_DIR)
|
|
145
|
+
fs.mkdirSync(crashDir, { recursive: true })
|
|
146
|
+
|
|
147
|
+
// Dedupe: evict oldest files of the same fingerprint beyond the cap.
|
|
148
|
+
const existing = fs.readdirSync(crashDir)
|
|
149
|
+
.filter(f => f.startsWith(fingerprint + '-'))
|
|
150
|
+
.sort()
|
|
151
|
+
while (existing.length >= MAX_FILES_PER_FINGERPRINT) {
|
|
152
|
+
const oldest = existing.shift()
|
|
153
|
+
if (oldest) {
|
|
154
|
+
try { fs.unlinkSync(path.join(crashDir, oldest)) } catch (_) {}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const sanitized = sanitizeCrashData({
|
|
159
|
+
envKeys: opts.envKeys,
|
|
160
|
+
requestUrl: opts.requestUrl,
|
|
161
|
+
headers: opts.headers,
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
const data = {
|
|
165
|
+
fingerprint,
|
|
166
|
+
timestamp: now,
|
|
167
|
+
version: opts.version || '',
|
|
168
|
+
platform: opts.platform || `${os.platform()}-${os.arch()}`,
|
|
169
|
+
error: {
|
|
170
|
+
name: error.name || 'Error',
|
|
171
|
+
message: error.message || String(error),
|
|
172
|
+
stack: typeof error.stack === 'string' ? error.stack : String(error),
|
|
173
|
+
},
|
|
174
|
+
...sanitized,
|
|
175
|
+
context: opts.context || {},
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const file = path.join(crashDir, `${fingerprint}-${now}.json`)
|
|
179
|
+
fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf8')
|
|
180
|
+
return file
|
|
181
|
+
} catch (_) {
|
|
182
|
+
return ''
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
module.exports = {
|
|
187
|
+
crashFingerprint,
|
|
188
|
+
sanitizeCrashData,
|
|
189
|
+
recordCrash,
|
|
190
|
+
CRASH_DIR,
|
|
191
|
+
MAX_FILES_PER_FINGERPRINT,
|
|
192
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect-tunnel.d.ts","sourceRoot":"","sources":["../../core/connect-tunnel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAEjC,eAAO,MAAM,4BAA4B,uEAC+B,CAAA;AAExE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAI7D;AAED,wBAAgB,sBAAsB,CAClC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,MAAqC,GAChD,IAAI,
|
|
1
|
+
{"version":3,"file":"connect-tunnel.d.ts","sourceRoot":"","sources":["../../core/connect-tunnel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAEjC,eAAO,MAAM,4BAA4B,uEAC+B,CAAA;AAExE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAI7D;AAED,wBAAgB,sBAAsB,CAClC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,MAAqC,GAChD,IAAI,CAsCN"}
|
|
@@ -34,8 +34,14 @@ function establishConnectTunnel(client, upstream, head, response = exports.CONNE
|
|
|
34
34
|
}
|
|
35
35
|
client.once('close', closeBoth);
|
|
36
36
|
upstream.once('close', closeBoth);
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
// Persistent error listeners: a tunnel socket can emit 'error' more
|
|
38
|
+
// than once (e.g. an RST while the other side is still draining). With
|
|
39
|
+
// `once` the listener would be consumed by the first error and a second
|
|
40
|
+
// emission would crash the process via uncaughtException (observed in
|
|
41
|
+
// the deno binary). `on` keeps the bridge protected for the socket's
|
|
42
|
+
// lifetime; closeBoth is idempotent.
|
|
43
|
+
client.on('error', closeBoth);
|
|
44
|
+
upstream.on('error', closeBoth);
|
|
39
45
|
if (head.length > 0)
|
|
40
46
|
upstream.write(head);
|
|
41
47
|
client.pipe(upstream);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect-tunnel.js","sourceRoot":"","sources":["../../core/connect-tunnel.ts"],"names":[],"mappings":";;;AAKA,sDAIC;AAED,
|
|
1
|
+
{"version":3,"file":"connect-tunnel.js","sourceRoot":"","sources":["../../core/connect-tunnel.ts"],"names":[],"mappings":";;;AAKA,sDAIC;AAED,wDA2CC;AApDY,QAAA,4BAA4B,GACrC,oEAAoE,CAAA;AAExE,SAAgB,qBAAqB,CAAC,KAAc;IAChD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAC3E,MAAM,IAAI,GAAI,KAA+B,CAAC,IAAI,CAAA;IAClD,OAAO,IAAI,KAAK,YAAY,IAAI,IAAI,KAAK,OAAO,CAAA;AACpD,CAAC;AAED,SAAgB,sBAAsB,CAClC,MAAc,EACd,QAAgB,EAChB,IAAY,EACZ,WAAmB,oCAA4B;IAE/C,IAAI,MAAM,GAAG,KAAK,CAAA;IAClB,MAAM,SAAS,GAAG,GAAS,EAAE;QACzB,IAAI,MAAM;YAAE,OAAM;QAClB,MAAM,GAAG,IAAI,CAAA;QAEb,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QACvB,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QACvB,IAAI,CAAC,MAAM,CAAC,SAAS;YAAE,MAAM,CAAC,OAAO,EAAE,CAAA;QACvC,IAAI,CAAC,QAAQ,CAAC,SAAS;YAAE,QAAQ,CAAC,OAAO,EAAE,CAAA;IAC/C,CAAC,CAAA;IAED,IAAI,MAAM,CAAC,SAAS,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;QACzC,SAAS,EAAE,CAAA;QACX,OAAM;IACV,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,EAAE;QACxB,IAAI,MAAM,CAAC,SAAS,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;YACzC,SAAS,EAAE,CAAA;YACX,OAAM;QACV,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;QAC/B,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;QACjC,oEAAoE;QACpE,uEAAuE;QACvE,wEAAwE;QACxE,sEAAsE;QACtE,qEAAqE;QACrE,qCAAqC;QACrC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;QAC7B,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;QAE/B,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACzC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACrB,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACzB,CAAC,CAAC,CAAA;AACN,CAAC"}
|
package/dist/core/h2-pool.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import * as http2 from 'http2';
|
|
1
2
|
import type { ProxyResponse } from './types';
|
|
2
3
|
export declare const UPSTREAM_TIMEOUT_CODE = "UPSTREAM_TIMEOUT";
|
|
3
4
|
export declare function cleanHeadersForH2(headers: Record<string, any>): Record<string, any>;
|
|
5
|
+
export declare function __setH2SessionFactoryForTest(factory: ((origin: string, opts: http2.SecureClientSessionOptions) => http2.ClientHttp2Session) | null): void;
|
|
4
6
|
export declare function makeProxyRequest(target: string, method: string, headers: Record<string, any>, reqBody: Buffer): Promise<ProxyResponse>;
|
|
5
7
|
//# sourceMappingURL=h2-pool.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"h2-pool.d.ts","sourceRoot":"","sources":["../../core/h2-pool.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"h2-pool.d.ts","sourceRoot":"","sources":["../../core/h2-pool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAc5C,eAAO,MAAM,qBAAqB,qBAAqB,CAAA;AA2BvD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CASnF;AAKD,wBAAgB,4BAA4B,CACxC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,0BAA0B,KAAK,KAAK,CAAC,kBAAkB,CAAC,GAAG,IAAI,GACvG,IAAI,CAEN;AA2HD,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAU5I"}
|
package/dist/core/h2-pool.js
CHANGED
|
@@ -38,6 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.UPSTREAM_TIMEOUT_CODE = void 0;
|
|
40
40
|
exports.cleanHeadersForH2 = cleanHeadersForH2;
|
|
41
|
+
exports.__setH2SessionFactoryForTest = __setH2SessionFactoryForTest;
|
|
41
42
|
exports.makeProxyRequest = makeProxyRequest;
|
|
42
43
|
const http = __importStar(require("http"));
|
|
43
44
|
const https = __importStar(require("https"));
|
|
@@ -92,6 +93,15 @@ function cleanHeadersForH2(headers) {
|
|
|
92
93
|
}
|
|
93
94
|
return cleaned;
|
|
94
95
|
}
|
|
96
|
+
// Test-only hook: override the session constructor so specs can inject a fake
|
|
97
|
+
// http2 session (e.g. one that emits 'error' repeatedly). Do not call from prod.
|
|
98
|
+
let sessionFactoryOverride = null;
|
|
99
|
+
function __setH2SessionFactoryForTest(factory) {
|
|
100
|
+
sessionFactoryOverride = factory;
|
|
101
|
+
}
|
|
102
|
+
function createH2Session(origin, opts) {
|
|
103
|
+
return sessionFactoryOverride ? sessionFactoryOverride(origin, opts) : http2.connect(origin, opts);
|
|
104
|
+
}
|
|
95
105
|
function getOrCreateH2Session(origin, servername) {
|
|
96
106
|
const poolKey = servername ? `${origin}#${servername}` : origin;
|
|
97
107
|
const cached = h2SessionPool.get(poolKey);
|
|
@@ -103,7 +113,7 @@ function getOrCreateH2Session(origin, servername) {
|
|
|
103
113
|
const connectOpts = { rejectUnauthorized: false };
|
|
104
114
|
if (servername)
|
|
105
115
|
connectOpts.servername = servername;
|
|
106
|
-
const session =
|
|
116
|
+
const session = createH2Session(origin, connectOpts);
|
|
107
117
|
const timeout = setTimeout(() => {
|
|
108
118
|
session.destroy();
|
|
109
119
|
const err = new Error(`HTTP/2 连接超时: 5s 内未能与 ${origin} 建立 HTTP/2 连接(上游不支持 H2 或网络不通)`);
|
|
@@ -116,10 +126,17 @@ function getOrCreateH2Session(origin, servername) {
|
|
|
116
126
|
h2SessionPool.set(poolKey, session);
|
|
117
127
|
resolve(session);
|
|
118
128
|
});
|
|
119
|
-
session
|
|
129
|
+
// Persistent error listener: a session that errored once must be
|
|
130
|
+
// evicted AND destroyed so it is never reused. Using `once` would drop
|
|
131
|
+
// the listener after the first error — a second error (e.g. an RST
|
|
132
|
+
// while a concurrent caller still holds the session) had no listener
|
|
133
|
+
// and crashed the whole process via uncaughtException.
|
|
134
|
+
session.on('error', (err) => {
|
|
120
135
|
clearTimeout(timeout);
|
|
121
136
|
h2SessionPool.delete(poolKey);
|
|
122
|
-
|
|
137
|
+
if (!session.destroyed)
|
|
138
|
+
session.destroy();
|
|
139
|
+
reject(err); // no-op after the promise has settled
|
|
123
140
|
});
|
|
124
141
|
session.on('close', () => { h2SessionPool.delete(poolKey); });
|
|
125
142
|
session.on('goaway', () => {
|
package/dist/core/h2-pool.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"h2-pool.js","sourceRoot":"","sources":["../../core/h2-pool.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,8CASC;
|
|
1
|
+
{"version":3,"file":"h2-pool.js","sourceRoot":"","sources":["../../core/h2-pool.ts"],"names":[],"mappings":"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"}
|
package/index.js
CHANGED
|
@@ -22,6 +22,8 @@ const {
|
|
|
22
22
|
} = require('./dist/core/client-identity')
|
|
23
23
|
const { createApplicationIdentityResolver } = require('./dist/core/application-identity')
|
|
24
24
|
const { runAsyncUpdateCheck } = require('./bin/lib/update-check')
|
|
25
|
+
const { resolveMeddleHome } = require('./bin/lib/meddle-home')
|
|
26
|
+
const { recordCrash, crashFingerprint } = require('./bin/lib/crash-report')
|
|
25
27
|
const {
|
|
26
28
|
authorizeProxyClient,
|
|
27
29
|
buildRemoteAccessConfig,
|
|
@@ -748,6 +750,15 @@ proxyServer.on('connect', async (req, socket, head) => {
|
|
|
748
750
|
markMitmServerUsed(server)
|
|
749
751
|
server._epActiveSockets = new Set()
|
|
750
752
|
|
|
753
|
+
// A client resetting its TLS connection (ECONNRESET) can surface
|
|
754
|
+
// as an 'error' on the http2 server. Without a listener this
|
|
755
|
+
// would propagate to uncaughtException and kill the whole proxy.
|
|
756
|
+
server.on('error', (err) => {
|
|
757
|
+
if (!isExpectedSocketError(err)) {
|
|
758
|
+
proxyDebug('MITM server error', originHost, getErrorMessage(err))
|
|
759
|
+
}
|
|
760
|
+
})
|
|
761
|
+
|
|
751
762
|
server._epRegisterClientIdentity = (remotePort, identity) => {
|
|
752
763
|
clientIdentityRegistry.register(remotePort, identity)
|
|
753
764
|
}
|
|
@@ -968,6 +979,28 @@ function handleFatalError(type, reason) {
|
|
|
968
979
|
if (!fatalErrorInProgress) {
|
|
969
980
|
fatalErrorInProgress = true
|
|
970
981
|
writeFatalError(type, reason)
|
|
982
|
+
|
|
983
|
+
// 崩溃采集: 落盘现场(指纹去重、脱敏),便于事后定位
|
|
984
|
+
const err = reason instanceof Error ? reason : new Error(String(reason))
|
|
985
|
+
try {
|
|
986
|
+
const home = resolveMeddleHome()
|
|
987
|
+
const crashFile = recordCrash({
|
|
988
|
+
home,
|
|
989
|
+
error: err,
|
|
990
|
+
version: require('./package.json').version,
|
|
991
|
+
envKeys: Object.keys(process.env || {}),
|
|
992
|
+
context: {
|
|
993
|
+
fatalType: type,
|
|
994
|
+
connections: activeProxySockets.size,
|
|
995
|
+
mitmServers: ctx.httpsServerMap ? ctx.httpsServerMap.size : 0,
|
|
996
|
+
},
|
|
997
|
+
})
|
|
998
|
+
if (crashFile) {
|
|
999
|
+
console.error(`[fatal] 崩溃报告已保存: ${crashFile} (指纹 ${crashFingerprint(err)})`)
|
|
1000
|
+
}
|
|
1001
|
+
} catch (_) {
|
|
1002
|
+
// 崩溃报告失败不影响退出
|
|
1003
|
+
}
|
|
971
1004
|
}
|
|
972
1005
|
|
|
973
1006
|
process.exit(1)
|
package/package.json
CHANGED