com.jimuwd.xian.registry-proxy 1.0.60 → 1.0.63

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.
Files changed (2) hide show
  1. package/dist/index.js +40 -24
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -160,8 +160,7 @@ async function fetchFromRegistry(registry, targetUrl, limiter) {
160
160
  limiter.release();
161
161
  }
162
162
  }
163
- // 修改后的 writeSuccessfulResponse 函数
164
- async function writeSuccessfulResponse(registryInfo, targetUrl, res, upstreamResponse, req, proxyInfo, proxyPort, registryInfos) {
163
+ async function writeResponseToDownstreamClient(registryInfo, targetUrl, resToDownstreamClient, upstreamResponse, downstreamReq, proxyInfo, proxyPort, registryInfos) {
165
164
  if (!upstreamResponse.ok)
166
165
  throw new Error("Only 2xx upstream response is supported");
167
166
  try {
@@ -184,7 +183,7 @@ async function writeSuccessfulResponse(registryInfo, targetUrl, res, upstreamRes
184
183
  // JSON 处理逻辑
185
184
  const data = await upstreamResponse.json();
186
185
  if (data.versions) {
187
- const host = req.headers.host || `localhost:${proxyPort}`;
186
+ const host = downstreamReq.headers.host || `localhost:${proxyPort}`;
188
187
  const baseUrl = `${proxyInfo.https ? 'https' : 'http'}://${host}${proxyInfo.basePath === '/' ? '' : proxyInfo.basePath}`;
189
188
  for (const versionKey in data.versions) {
190
189
  const packageVersion = data.versions[versionKey];
@@ -196,32 +195,49 @@ async function writeSuccessfulResponse(registryInfo, targetUrl, res, upstreamRes
196
195
  }
197
196
  }
198
197
  }
199
- res.writeHead(upstreamResponse.status, { "content-type": contentType }).end(JSON.stringify(data));
198
+ resToDownstreamClient.writeHead(upstreamResponse.status, { "content-type": contentType }).end(JSON.stringify(data));
200
199
  }
201
200
  else {
202
201
  // 二进制流处理
203
202
  if (!upstreamResponse.body) {
204
203
  logger.error(`Empty response body from ${targetUrl}`);
205
- res.writeHead(502).end('Empty Upstream Response');
204
+ resToDownstreamClient.writeHead(502).end('Empty Upstream Response');
206
205
  }
207
206
  else {
208
207
  // write back to client
209
- res.writeHead(upstreamResponse.status, safeHeaders);
208
+ resToDownstreamClient.writeHead(upstreamResponse.status, safeHeaders);
210
209
  // stop transfer if client is closed accidentally.
211
- // req.on('close', () => upstreamResponse.body?.unpipe());
210
+ const cleanup = () => {
211
+ downstreamReq.off('close', cleanup);
212
+ logger.warn("Stop transfer when client is closed accidentally.");
213
+ upstreamResponse.body?.unpipe();
214
+ };
215
+ downstreamReq.on('close', cleanup);
212
216
  // write back body data (chunked probably)
217
+ // pipe upstream body to downstream client and end when upstream ends.
218
+ upstreamResponse.body.pipe(resToDownstreamClient, { end: true });
213
219
  upstreamResponse.body
214
- .on('data', (chunk) => res.write(chunk))
215
- .on('end', () => res.end())
216
- .on('close', () => res.destroy(new Error(`Upstream server ${registryInfo.normalizedRegistryUrl} closed connection while transferring data on url ${targetUrl}`)))
217
- .on('error', (err) => res.destroy(new Error(`Stream error: ${err.message}`, { cause: err, })));
220
+ .on('data', (chunk) => logger.info(`chunk transferred size=${chunk.length}`))
221
+ .on('end', () => {
222
+ logger.info(`Upstream server ${registryInfo.normalizedRegistryUrl} response.body ended on url ${targetUrl}`);
223
+ })
224
+ .on('close', () => {
225
+ const errMsg = `Upstream server ${registryInfo.normalizedRegistryUrl} closed connection while transferring data on url ${targetUrl}`;
226
+ logger.error(errMsg);
227
+ // resToDownstreamClient.destroy(new Error(errMsg));
228
+ })
229
+ .on('error', (err) => {
230
+ const errMsg = `Stream error: ${err.message}`;
231
+ logger.error(errMsg);
232
+ resToDownstreamClient.destroy(new Error(errMsg, { cause: err, }));
233
+ });
218
234
  }
219
235
  }
220
236
  }
221
237
  catch (err) {
222
238
  logger.error('Failed to write upstreamResponse:', err);
223
- if (!res.headersSent) {
224
- res.writeHead(502).end('Internal Server Error');
239
+ if (!resToDownstreamClient.headersSent) {
240
+ resToDownstreamClient.writeHead(502).end('Internal Server Error');
225
241
  }
226
242
  }
227
243
  }
@@ -233,41 +249,41 @@ export async function startProxyServer(proxyConfigPath, localYarnConfigPath, glo
233
249
  logger.info('Proxy base path:', basePathPrefixedWithSlash);
234
250
  logger.info('HTTPS:', !!proxyInfo.https);
235
251
  let proxyPort;
236
- const requestHandler = async (req, res) => {
237
- if (!req.url || !req.headers.host) {
238
- res.writeHead(400).end('Invalid Request');
252
+ const requestHandler = async (reqFromDownstreamClient, resToDownstreamClient) => {
253
+ if (!reqFromDownstreamClient.url || !reqFromDownstreamClient.headers.host) {
254
+ resToDownstreamClient.writeHead(400).end('Invalid Request');
239
255
  return;
240
256
  }
241
- const fullUrl = new URL(req.url, `${proxyInfo.https ? 'https' : 'http'}://${req.headers.host}`);
257
+ const fullUrl = new URL(reqFromDownstreamClient.url, `${proxyInfo.https ? 'https' : 'http'}://${reqFromDownstreamClient.headers.host}`);
242
258
  if (!fullUrl.pathname.startsWith(basePathPrefixedWithSlash)) {
243
- res.writeHead(404).end('Not Found');
259
+ resToDownstreamClient.writeHead(404).end('Not Found');
244
260
  return;
245
261
  }
246
262
  const path = basePathPrefixedWithSlash === '/'
247
263
  ? fullUrl.pathname
248
264
  : fullUrl.pathname.slice(basePathPrefixedWithSlash.length);
249
265
  // 顺序尝试注册表,获取第一个成功响应
250
- let successfulResponse = null;
266
+ let successfulResponseFromUpstream = null;
251
267
  let targetRegistry = null;
252
268
  let targetUrl = null;
253
269
  for (const registry of registryInfos) {
254
- if (req.destroyed)
270
+ if (reqFromDownstreamClient.destroyed)
255
271
  break;
256
272
  targetRegistry = registry;
257
273
  const search = fullUrl.search || '';
258
274
  targetUrl = `${registry.normalizedRegistryUrl}${path}${search}`;
259
275
  const okResponseOrNull = await fetchFromRegistry(registry, targetUrl, limiter);
260
276
  if (okResponseOrNull) {
261
- successfulResponse = okResponseOrNull;
277
+ successfulResponseFromUpstream = okResponseOrNull;
262
278
  break;
263
279
  }
264
280
  }
265
281
  // 统一回写响应
266
- if (successfulResponse) {
267
- await writeSuccessfulResponse(targetRegistry, targetUrl, res, successfulResponse, req, proxyInfo, proxyPort, registryInfos);
282
+ if (successfulResponseFromUpstream) {
283
+ await writeResponseToDownstreamClient(targetRegistry, targetUrl, resToDownstreamClient, successfulResponseFromUpstream, reqFromDownstreamClient, proxyInfo, proxyPort, registryInfos);
268
284
  }
269
285
  else {
270
- res.writeHead(404).end('All upstream registries failed');
286
+ resToDownstreamClient.writeHead(404).end('All upstream registries failed');
271
287
  }
272
288
  };
273
289
  let server;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.jimuwd.xian.registry-proxy",
3
- "version": "1.0.60",
3
+ "version": "1.0.63",
4
4
  "description": "A lightweight npm registry proxy with fallback support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",