Skip to content

新增独立的代理设置 / Add an independent proxy setting - #6483

Closed
HardBrick21 wants to merge 2 commits into
YiiGuxing:masterfrom
HardBrick21:feature/proxy-settings
Closed

HardBrick21 wants to merge 2 commits into
YiiGuxing:masterfrom
HardBrick21:feature/proxy-settings

Conversation

@HardBrick21

@HardBrick21 HardBrick21 commented Sep 30, 2026 •

Copy link
Copy Markdown

这个改动做了什么

为翻译请求新增一个可选的插件级代理,让翻译走自己的代理,而不必依赖 IDE 的全局 HTTP 代理。

在部分网络环境下,谷歌翻译、OpenAI、DeepL 无法直连。此前插件完全没有代理配置——所有请求
都经由 com.intellij.util.io.HttpRequests,而它只认 IDE 代理
(Settings → Appearance & Behavior → System Settings → HTTP Proxy)。用户无法让翻译引擎
单独使用某个代理。

PixPin_2026-09-30_16-05-08

行为

  • 默认关闭。 行为不变:请求仍走 HttpRequests,即沿用 IDE 代理。
  • 启用后,插件全部 HTTP 请求与 Edge TTS 的 WebSocket 都走所配置的代理。支持 HTTP 与
    SOCKS5,不支持认证。
  • 配置位置:Settings → Tools → Translation → Proxy。
  • 不触碰任何进程级状态。 HttpConfigurable、JVM 系统属性、全局 ProxySelector 均不作修改,
    因此不影响 IDE 自身与其他插件的请求。

为什么必须另建连接

RequestBuilder 只暴露了 useProxy(boolean),没有任何接收代理地址的入口。反汇编
HttpRequests$RequestImpl 可确认其唯一分支是:myUseProxy 为真 →
IdeUiService.openHttpConnection(url)(即 IDE 代理),否则
url.openConnection(Proxy.NO_PROXY)。

而 com.intellij.util.io.RequestBuilder 是 public abstract、只有一个抽象方法
connect(RequestProcessor<T>),HttpRequests.Request 是 public interface。因此走代理的
路径只需继承平台 builder:

  • ProxiedRequestBuilder : RequestBuilder() 记录各项配置,只重写 connect,在其中执行
    URL.openConnection(proxy)。
  • ProxiedRequest : HttpRequests.Request 委托给该连接。

这样所有调用点都无需改动,包括 MicrosoftHttp 用到的平台扩展 setHeadersViaTuner()——
若改为替换 builder 类型,这个内部 API 就需要重新实现。

与既有路径的行为对齐

已通过反射读取 HttpRequests$RequestBuilderImpl 的私有默认值并逐一对齐,否则同一个请求会
因为"是否配置了代理"而表现出不同行为:

字段 平台默认值 本实现
myRedirectLimit 10 10
myConnectTimeout 10000 10000
myTimeout(读) 60000 60000
myGzip true true
myThrowStatusCodeException true true

其他改动

  • 原先直接调用 HttpRequests 的 5 处(BingTranslator、MicrosoftHttp、OpenAiHttp、
    GoogleTTSPlayer、WordBookService)改为走统一工厂。
  • Http.send 的重定向重建原先直接构造平台 builder,会导致重定向后的请求绕过代理;现同样
    走工厂。
  • Edge TTS 使用 Java-WebSocket,是与 HTTP 并列的另一条通道,单独调用 setProxy()。
  • 配置了代理时,连接类失败(connect / unknown host / socket / SSL / timeout)会在错误信息后
    追加"请检查代理设置"。状态码错误不追加——那不是代理的问题。

测试

65 个测试通过(63 个既有 + 新增)。新增 3 个测试类:

  • ProxySettingsTest — 合法性边界、Proxy 构造、XML 序列化往返。
  • HttpProxyTest — 用本地假代理证明 GET 与 POST 确实经由代理发出(目标主机为
    example.invalid,永不解析,直连不可能成功),并验证默认请求 gzip 的行为与平台一致。
  • ExceptionsTest — 代理提示只追加到连接类失败。

代理传输本身也用与插件完全相同的 JDK 调用路径(URL.openConnection(proxy))做了端到端验证:

路径 结果
HTTP 代理 → google.com (https) 204
HTTP 代理 → api.openai.com (https) 401(可达,未带 key)
HTTP 代理 → baidu.com (http) 200
SOCKS5 → google.com (https) 204
SOCKS5 → api.openai.com (https) 401
不使用代理 → google.com (https) 连接超时

JDK 17 上 SOCKS5 走 HTTPS 可用,因此保留 SOCKS5,未降级为仅支持 HTTP。

自动化测试未覆盖的部分

建议用 runIde 手工验证:

  • Edge TTS 走代理播放——WebSocket 隧道未验证。
  • 设置面板的渲染与勾选联动。
  • 单词本首次启动时下载 SQLite 驱动(现在也会走代理)。
  • saveToFile 的进度与取消语义。

备注

  • 代理启用后,国内引擎(百度、有道、阿里、Bing)也会走代理。这是有意为之,不设例外列表。
  • CHANGELOG 已添加 [Unreleased] 条目。

houlixuan and others added 2 commits September 30, 2026 15:34
Google Translate, OpenAI and other engines cannot be reached directly in some
networks. The plugin had no proxy configuration of its own and relied entirely
on the IDE-wide HTTP proxy.

Adds an opt-in global proxy (HTTP or SOCKS5) to the Translation settings. When
disabled - the default - requests keep going through HttpRequests and therefore
the IDE proxy, so existing behavior is unchanged. When enabled, the plugin opens
connections itself through the configured proxy.

com.intellij.util.io.RequestBuilder turns out to be subclassable with a single
abstract `connect`, and HttpRequests.Request is a public interface, so the
proxied path extends the platform builder rather than replacing the type. Call
sites and the platform's setHeadersViaTuner() are left untouched.

Defaults mirror HttpRequests$RequestBuilderImpl (10s connect, 60s read, gzip on,
redirect limit 10) so that the proxied and platform paths behave the same.

The Edge TTS WebSocket, which does not go through Http, honors the proxy too.
Connection-level failures now hint at the proxy settings when one is configured.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The host field feeds straight into InetSocketAddress, so a value like
"http://127.0.0.1" is resolved as a hostname, yields an unresolved address and
fails later with an error that gives no hint about the proxy configuration. The
proxy protocol is picked by the type combo box, not by a URL scheme.

Documents this in three places: a KDoc note on ProxySettings.host and
toProxyOrNull(), a placeholder on the host field showing "127.0.0.1", and a
comment under the field in the settings panel.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@YiiGuxing

Copy link
Copy Markdown
Owner

非常感谢你提交这个 PR,也谢谢你花时间实现插件的 HTTP 独立代理配置。独立代理支持确实是项目后续需要的能力,我很感激你主动推进这件事。

不过,我觉得我需要和你同步一下项目路线:下一个大版本计划引入新的 HTTP 请求库,并在新的 HTTP 层里统一支持代理配置。这个 PR 目前重新实现了 IntelliJ 平台的 HTTP 请求逻辑,和后续架构会有较大重叠;如果现在合并,等新版本迁移时可能会产生大量冲突,也会增加后续维护成本。因此我暂时先不合并并关闭这个 PR,但这并不是否定这个需求或你的工作。再次感谢你的贡献和理解。

@YiiGuxing YiiGuxing closed this Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants