mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-05 07:10:55 +00:00
Merge pull request #2745 from QwenLM/fix/proxy-url-compability
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
fix: normalize proxy URLs to support addresses without protocol prefix
This commit is contained in:
commit
4b05c74b02
4 changed files with 164 additions and 3 deletions
|
|
@ -108,6 +108,7 @@ import { shouldDefaultToNodePty } from '../utils/shell-utils.js';
|
|||
import { WorkspaceContext } from '../utils/workspaceContext.js';
|
||||
import { type ToolName } from '../utils/tool-utils.js';
|
||||
import { getErrorMessage } from '../utils/errors.js';
|
||||
import { normalizeProxyUrl } from '../utils/proxyUtils.js';
|
||||
|
||||
// Local config modules
|
||||
import type { FileFilteringOptions } from './constants.js';
|
||||
|
|
@ -747,8 +748,9 @@ export class Config {
|
|||
initializeTelemetry(this);
|
||||
}
|
||||
|
||||
if (this.getProxy()) {
|
||||
setGlobalDispatcher(new ProxyAgent(this.getProxy() as string));
|
||||
const proxyUrl = this.getProxy();
|
||||
if (proxyUrl) {
|
||||
setGlobalDispatcher(new ProxyAgent(proxyUrl));
|
||||
}
|
||||
this.geminiClient = new GeminiClient(this);
|
||||
this.chatRecordingService = this.chatRecordingEnabled
|
||||
|
|
@ -1717,7 +1719,7 @@ export class Config {
|
|||
}
|
||||
|
||||
getProxy(): string | undefined {
|
||||
return this.proxy;
|
||||
return normalizeProxyUrl(this.proxy);
|
||||
}
|
||||
|
||||
getWorkingDir(): string {
|
||||
|
|
|
|||
|
|
@ -217,6 +217,7 @@ export * from './utils/pathReader.js';
|
|||
export * from './utils/paths.js';
|
||||
export * from './utils/projectSummary.js';
|
||||
export * from './utils/promptIdContext.js';
|
||||
export * from './utils/proxyUtils.js';
|
||||
export * from './utils/quotaErrorDetection.js';
|
||||
export * from './utils/readManyFiles.js';
|
||||
export * from './utils/request-tokenizer/supportedImageFormats.js';
|
||||
|
|
|
|||
105
packages/core/src/utils/proxyUtils.test.ts
Normal file
105
packages/core/src/utils/proxyUtils.test.ts
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { normalizeProxyUrl } from './proxyUtils.js';
|
||||
|
||||
describe('normalizeProxyUrl', () => {
|
||||
it('should return undefined for undefined input', () => {
|
||||
expect(normalizeProxyUrl(undefined)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return undefined for empty string', () => {
|
||||
expect(normalizeProxyUrl('')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return undefined for whitespace-only string', () => {
|
||||
expect(normalizeProxyUrl(' ')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should add http:// prefix to proxy URL without protocol', () => {
|
||||
expect(normalizeProxyUrl('127.0.0.1:7860')).toBe('http://127.0.0.1:7860');
|
||||
});
|
||||
|
||||
it('should add http:// prefix to proxy URL with port only', () => {
|
||||
expect(normalizeProxyUrl('localhost:8080')).toBe('http://localhost:8080');
|
||||
});
|
||||
|
||||
it('should not modify URL that already has http:// prefix', () => {
|
||||
expect(normalizeProxyUrl('http://127.0.0.1:7860')).toBe(
|
||||
'http://127.0.0.1:7860',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not modify URL that already has https:// prefix', () => {
|
||||
expect(normalizeProxyUrl('https://proxy.example.com:443')).toBe(
|
||||
'https://proxy.example.com:443',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle HTTP:// prefix (case insensitive)', () => {
|
||||
expect(normalizeProxyUrl('HTTP://127.0.0.1:7860')).toBe(
|
||||
'HTTP://127.0.0.1:7860',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle HTTPS:// prefix (case insensitive)', () => {
|
||||
expect(normalizeProxyUrl('HTTPS://proxy.example.com:443')).toBe(
|
||||
'HTTPS://proxy.example.com:443',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle proxy URL with authentication', () => {
|
||||
expect(normalizeProxyUrl('user:pass@proxy.example.com:8080')).toBe(
|
||||
'http://user:pass@proxy.example.com:8080',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle proxy URL with authentication and http:// prefix', () => {
|
||||
expect(normalizeProxyUrl('http://user:pass@proxy.example.com:8080')).toBe(
|
||||
'http://user:pass@proxy.example.com:8080',
|
||||
);
|
||||
});
|
||||
|
||||
it('should trim whitespace from proxy URL', () => {
|
||||
expect(normalizeProxyUrl(' 127.0.0.1:7860 ')).toBe(
|
||||
'http://127.0.0.1:7860',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle IPv6 addresses', () => {
|
||||
expect(normalizeProxyUrl('[::1]:8080')).toBe('http://[::1]:8080');
|
||||
});
|
||||
|
||||
it('should handle IPv6 addresses with http:// prefix', () => {
|
||||
expect(normalizeProxyUrl('http://[::1]:8080')).toBe('http://[::1]:8080');
|
||||
});
|
||||
|
||||
// SOCKS proxy tests - should throw error since undici doesn't support SOCKS
|
||||
it('should throw error for socks:// proxy URL', () => {
|
||||
expect(() => normalizeProxyUrl('socks://proxy.example.com:1080')).toThrow(
|
||||
'SOCKS proxy is not supported',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error for socks4:// proxy URL', () => {
|
||||
expect(() => normalizeProxyUrl('socks4://proxy.example.com:1080')).toThrow(
|
||||
'SOCKS proxy is not supported',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error for socks5:// proxy URL', () => {
|
||||
expect(() => normalizeProxyUrl('socks5://proxy.example.com:1080')).toThrow(
|
||||
'SOCKS proxy is not supported',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error for SOCKS5:// proxy URL (case insensitive)', () => {
|
||||
expect(() => normalizeProxyUrl('SOCKS5://proxy.example.com:1080')).toThrow(
|
||||
'SOCKS proxy is not supported',
|
||||
);
|
||||
});
|
||||
});
|
||||
53
packages/core/src/utils/proxyUtils.ts
Normal file
53
packages/core/src/utils/proxyUtils.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Normalizes a proxy URL to ensure it has a valid protocol prefix.
|
||||
*
|
||||
* Many proxy tools and environment variables provide proxy addresses without
|
||||
* a protocol prefix (e.g., "127.0.0.1:7860" instead of "http://127.0.0.1:7860").
|
||||
* This function adds the "http://" prefix if missing, since HTTP proxies are
|
||||
* the most common default.
|
||||
*
|
||||
* Note: Only HTTP and HTTPS proxies are supported. SOCKS proxies (socks://,
|
||||
* socks4://, socks5://) are NOT supported because the underlying undici library
|
||||
* does not support them. See: https://github.com/nodejs/undici/issues/2224
|
||||
*
|
||||
* @param proxyUrl - The proxy URL to normalize
|
||||
* @returns The normalized proxy URL with protocol prefix, or undefined if input is undefined/empty
|
||||
* @throws Error if a SOCKS proxy URL is provided
|
||||
*/
|
||||
export function normalizeProxyUrl(
|
||||
proxyUrl: string | undefined,
|
||||
): string | undefined {
|
||||
if (!proxyUrl) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const trimmed = proxyUrl.trim();
|
||||
if (!trimmed) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Check if the URL already has a protocol prefix
|
||||
// Only support http and https protocols (undici limitation)
|
||||
if (/^https?:\/\//i.test(trimmed)) {
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
// Reject SOCKS proxies - undici does not support them
|
||||
if (/^socks[45]?:\/\//i.test(trimmed)) {
|
||||
throw new Error(
|
||||
`SOCKS proxy is not supported. The underlying HTTP client (undici) only supports HTTP and HTTPS proxies. ` +
|
||||
`Please use an HTTP/HTTPS proxy instead, or set up a SOCKS-to-HTTP proxy converter. ` +
|
||||
`See: https://github.com/nodejs/undici/issues/2224`,
|
||||
);
|
||||
}
|
||||
|
||||
// Add http:// prefix for proxy URLs without protocol
|
||||
// HTTP is the default for most proxy configurations
|
||||
return `http://${trimmed}`;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue