com.jimuwd.xian.registry-proxy 1.0.36 → 1.0.37
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/dist/index.js +51 -46
- package/package.json +1 -1
- package/src/index.ts +59 -59
package/dist/index.js
CHANGED
|
@@ -139,10 +139,9 @@ async function loadProxyInfo(proxyConfigPath = './.registry-proxy.yml', localYar
|
|
|
139
139
|
const basePath = removeEndingSlashAndForceStartingSlash(proxyConfig.basePath);
|
|
140
140
|
return { registries, https, basePath };
|
|
141
141
|
}
|
|
142
|
-
async function fetchFromRegistry(registry,
|
|
142
|
+
async function fetchFromRegistry(registry, targetUrl, limiter) {
|
|
143
143
|
await limiter.acquire();
|
|
144
144
|
try {
|
|
145
|
-
const targetUrl = `${registry.normalizedRegistryUrl}${path}${search || ''}`;
|
|
146
145
|
console.log(`Fetching from: ${targetUrl}`);
|
|
147
146
|
const headers = registry.token ? { Authorization: `Bearer ${registry.token}` } : undefined;
|
|
148
147
|
const response = await fetch(targetUrl, { headers });
|
|
@@ -161,25 +160,28 @@ async function fetchFromRegistry(registry, path, search, limiter) {
|
|
|
161
160
|
limiter.release();
|
|
162
161
|
}
|
|
163
162
|
}
|
|
164
|
-
// 修改后的
|
|
165
|
-
async function
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const safeHeaders = {
|
|
169
|
-
'content-type': contentType,
|
|
170
|
-
'connection': 'keep-alive'
|
|
171
|
-
};
|
|
172
|
-
// 复制所有可能需要的头信息
|
|
173
|
-
const headersToCopy = [
|
|
174
|
-
'cache-control', 'etag', 'last-modified',
|
|
175
|
-
'content-encoding', 'content-length'
|
|
176
|
-
];
|
|
177
|
-
headersToCopy.forEach(header => {
|
|
178
|
-
const value = response.headers.get(header);
|
|
179
|
-
if (value)
|
|
180
|
-
safeHeaders[header] = value;
|
|
181
|
-
});
|
|
163
|
+
// 修改后的 writeSuccessfulResponse 函数
|
|
164
|
+
async function writeSuccessfulResponse(registryInfo, targetUrl, res, response, req, proxyInfo, proxyPort, registryInfos) {
|
|
165
|
+
if (!response.ok)
|
|
166
|
+
throw new Error("Only 2xx response is supported");
|
|
182
167
|
try {
|
|
168
|
+
const contentType = response.headers.get('Content-Type') || 'application/octet-stream';
|
|
169
|
+
// 准备通用头信息
|
|
170
|
+
const safeHeaders = {
|
|
171
|
+
'content-type': contentType,
|
|
172
|
+
'connection': 'keep-alive'
|
|
173
|
+
};
|
|
174
|
+
// 复制所有可能需要的头信息
|
|
175
|
+
const headersToCopy = [
|
|
176
|
+
'cache-control', 'etag', 'last-modified',
|
|
177
|
+
'content-encoding', 'content-length'
|
|
178
|
+
];
|
|
179
|
+
headersToCopy.forEach(header => {
|
|
180
|
+
const value = response.headers.get(header);
|
|
181
|
+
if (value)
|
|
182
|
+
safeHeaders[header] = value;
|
|
183
|
+
});
|
|
184
|
+
res.writeHead(response.status, safeHeaders);
|
|
183
185
|
if (contentType.includes('application/json')) {
|
|
184
186
|
// JSON 处理逻辑
|
|
185
187
|
const data = await response.json();
|
|
@@ -194,38 +196,36 @@ async function writeResponse(res, response, req, proxyInfo, proxyPort, registryI
|
|
|
194
196
|
}
|
|
195
197
|
}
|
|
196
198
|
}
|
|
197
|
-
res.writeHead(200, safeHeaders);
|
|
198
199
|
res.end(JSON.stringify(data));
|
|
199
200
|
}
|
|
200
201
|
else {
|
|
201
202
|
// 二进制流处理
|
|
202
203
|
if (!response.body) {
|
|
203
|
-
console.error(
|
|
204
|
-
process.exit(1);
|
|
205
|
-
}
|
|
206
|
-
// 修复流类型转换问题
|
|
207
|
-
let nodeStream;
|
|
208
|
-
if (typeof Readable.fromWeb === 'function') {
|
|
209
|
-
// Node.js 17+ 标准方式
|
|
210
|
-
nodeStream = Readable.fromWeb(response.body);
|
|
204
|
+
console.error(`Empty response body from ${targetUrl}`);
|
|
211
205
|
}
|
|
212
206
|
else {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
passThrough
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
207
|
+
let nodeStream;
|
|
208
|
+
if (typeof Readable.fromWeb === 'function') {
|
|
209
|
+
// Node.js 17+ 标准方式
|
|
210
|
+
nodeStream = Readable.fromWeb(response.body);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
// Node.js 16 及以下版本的兼容方案
|
|
214
|
+
const { PassThrough } = await import('stream');
|
|
215
|
+
const passThrough = new PassThrough();
|
|
216
|
+
const reader = response.body.getReader();
|
|
217
|
+
const pump = async () => {
|
|
218
|
+
const { done, value } = await reader.read();
|
|
219
|
+
if (done)
|
|
220
|
+
return passThrough.end();
|
|
221
|
+
passThrough.write(value);
|
|
222
|
+
await pump();
|
|
223
|
+
};
|
|
224
|
+
pump().catch(err => passThrough.destroy(err));
|
|
225
|
+
nodeStream = passThrough;
|
|
226
|
+
}
|
|
227
|
+
await pipeline(nodeStream, res);
|
|
226
228
|
}
|
|
227
|
-
res.writeHead(response.status, safeHeaders);
|
|
228
|
-
await pipeline(nodeStream, res);
|
|
229
229
|
}
|
|
230
230
|
}
|
|
231
231
|
catch (err) {
|
|
@@ -258,10 +258,15 @@ export async function startProxyServer(proxyConfigPath, localYarnConfigPath, glo
|
|
|
258
258
|
: fullUrl.pathname.slice(basePathPrefixedWithSlash.length);
|
|
259
259
|
// 顺序尝试注册表,获取第一个成功响应
|
|
260
260
|
let successfulResponse = null;
|
|
261
|
+
let targetRegistry = null;
|
|
262
|
+
let targetUrl = null;
|
|
261
263
|
for (const registry of registryInfos) {
|
|
262
264
|
if (req.destroyed)
|
|
263
265
|
break;
|
|
264
|
-
|
|
266
|
+
targetRegistry = registry;
|
|
267
|
+
const search = fullUrl.search || '';
|
|
268
|
+
targetUrl = `${registry.normalizedRegistryUrl}${path}${search}`;
|
|
269
|
+
const response = await fetchFromRegistry(registry, targetUrl, limiter);
|
|
265
270
|
if (response) {
|
|
266
271
|
successfulResponse = response;
|
|
267
272
|
break;
|
|
@@ -269,7 +274,7 @@ export async function startProxyServer(proxyConfigPath, localYarnConfigPath, glo
|
|
|
269
274
|
}
|
|
270
275
|
// 统一回写响应
|
|
271
276
|
if (successfulResponse) {
|
|
272
|
-
await
|
|
277
|
+
await writeSuccessfulResponse(targetRegistry, targetUrl, res, successfulResponse, req, proxyInfo, proxyPort, registryInfos);
|
|
273
278
|
}
|
|
274
279
|
else {
|
|
275
280
|
res.writeHead(404).end('All upstream registries failed');
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -189,16 +189,14 @@ async function loadProxyInfo(
|
|
|
189
189
|
|
|
190
190
|
async function fetchFromRegistry(
|
|
191
191
|
registry: RegistryInfo,
|
|
192
|
-
|
|
193
|
-
search: string,
|
|
192
|
+
targetUrl: string,
|
|
194
193
|
limiter: ConcurrencyLimiter
|
|
195
194
|
): Promise<Response | null> {
|
|
196
195
|
await limiter.acquire();
|
|
197
196
|
try {
|
|
198
|
-
const targetUrl = `${registry.normalizedRegistryUrl}${path}${search || ''}`;
|
|
199
197
|
console.log(`Fetching from: ${targetUrl}`);
|
|
200
|
-
const headers = registry.token ? {
|
|
201
|
-
const response = await fetch(targetUrl, {
|
|
198
|
+
const headers = registry.token ? {Authorization: `Bearer ${registry.token}`} : undefined;
|
|
199
|
+
const response = await fetch(targetUrl, {headers});
|
|
202
200
|
console.log(`Response from ${targetUrl}: ${response.status} ${response.statusText}`);
|
|
203
201
|
return response.ok ? response : null;
|
|
204
202
|
} catch (e) {
|
|
@@ -215,8 +213,10 @@ async function fetchFromRegistry(
|
|
|
215
213
|
}
|
|
216
214
|
}
|
|
217
215
|
|
|
218
|
-
// 修改后的
|
|
219
|
-
async function
|
|
216
|
+
// 修改后的 writeSuccessfulResponse 函数
|
|
217
|
+
async function writeSuccessfulResponse(
|
|
218
|
+
registryInfo: RegistryInfo,
|
|
219
|
+
targetUrl: string,
|
|
220
220
|
res: ServerResponse,
|
|
221
221
|
response: Response,
|
|
222
222
|
req: IncomingMessage,
|
|
@@ -224,26 +224,32 @@ async function writeResponse(
|
|
|
224
224
|
proxyPort: number,
|
|
225
225
|
registryInfos: RegistryInfo[]
|
|
226
226
|
): Promise<void> {
|
|
227
|
-
const contentType = response.headers.get('Content-Type') || 'application/octet-stream';
|
|
228
227
|
|
|
229
|
-
|
|
230
|
-
const safeHeaders: OutgoingHttpHeaders = {
|
|
231
|
-
'content-type': contentType,
|
|
232
|
-
'connection': 'keep-alive'
|
|
233
|
-
};
|
|
228
|
+
if (!response.ok) throw new Error("Only 2xx response is supported");
|
|
234
229
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
'cache-control', 'etag', 'last-modified',
|
|
238
|
-
'content-encoding', 'content-length'
|
|
239
|
-
];
|
|
230
|
+
try {
|
|
231
|
+
const contentType = response.headers.get('Content-Type') || 'application/octet-stream';
|
|
240
232
|
|
|
241
|
-
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
233
|
+
// 准备通用头信息
|
|
234
|
+
const safeHeaders: OutgoingHttpHeaders = {
|
|
235
|
+
'content-type': contentType,
|
|
236
|
+
'connection': 'keep-alive'
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// 复制所有可能需要的头信息
|
|
240
|
+
const headersToCopy = [
|
|
241
|
+
'cache-control', 'etag', 'last-modified',
|
|
242
|
+
'content-encoding', 'content-length'
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
headersToCopy.forEach(header => {
|
|
246
|
+
const value = response.headers.get(header);
|
|
247
|
+
if (value) safeHeaders[header] = value;
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
res.writeHead(response.status, safeHeaders);
|
|
245
252
|
|
|
246
|
-
try {
|
|
247
253
|
if (contentType.includes('application/json')) {
|
|
248
254
|
// JSON 处理逻辑
|
|
249
255
|
const data = await response.json() as PackageData;
|
|
@@ -259,39 +265,35 @@ async function writeResponse(
|
|
|
259
265
|
}
|
|
260
266
|
}
|
|
261
267
|
}
|
|
262
|
-
res.writeHead(200, safeHeaders);
|
|
263
268
|
res.end(JSON.stringify(data));
|
|
264
269
|
} else {
|
|
265
270
|
// 二进制流处理
|
|
266
271
|
if (!response.body) {
|
|
267
|
-
console.error(
|
|
268
|
-
process.exit(1);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// 修复流类型转换问题
|
|
272
|
-
let nodeStream: NodeJS.ReadableStream;
|
|
273
|
-
if (typeof Readable.fromWeb === 'function') {
|
|
274
|
-
// Node.js 17+ 标准方式
|
|
275
|
-
nodeStream = Readable.fromWeb(response.body as any);
|
|
272
|
+
console.error(`Empty response body from ${targetUrl}`);
|
|
276
273
|
} else {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
274
|
+
let nodeStream: NodeJS.ReadableStream;
|
|
275
|
+
if (typeof Readable.fromWeb === 'function') {
|
|
276
|
+
// Node.js 17+ 标准方式
|
|
277
|
+
nodeStream = Readable.fromWeb(response.body as any);
|
|
278
|
+
} else {
|
|
279
|
+
// Node.js 16 及以下版本的兼容方案
|
|
280
|
+
const {PassThrough} = await import('stream');
|
|
281
|
+
const passThrough = new PassThrough();
|
|
282
|
+
const reader = (response.body as any).getReader();
|
|
283
|
+
|
|
284
|
+
const pump = async () => {
|
|
285
|
+
const {done, value} = await reader.read();
|
|
286
|
+
if (done) return passThrough.end();
|
|
287
|
+
passThrough.write(value);
|
|
288
|
+
await pump();
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
pump().catch(err => passThrough.destroy(err));
|
|
292
|
+
nodeStream = passThrough;
|
|
293
|
+
}
|
|
292
294
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
+
await pipeline(nodeStream, res);
|
|
296
|
+
}
|
|
295
297
|
}
|
|
296
298
|
} catch (err) {
|
|
297
299
|
console.error('Failed to write response:', err);
|
|
@@ -335,16 +337,14 @@ export async function startProxyServer(
|
|
|
335
337
|
|
|
336
338
|
// 顺序尝试注册表,获取第一个成功响应
|
|
337
339
|
let successfulResponse: Response | null = null;
|
|
340
|
+
let targetRegistry: RegistryInfo | null = null;
|
|
341
|
+
let targetUrl: string | null = null;
|
|
338
342
|
for (const registry of registryInfos) {
|
|
339
343
|
if (req.destroyed) break;
|
|
340
|
-
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
fullUrl.search,
|
|
345
|
-
limiter
|
|
346
|
-
);
|
|
347
|
-
|
|
344
|
+
targetRegistry = registry;
|
|
345
|
+
const search = fullUrl.search || '';
|
|
346
|
+
targetUrl = `${registry.normalizedRegistryUrl}${path}${search}`;
|
|
347
|
+
const response = await fetchFromRegistry(registry, targetUrl, limiter);
|
|
348
348
|
if (response) {
|
|
349
349
|
successfulResponse = response;
|
|
350
350
|
break;
|
|
@@ -353,7 +353,7 @@ export async function startProxyServer(
|
|
|
353
353
|
|
|
354
354
|
// 统一回写响应
|
|
355
355
|
if (successfulResponse) {
|
|
356
|
-
await
|
|
356
|
+
await writeSuccessfulResponse(targetRegistry!, targetUrl!, res, successfulResponse, req, proxyInfo, proxyPort, registryInfos);
|
|
357
357
|
} else {
|
|
358
358
|
res.writeHead(404).end('All upstream registries failed');
|
|
359
359
|
}
|