Skip to content

Commit 157f0ed

Browse files
grissom.wangclaude
andcommitted
fix: address code review feedback on HTTP proxy support
- Extract shared config-paths.js module to avoid path duplication - Fix setup-proxy.js show() hint missing 'set' subcommand - Exit with error on corrupted config.json to prevent data loss - Destroy raw socket on TLS handshake failure to prevent fd leak - Cache HttpsProxyAgent at module level for keepAlive reuse - Log warnings when proxy environment variables or config are invalid Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 1898f19 commit 157f0ed

3 files changed

Lines changed: 53 additions & 24 deletions

File tree

scripts/config-paths.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"use strict";
2+
3+
const path = require("path");
4+
const os = require("os");
5+
6+
const CONFIG_DIR = path.join(os.homedir(), ".opencodereview");
7+
const CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
8+
9+
module.exports = { CONFIG_DIR, CONFIG_PATH };

scripts/install.js

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const http = require("http");
77
const https = require("https");
88
const tls = require("tls");
99
const crypto = require("crypto");
10+
const { CONFIG_PATH } = require("./config-paths");
1011

1112
const IS_WINDOWS = process.platform === "win32";
1213
const BINARY_NAME = IS_WINDOWS ? "opencodereview.exe" : "opencodereview";
@@ -27,8 +28,6 @@ function error(msg) {
2728
console.error(`[ERROR] ${msg}`);
2829
}
2930

30-
const stateDirPath = path.join(require("os").homedir(), ".opencodereview");
31-
3231
function getProxyUrl() {
3332
// 1. env vars take highest priority
3433
const proxy =
@@ -39,19 +38,23 @@ function getProxyUrl() {
3938
if (proxy) {
4039
try {
4140
return new URL(proxy);
42-
} catch (_) {}
41+
} catch (_) {
42+
warn(`Ignoring invalid proxy env var: ${proxy}`);
43+
}
4344
}
4445

4546
// 2. fallback: read from ~/.opencodereview/config.json
4647
try {
47-
const configPath = path.join(stateDirPath, "config.json");
48-
if (fs.existsSync(configPath)) {
49-
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
48+
if (fs.existsSync(CONFIG_PATH)) {
49+
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8"));
5050
if (config.proxy && config.proxy.url) {
51-
return new URL(config.proxy.url);
51+
const url = new URL(config.proxy.url);
52+
return url;
5253
}
5354
}
54-
} catch (_) {}
55+
} catch (e) {
56+
warn(`Failed to read proxy config from ${CONFIG_PATH}: ${e.message}`);
57+
}
5558

5659
return null;
5760
}
@@ -112,7 +115,10 @@ class HttpsProxyAgent extends https.Agent {
112115
const tlsSocket = tls.connect(tlsOpts, () =>
113116
doneOnce(null, tlsSocket)
114117
);
115-
tlsSocket.on("error", (err) => doneOnce(err));
118+
tlsSocket.on("error", (err) => {
119+
socket.destroy();
120+
doneOnce(err);
121+
});
116122
});
117123

118124
req.on("error", (err) => doneOnce(err));
@@ -183,6 +189,25 @@ function buildUrl(pattern, vars) {
183189
.replace(/\{arch\}/g, vars.arch);
184190
}
185191

192+
let _cachedAgent = null;
193+
let _cachedProxyKey = null;
194+
195+
function getProxyAgent() {
196+
const proxyUrl = getProxyUrl();
197+
if (!proxyUrl) {
198+
_cachedAgent = null;
199+
_cachedProxyKey = null;
200+
return null;
201+
}
202+
const key = proxyUrl.href;
203+
if (_cachedAgent && _cachedProxyKey === key) {
204+
return _cachedAgent;
205+
}
206+
_cachedAgent = new HttpsProxyAgent(proxyUrl);
207+
_cachedProxyKey = key;
208+
return _cachedAgent;
209+
}
210+
186211
function download(url, maxRedirects = 10) {
187212
if (!url.startsWith("https")) {
188213
return Promise.reject(new Error(`Refusing non-HTTPS download: ${url}`));
@@ -191,10 +216,8 @@ function download(url, maxRedirects = 10) {
191216
return Promise.reject(new Error(`Too many redirects fetching ${url}`));
192217
}
193218

194-
const proxyUrl = getProxyUrl();
195-
const options = proxyUrl
196-
? { agent: new HttpsProxyAgent(proxyUrl) }
197-
: {};
219+
const agent = getProxyAgent();
220+
const options = agent ? { agent } : {};
198221

199222
return new Promise((resolve, reject) => {
200223
https.get(url, options, (res) => {

scripts/setup-proxy.js

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22
"use strict";
33

44
const fs = require("fs");
5-
const path = require("path");
6-
const os = require("os");
7-
8-
const configDir = path.join(os.homedir(), ".opencodereview");
9-
const configPath = path.join(configDir, "config.json");
5+
const { CONFIG_DIR, CONFIG_PATH } = require("./config-paths");
106

117
function info(msg) {
128
console.log(`[INFO] ${msg}`);
@@ -17,20 +13,21 @@ function error(msg) {
1713
}
1814

1915
function loadConfig() {
20-
fs.mkdirSync(configDir, { recursive: true });
21-
if (fs.existsSync(configPath)) {
16+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
17+
if (fs.existsSync(CONFIG_PATH)) {
2218
try {
23-
return JSON.parse(fs.readFileSync(configPath, "utf8"));
19+
return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8"));
2420
} catch (e) {
2521
error(`Failed to parse config.json: ${e.message}`);
26-
return {};
22+
error("Please fix or remove the corrupted file before retrying.");
23+
process.exit(1);
2724
}
2825
}
2926
return {};
3027
}
3128

3229
function saveConfig(config) {
33-
fs.writeFileSync(configPath, JSON.stringify(config, null, 4) + "\n");
30+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 4) + "\n");
3431
}
3532

3633
function show() {
@@ -39,7 +36,7 @@ function show() {
3936
info(`Current proxy: ${config.proxy.url}`);
4037
} else {
4138
info("No proxy configured.");
42-
info("Set one with: node scripts/setup-proxy.js http://127.0.0.1:7897");
39+
info("Set one with: node scripts/setup-proxy.js set http://127.0.0.1:7897");
4340
}
4441
}
4542

0 commit comments

Comments
 (0)