Skip to content

Move Browser Dialer to sockopt.dialerProxy browser:// URL usage, with UUID path validation, startup-time conflict checks, instance-lifecycle listen/serve control, XHTTP mode constraints, and static initialization simplification#6016

Draft
Copilot wants to merge 44 commits into
mainfrom
copilot/move-browser-dialer-to-sockopt

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 26, 2026

This PR moves Browser Dialer usage to sockopt.dialerProxy with browser://host:port/uuid URLs and removes the earlier parse-time listener startup side effects.

What changed

  • dialerProxy now uses browser://host:port/uuid
  • Browser Dialer URLs are collected and validated during config build
  • URL validation includes host:port parsing, UUID path validation, and same-port/different-IP conflict checks
  • Browser Dialer remains restricted to WebSocket and XHTTP, with XHTTP limited to auto / packet-up, and REALITY rejected
  • Listener startup is now controlled by the Xray instance lifecycle:
    • start in core.Instance.Start()
    • stop in core.Instance.Close()
  • Startup failure cleanup now also tears down Browser Dialer listeners

Scope

The change keeps the simplified non-dynamic model, but ties listener lifetime to the actual Xray instance instead of starting listeners while only parsing/building config.

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 26, 2026

原始提示

根据 #5978 (comment) 把 Browser Dialer 移到 sockopt

GitHub Copilot 似乎把 Opus 等 3x 的模型下架了,这个是 GPT 5.3-Codex

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 26, 2026

@copilot 根据 d14767d 重新生成 proto,另外不用保留环境变量和 reload

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 26, 2026

@copilot 根据 d14767d 重新生成 proto,另外不用保留环境变量和 reload

已按要求处理:去掉了环境变量回退与 Reload 路径,Browser Dialer 仅通过 sockopt.browserDialer 使用;并按你给的提交方式重新生成了 proto(最终只保留了本次相关的 transport/internet/config.pb.go 变更)。对应提交:27b8b29

Copilot finished work on behalf of RPRX April 26, 2026 07:45
Copilot AI requested a review from RPRX April 26, 2026 07:45
@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 26, 2026

@copilot 环境变量提示迁移,使用 errors.PrintRemoved 那啥

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 26, 2026

@copilot 环境变量提示迁移,使用 errors.PrintRemoved 那啥

已加上环境变量迁移/移除提示:检测到 xray.browser.dialer.address 时会通过 errors.PrintRemovedFeatureError(...) 输出迁移到 sockopt.browserDialer 的提示。对应提交:84d04d0

Copilot finished work on behalf of RPRX April 26, 2026 07:56
@RPRX

This comment was marked as outdated.

@fish4terrisa-MSDSM

This comment was marked as outdated.

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 26, 2026

我想了一下这里的设计,当监听同一个端口时,按 path/query 来区分不同的浏览器的话其实也行

@copilot 你改一下改成 path 区分吧,未提供 csrfToken 时就返回 page,同时也防止了其它软件探测出 Browser Dialer 的存在

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 26, 2026

我想了一下这里的设计,当监听同一个端口时,按 path/query 来区分不同的浏览器的话其实也行

@copilot 你改一下改成 path 区分吧,未提供 csrfToken 时就��...

已改成 path 区分:WebSocket CSRF 校验从 query token 改为 path(/websocket/<csrfToken>);未提供或不匹配 csrfToken 的请求统一返回 page,同时保留日志错误细节。对应提交:2691a1a

Copilot finished work on behalf of RPRX April 26, 2026 15:02
@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 26, 2026

@copilot 我的意思是比如:

  1. broswerDialer 选项除了 IP、端口外,还必须带 path,比如 "127.0.0.1:8080/example",这样多个出站可以用同一个端口
  2. 外部程序访问错误 path 时,Xray 直接关闭连接,防止了其它软件探测出 Browser Dialer 的存在
  3. 外部程序访问正确 path 时,Xray 返回 webpage HTML,其中的 path 和 csrfToken 被动态替换掉
  4. 外部程序访问正确 path 加 csrfToken 时,比如 "ws://127.0.0.1:8080/example/csrfToken",Xray 与外部程序建立 WS 连接

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 26, 2026

@copilot 我的意思是比如:

  1. broswerDialer 选项除了 IP、端口外,还必须带 path,比如 "127.0.0.1:8080/example",这样多个出站可以用同一个端口
    ...

已按这 4 点实现:sockopt.browserDialer 现在要求 host:port/path;同端口按 path 分流;错误 path 直接断开连接;正确 path 返回动态替换了 path+csrfToken 的 HTML,ws://host:port/<path>/<csrfToken> 可建立 WS。对应提交:c48c475。UI 截图不适用(本次为内部嵌入页路由行为改动)。

Copilot AI changed the title Move Browser Dialer selection into sockopt (per-outbound) and wire transport usage Move Browser Dialer selection into sockopt (per-outbound) and wire path-scoped transport usage Apr 26, 2026
Copilot finished work on behalf of RPRX April 27, 2026 13:08
@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 27, 2026

等下我发现那个不是 Override() 里的所以它没改

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 27, 2026

@Fangliding 我没理解你说的那个是啥问题,这个 xray.go Build() 不应该只会被执行一次吗?没有多 instance

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 27, 2026

你指的是这里 hs, err := splitHTTPSettingsCopy.Build() 还是什么

@Fangliding
Copy link
Copy Markdown
Member

不是 解析config的时候就部署这些服务很dirty的 你强行让它解析config的时候拉起来结果就是用它当lib的时候关了没法回收 api加的出站不会过这个 那个streamsettings是没状态的 这个矛盾没法调和 要么注册成一个 出入站一样的feature 然后走我之前说的出站用一个小选项选择
或者让AI都能分析出
@copilot 不改动 从 infra/conf/xray.go 的改动开始检查 解析config即拉起服务会对模块的封装造成什么影响

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 27, 2026

不是 解析config的时候就部署这些服务很dirty的 你强行让它解析config的时候拉起来结果就是用它当lib的时候关了没法回收 api加的出站不会过这个 那个strea...

是,hs, err := splitHTTPSettingsCopy.Build() 这段本身不是把服务拉起来,它只是为了临时拿到 XHTTP 的 mode 做校验。真正有副作用的是从 infra/conf/xray.go 开始的这几步:BeginCollectingDialerProxyURLs() / ConfigureCollectedDialerProxyURLs() / StartCollectedDialerProxyURLs(),最后在 manager.gonet.Listen + go server.Serve(...)

问题点也主要在这里:Config.Build() 从“构建配置”变成了“顺便启动运行时服务”。这样会把监听器生命周期绑在全局包状态上,而不是绑在 core/instance 上;如果拿 Xray 当 lib、后续启动失败、或关闭实例,这批 listener 没有对应回收路径。API 动态加的 outbound 也不会走这条 build-time 路径,所以行为也不一致。

所以核心矛盾不是 Build() 会不会被多次执行,而是“在配置构建阶段启动服务”本身会破坏模块边界和生命周期归属。

Copilot AI requested a review from Fangliding April 27, 2026 13:27
@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 27, 2026

没拿 Xray 当过 lib,我以为是整个销毁的,动态加出站就不考虑了,本来只是为了代码简单些、不加锁

那就加 Xray instance 控制那里 start 并实现 stop @copilot

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 27, 2026

没拿 Xray 当过 lib,我以为是整个销毁的,动态加出站就不考虑了,本来只是为了代码简单些、不加锁

那就加 Xray instance 控制那里 start 并实现 stop @...

按这个改了:配置构建阶段现在只做 browser dialer URL 的收集/校验,listener start 挪到 core.Instance.Start()core.Instance.Close() 里补了 stop,启动过程中如果后续 feature start 失败也会回收 browser dialer。对应提交:e088cc4

已验证:go test ./core ./transport/internet/browser_dialer && go test ./infra/conf -run 'TestXrayConfig|TestConfig_Override'

截图:

Copilot AI changed the title Move Browser Dialer to sockopt.dialerProxy browser:// URL usage, with UUID path validation, startup-time conflict checks, explicit post-collection listen/serve startup, XHTTP mode constraints, and static initialization simplification Move Browser Dialer to sockopt.dialerProxy browser:// URL usage, with UUID path validation, startup-time conflict checks, instance-lifecycle listen/serve control, XHTTP mode constraints, and static initialization simplification Apr 27, 2026
Copilot finished work on behalf of RPRX April 27, 2026 13:40
@Fangliding
Copy link
Copy Markdown
Member

为了 "简单些、不加锁" 那最好的办法还是之前那样单例 开几个例除了能控制某个出站稳定使用某个不同的浏览器这个很边缘的用法就没啥用了 绕过连接数限制这点之前已经说了不存在

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 27, 2026

不同出站使用不同的浏览器是有必要的,比如上下行分离可以上下行用不同的浏览器,甚至两台设备的浏览器,这样更彻底

并且 listen 的需求可能也不同,比如有的想 listen 10.x 啥的,就需要两个端口,总之现在这样能 cover 最多需求

虽然有个 path 作为 token 验证的话,只监听 0.0.0.0 或 ::0 其实也不是不行,但是能被其它网段探测出有个 web sever

@Fangliding
Copy link
Copy Markdown
Member

前段时间chrome的大手已经默认禁止网页再fetch到本地IP了 还导致了部分人的fake dns炸掉

@Fangliding
Copy link
Copy Markdown
Member

@copilot 你自己给自己打分 这样是否dirty 有没有打破封装 和其他模块(比如API添加出站)的协作性是什么情况

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 27, 2026

总之现在功能是完善了,如果按之前的环境变量开单例大概是 sockopt 只指定路径,重新弄 instance 的话需要 reload

但是这个 PR 还有另一点是推动 Browser Dialer 普及,多填个 dialerProxy 就行了,之前放环境变量是准备加 env 但不太好弄

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 27, 2026

和其他模块(比如API添加出站)的协作性是什么情况

你这属于恶意提问了,本来就说了暂不考虑 API 加出站的情况

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 27, 2026

@copilot 你自己给自己打分 这样是否dirty 有没有打破封装 和其他模块(比如API添加出站)的协作性是什么情况

自评:比之前在 Config.Build() 里直接 listen+serve 干净很多,但还不算最理想,还是有一点 dirty。当前 e088cc4 的状态大概是:

  1. 没再在解析配置时启动服务,生命周期改到 core.Instance.Start()/Close(),这部分没有再明显打破构建/运行边界;
  2. 但 Browser Dialer 仍然是包级全局状态,不是完整挂进 Xray feature 体系,所以封装仍不算彻底;
  3. 和其他模块的协作性上,静态配置启动没问题,但 API 动态添加 outbound 不会走这套 collect/configure 流程,所以新加的 browser:// 不会自动注册/监听,这里就是不完整的,和现在“不支持动态增减”的方案一致。

所以结论就是:比 parse-time side effect 版好很多,但如果按最严格标准看,仍然不是最干净的最终形态。

截图:

@Fangliding
Copy link
Copy Markdown
Member

它不好用的原因不是env 是需要单开一个浏览器网页还得让它绕过代理系统还得一直活着不让他被回收掉以及不能指定IP必须指定可以正确解析的域名

@fish4terrisa-MSDSM
Copy link
Copy Markdown
Contributor

fish4terrisa-MSDSM commented Apr 27, 2026

它不好用的原因不是env 是需要单开一个浏览器网页还得让它绕过代理系统还得一直活着不让他被回收掉以及不能指定IP必须指定可以正确解析的域名

這個是受限於browser dialer本身最開始的設計吧

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 27, 2026

它不好用的原因不是env 是需要单开一个浏览器网页还得让它绕过代理系统还得一直活着不让他被回收掉以及不能指定IP必须指定可以正确解析的域名

指定 IP 是可以的,如果浏览器没配置 DoH 的话

按这么说的话就是还是要改成前所未有的顶级模块,这个模块能自动改 DNS IP,并且具有在 HTTP/Socks/Tunnel/TUN 入站自动识别请求并 direct 出去的能力,就像 TUN 的 autoOutboundsInterface,这个工程量就有点大了但用起来最方便

@fish4terrisa-MSDSM 感觉可能最终会设计成那样,我的建议还是先别等这个 PR 了,先按 v26.4.25 的代码给 v2rayNG 加上

@fish4terrisa-MSDSM
Copy link
Copy Markdown
Contributor

@fish4terrisa-MSDSM 感觉可能最终会设计成那样,我的建议还是先别等这个 PR 了,先按 v26.4.25 的代码给 v2rayNG 加上

行吧 我這幾天看下, 主要是如果使用v26.4.25, v2rayNG用的AndroidLibXrayLite那邊還要改,不單是v2rayNG的部分, 而且這邊改完後還得revert AndroidLibXrayLite的改動, 畢竟等改完browserDialer的配置肯定在配置文件裡,不會再是單獨調用一個Reload函數讓它讀環境變量
我比較懶所以想等下(笑

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 27, 2026

因为这个搞着搞着,既然风扇都这么说了,不如一步到位弄成比 TUN 更顶级的模块,方便直接跑 core 的直接使用,每个 GUI 也无需重复工作,只是对于 core 来说有一些工程量,所以得排到下个月的版本了

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.

4 participants