Skip to content

Commit 7bd2afa

Browse files
committed
feat: Add HTTP proxy support for corporate/enterprise users
Add HTTPS_PROXY/HTTP_PROXY environment variable support to the CLI bootstrap scripts so users behind corporate proxies can download the codebuff binary. Code changes (applied to release, staging, and freebuff variants): - Add HTTP CONNECT tunnel proxy support (zero new dependencies) - Support both http:// and https:// proxy URLs - Support proxy authentication via URL credentials - Respect NO_PROXY/no_proxy with port-suffix stripping - Drain redirect responses before following to prevent socket leaks - Use agent:false + createConnection for clean per-request tunneling - Show proxy configuration hint on timeout/connection errors Documentation: - Add Corporate Proxy / Firewall section to CLI README - Update WINDOWS.md troubleshooting with HTTPS_PROXY as primary fix Fixes: Users behind corporate proxies getting "Request timeout" errors
1 parent e3ea66f commit 7bd2afa

File tree

5 files changed

+389
-38
lines changed

5 files changed

+389
-38
lines changed

WINDOWS.md

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,40 @@ Codebuff checks GitHub for the latest release on first run. This fails when:
5454

5555
**Solutions**:
5656

57-
1. **Verify GitHub access**:
57+
1. **Set the `HTTPS_PROXY` environment variable** (if behind corporate proxy):
58+
59+
Codebuff natively supports proxy environment variables. This is the recommended fix:
60+
61+
**PowerShell:**
62+
```powershell
63+
$env:HTTPS_PROXY = "http://your-proxy-server:port"
64+
codebuff
65+
```
66+
67+
**CMD:**
68+
```cmd
69+
set HTTPS_PROXY=http://your-proxy-server:port
70+
codebuff
71+
```
72+
73+
To make it permanent, add `HTTPS_PROXY` to your Windows System Environment Variables (Settings → System → Advanced → Environment Variables).
74+
75+
2. **Verify network access**:
5876
```powershell
59-
curl https://github.com/CodebuffAI/codebuff/releases.atom
77+
curl https://registry.npmjs.org/codebuff/latest
6078
```
6179
If this fails, you have a network/firewall issue.
6280

63-
2. **Configure npm proxy** (if behind corporate proxy):
81+
3. **Configure npm proxy** (for the `npm install` step only):
6482
```powershell
6583
npm config set proxy http://your-proxy-server:port
6684
npm config set https-proxy http://your-proxy-server:port
6785
```
86+
Note: This only helps with `npm install`. Codebuff's own downloads use `HTTPS_PROXY` instead.
6887

69-
3. **Disable VPN temporarily** or whitelist GitHub in your firewall
88+
4. **Disable VPN temporarily** or whitelist `registry.npmjs.org` and `codebuff.com` in your firewall
7089

71-
4. **Clear npm cache and reinstall**:
90+
5. **Clear npm cache and reinstall**:
7291
```powershell
7392
npm cache clean --force
7493
npm uninstall -g codebuff

cli/release-staging/index.js

Lines changed: 109 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const http = require('http')
66
const https = require('https')
77
const os = require('os')
88
const path = require('path')
9+
const tls = require('tls')
910
const zlib = require('zlib')
1011

1112
const tar = require('tar')
@@ -96,6 +97,76 @@ function trackUpdateFailed(errorMessage, version, context = {}) {
9697
}
9798
}
9899

100+
function getProxyUrl() {
101+
return (
102+
process.env.HTTPS_PROXY ||
103+
process.env.https_proxy ||
104+
process.env.HTTP_PROXY ||
105+
process.env.http_proxy ||
106+
null
107+
)
108+
}
109+
110+
function shouldBypassProxy(hostname) {
111+
const noProxy = process.env.NO_PROXY || process.env.no_proxy || ''
112+
if (!noProxy) return false
113+
const domains = noProxy.split(',').map((d) => d.trim().toLowerCase().replace(/:\d+$/, ''))
114+
const host = hostname.toLowerCase()
115+
return domains.some((d) => {
116+
if (d === '*') return true
117+
if (d.startsWith('.')) return host.endsWith(d) || host === d.slice(1)
118+
return host === d || host.endsWith('.' + d)
119+
})
120+
}
121+
122+
function connectThroughProxy(proxyUrl, targetHost, targetPort) {
123+
return new Promise((resolve, reject) => {
124+
const proxy = new URL(proxyUrl)
125+
const isHttpsProxy = proxy.protocol === 'https:'
126+
const connectOptions = {
127+
hostname: proxy.hostname,
128+
port: proxy.port || (isHttpsProxy ? 443 : 80),
129+
method: 'CONNECT',
130+
path: `${targetHost}:${targetPort}`,
131+
headers: {
132+
Host: `${targetHost}:${targetPort}`,
133+
},
134+
}
135+
136+
if (proxy.username || proxy.password) {
137+
const auth = Buffer.from(
138+
`${decodeURIComponent(proxy.username || '')}:${decodeURIComponent(proxy.password || '')}`,
139+
).toString('base64')
140+
connectOptions.headers['Proxy-Authorization'] = `Basic ${auth}`
141+
}
142+
143+
const transport = isHttpsProxy ? https : http
144+
const req = transport.request(connectOptions)
145+
146+
req.on('connect', (res, socket) => {
147+
if (res.statusCode === 200) {
148+
resolve(socket)
149+
} else {
150+
socket.destroy()
151+
reject(
152+
new Error(`Proxy CONNECT failed with status ${res.statusCode}`),
153+
)
154+
}
155+
})
156+
157+
req.on('error', (err) => {
158+
reject(new Error(`Proxy connection failed: ${err.message}`))
159+
})
160+
161+
req.setTimeout(CONFIG.requestTimeout, () => {
162+
req.destroy()
163+
reject(new Error('Proxy connection timeout.'))
164+
})
165+
166+
req.end()
167+
})
168+
}
169+
99170
const PLATFORM_TARGETS = {
100171
'linux-x64': `${packageName}-linux-x64.tar.gz`,
101172
'linux-arm64': `${packageName}-linux-arm64.tar.gz`,
@@ -120,20 +191,37 @@ const term = {
120191
},
121192
}
122193

123-
function httpGet(url, options = {}) {
124-
return new Promise((resolve, reject) => {
125-
const parsedUrl = new URL(url)
126-
const reqOptions = {
127-
hostname: parsedUrl.hostname,
128-
path: parsedUrl.pathname + parsedUrl.search,
129-
headers: {
130-
'User-Agent': CONFIG.userAgent,
131-
...options.headers,
132-
},
133-
}
194+
async function httpGet(url, options = {}) {
195+
const parsedUrl = new URL(url)
196+
const proxyUrl = getProxyUrl()
197+
198+
const reqOptions = {
199+
hostname: parsedUrl.hostname,
200+
path: parsedUrl.pathname + parsedUrl.search,
201+
headers: {
202+
'User-Agent': CONFIG.userAgent,
203+
...options.headers,
204+
},
205+
}
206+
207+
if (proxyUrl && !shouldBypassProxy(parsedUrl.hostname)) {
208+
const tunnelSocket = await connectThroughProxy(
209+
proxyUrl,
210+
parsedUrl.hostname,
211+
parsedUrl.port || 443,
212+
)
213+
reqOptions.agent = false
214+
reqOptions.createConnection = () =>
215+
tls.connect({
216+
socket: tunnelSocket,
217+
servername: parsedUrl.hostname,
218+
})
219+
}
134220

221+
return new Promise((resolve, reject) => {
135222
const req = https.get(reqOptions, (res) => {
136223
if (res.statusCode === 302 || res.statusCode === 301) {
224+
res.resume()
137225
return httpGet(new URL(res.headers.location, url).href, options)
138226
.then(resolve)
139227
.catch(reject)
@@ -401,6 +489,11 @@ async function ensureBinaryExists() {
401489
if (!version) {
402490
console.error('❌ Failed to determine latest version')
403491
console.error('Please check your internet connection and try again')
492+
if (!getProxyUrl()) {
493+
console.error(
494+
'If you are behind a proxy, set the HTTPS_PROXY environment variable',
495+
)
496+
}
404497
process.exit(1)
405498
}
406499

@@ -410,6 +503,11 @@ async function ensureBinaryExists() {
410503
term.clearLine()
411504
console.error('❌ Failed to download codecane:', error.message)
412505
console.error('Please check your internet connection and try again')
506+
if (!getProxyUrl()) {
507+
console.error(
508+
'If you are behind a proxy, set the HTTPS_PROXY environment variable',
509+
)
510+
}
413511
process.exit(1)
414512
}
415513
}

cli/release/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ Some have said every change should be paired with a unit test. In 2024, every ch
5656

5757
## Troubleshooting
5858

59+
### Permission Errors
60+
5961
If you are getting permission errors during installation, try using sudo:
6062

6163
```
@@ -64,6 +66,42 @@ sudo npm install -g codebuff
6466

6567
If you still have errors, it's a good idea to [reinstall Node](https://nodejs.org/en/download).
6668

69+
### Corporate Proxy / Firewall
70+
71+
If you see `Failed to download codebuff: Request timeout` or `Failed to determine latest version`, you may be behind a corporate proxy or firewall.
72+
73+
Codebuff respects standard proxy environment variables. Set `HTTPS_PROXY` to route traffic through your proxy:
74+
75+
**Linux / macOS (bash/zsh):**
76+
```bash
77+
export HTTPS_PROXY=http://your-proxy-server:port
78+
codebuff
79+
```
80+
81+
**Windows (PowerShell):**
82+
```powershell
83+
$env:HTTPS_PROXY = "http://your-proxy-server:port"
84+
codebuff
85+
```
86+
87+
**Windows (CMD):**
88+
```cmd
89+
set HTTPS_PROXY=http://your-proxy-server:port
90+
codebuff
91+
```
92+
93+
To make it permanent, add the `export` or `set` line to your shell profile (e.g. `~/.bashrc`, `~/.zshrc`, or Windows System Environment Variables).
94+
95+
**Supported environment variables:**
96+
97+
| Variable | Purpose |
98+
|---|---|
99+
| `HTTPS_PROXY` / `https_proxy` | Proxy for HTTPS requests (recommended) |
100+
| `HTTP_PROXY` / `http_proxy` | Fallback proxy for HTTP requests |
101+
| `NO_PROXY` / `no_proxy` | Comma-separated list of hostnames to bypass the proxy (port suffixes are ignored) |
102+
103+
Both `http://` and `https://` proxy URLs are supported. Proxy authentication is supported via URL credentials (e.g. `http://user:password@proxy:port`).
104+
67105
## Feedback
68106

69107
We value your input! Please email your feedback to `founders@codebuff.com`. Thank you for using Codebuff!

0 commit comments

Comments
 (0)