com.jimuwd.xian.registry-proxy 1.0.94 → 1.0.96

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 +31 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -205,6 +205,9 @@ async function writeResponseToDownstreamClient(registryInfo, targetUrl, resToDow
205
205
  }
206
206
  const bodyData = JSON.stringify(data);
207
207
  resToDownstreamClient.removeHeader('Transfer-Encoding');
208
+ // 默认是 connection: keep-alive 和 keep-alive: timeout=5,这里直接给它咔嚓掉
209
+ resToDownstreamClient.setHeader('Connection', 'close');
210
+ resToDownstreamClient.removeHeader('Keep-Alive');
208
211
  resToDownstreamClient.setHeader('content-type', 'application/json');
209
212
  resToDownstreamClient.setHeader('content-length', Buffer.byteLength(bodyData));
210
213
  logger.info(`Response to downstream client headers`, JSON.stringify(resToDownstreamClient.getHeaders()), targetUrl);
@@ -283,6 +286,15 @@ async function writeResponseToDownstreamClient(registryInfo, targetUrl, resToDow
283
286
  }
284
287
  }
285
288
  }
289
+ function getDownstreamClientIp(req) {
290
+ // 如果经过代理(如 Nginx),取 X-Forwarded-For 的第一个 IP
291
+ const forwardedFor = req.headers['x-forwarded-for'];
292
+ if (forwardedFor) {
293
+ return forwardedFor.toString().split(',')[0].trim();
294
+ }
295
+ // 直接连接时,取 socket.remoteAddress
296
+ return req.socket.remoteAddress;
297
+ }
286
298
  export async function startProxyServer(proxyConfigPath, localYarnConfigPath, globalYarnConfigPath, port = 0) {
287
299
  const proxyInfo = await loadProxyInfo(proxyConfigPath, localYarnConfigPath, globalYarnConfigPath);
288
300
  const registryInfos = proxyInfo.registries;
@@ -292,11 +304,19 @@ export async function startProxyServer(proxyConfigPath, localYarnConfigPath, glo
292
304
  logger.info('HTTPS:', !!proxyInfo.https);
293
305
  let proxyPort;
294
306
  const requestHandler = async (reqFromDownstreamClient, resToDownstreamClient) => {
295
- if (!reqFromDownstreamClient.url || !reqFromDownstreamClient.headers.host) {
307
+ const downstreamUserAgent = reqFromDownstreamClient.headers["user-agent"]; // "curl/x.x.x"
308
+ const downstreamIp = getDownstreamClientIp(reqFromDownstreamClient);
309
+ const downstreamRequestedHttpMethod = reqFromDownstreamClient.method; // "GET", "POST", etc.
310
+ const downstreamRequestedHost = reqFromDownstreamClient.headers.host; // "example.com:8080"
311
+ const downstreamRequestedFullPath = reqFromDownstreamClient.url; // "/some/path?param=1&param=2"
312
+ logger.info(`Received downstream request ${downstreamUserAgent} ${downstreamIp} ${downstreamRequestedHttpMethod} ${downstreamRequestedHost} ${downstreamRequestedFullPath}`);
313
+ if (!downstreamRequestedFullPath || !downstreamRequestedHost) {
314
+ logger.warn(`400 Invalid Request, downstream ${downstreamUserAgent} req.url is absent or downstream.headers.host is absent.`);
296
315
  resToDownstreamClient.writeHead(400).end('Invalid Request');
297
316
  return;
298
317
  }
299
- const fullUrl = new URL(reqFromDownstreamClient.url, `${proxyInfo.https ? 'https' : 'http'}://${reqFromDownstreamClient.headers.host}`);
318
+ const baseUrl = `${proxyInfo.https ? 'https' : 'http'}://${downstreamRequestedHost}`;
319
+ const fullUrl = new URL(downstreamRequestedFullPath, baseUrl);
300
320
  if (!fullUrl.pathname.startsWith(basePathPrefixedWithSlash)) {
301
321
  resToDownstreamClient.writeHead(404).end('Not Found');
302
322
  return;
@@ -304,19 +324,22 @@ export async function startProxyServer(proxyConfigPath, localYarnConfigPath, glo
304
324
  const path = basePathPrefixedWithSlash === '/'
305
325
  ? fullUrl.pathname
306
326
  : fullUrl.pathname.slice(basePathPrefixedWithSlash.length);
307
- // 顺序尝试注册表,获取第一个成功响应
327
+ const search = fullUrl.search || '';
328
+ const targetPath = path + search;
329
+ logger.info(`Proxying to ${targetPath}`);
330
+ // 按配置顺序尝试注册表,获取第一个成功响应
308
331
  let successfulResponseFromUpstream = null;
309
332
  let targetRegistry = null;
310
333
  let targetUrl = null;
311
- for (const registry of registryInfos) {
334
+ for (const registry_i of registryInfos) {
335
+ targetRegistry = registry_i;
336
+ targetUrl = `${targetRegistry.normalizedRegistryUrl}${targetPath}`;
312
337
  if (reqFromDownstreamClient.destroyed) {
313
338
  // 如果下游客户端自己提前断开(或取消)请求,那么这里不再逐个fallback方式请求(fetch)上游数据了,直接退出循环并返回
339
+ logger.warn(`Downstream ${reqFromDownstreamClient.headers["user-agent"]} request is destroyed, no need to proxy request to upstream ${targetUrl} any more.`);
314
340
  return;
315
341
  }
316
- targetRegistry = registry;
317
- const search = fullUrl.search || '';
318
- targetUrl = `${registry.normalizedRegistryUrl}${path}${search}`;
319
- const okResponseOrNull = await fetchFromRegistry(registry, targetUrl, reqFromDownstreamClient, limiter);
342
+ const okResponseOrNull = await fetchFromRegistry(targetRegistry, targetUrl, reqFromDownstreamClient, limiter);
320
343
  if (okResponseOrNull) {
321
344
  successfulResponseFromUpstream = okResponseOrNull;
322
345
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.jimuwd.xian.registry-proxy",
3
- "version": "1.0.94",
3
+ "version": "1.0.96",
4
4
  "description": "A lightweight npm registry proxy with fallback support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",