Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebSocket 0-RTT #375

Merged
merged 1 commit into from
Mar 14, 2021
Merged

WebSocket 0-RTT #375

merged 1 commit into from
Mar 14, 2021

Conversation

RPRX
Copy link
Member

@RPRX RPRX commented Mar 14, 2021

Background

v2fly/v2ray-core#378 本来想的是不等握手完成就连着发 payload,但某些 WebSocket 服务端实现可能会拒绝此行为

v2fly 的计划是将首个 payload 放 path,我不喜欢那个做法,因为要改很多配置,而且 path 往往会被 log

这里感谢 @darhwa 提出可以把首个 payload 放 WS header v2fly/v2ray-core#378 (comment)

Header

最初的想法是需要自定义 header 名,但有一些麻烦,我希望尽量减少对现有配置的改动,以便此功能可以迅速普及

所以最终决定使用 Sec-WebSocket-Protocol 这个 header 承载 early data(一般带着内层 TLS Client Hello):

  1. WebSocket 标准中本来就有它,用于自定义子协议名称,即使经过反代,也能送达 Xray-core 服务端
  2. 浏览器 JavaScript 可以 ws = new WebSocket(url [, protocols]) 设置它,方便日后的浏览器中转

具体方式是客户端将 early data 进行标准 Base64 编码后放入这个 header,服务端若成功解析即当作 early data 处理

Configuration

为了此功能可以迅速普及,经过反复思考后,我的设计是不需 GUI 新增配置项,只需在现有 path 后加上 ?ed=2048

  1. 客户端、服务端均升至 Xray-core v1.4.0+,注意不需要改服务端的任何配置
  2. 只需在客户端的 path 后加上 ?ed=2048 即可减少 1-RTT,2048 代表 early data 的长度上限,目前建议写 2048

与此同时,现在就可以下发带 ?ed=2048 的订阅,因为旧的客户端虽然不支持 early data,但仍然可以正常使用节点

  1. 对于新客户端,?ed=2048 在本地就会被解析,不会被发到服务端,到服务端的是 Sec-WebSocket-Protocol
  2. 对于旧客户端,?ed=2048 在本地不会被解析,且会被发到服务端,但只是多了个参数,不会影响正常使用

就像 XUDP 一样,这也是一个非常简单且优雅的解决方案。至此,绝大多数场景下,WSS 的延迟已等同于 TCP+TLS 🎉

@RPRX RPRX merged commit 60b0687 into main Mar 14, 2021
@RPRX RPRX deleted the wsed branch March 14, 2021 07:10
This was referenced Mar 14, 2021
conn, resp, err := dialer.Dial(uri, wsSettings.GetRequestHeader())
header := wsSettings.GetRequestHeader()
if ed != nil {
header.Set("Sec-WebSocket-Protocol", base64.StdEncoding.EncodeToString(ed))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里的header需不需要clone一次,防止并发dialWebsocket的时候出错?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func (c *Config) GetRequestHeader() http.Header {
header := http.Header{}
for _, h := range c.Header {
header.Add(h.Key, h.Value)
}
return header
}

wwqgtxx added a commit to wwqgtxx/wstunnel that referenced this pull request Mar 14, 2021
wwqgtxx added a commit to wwqgtxx/wstunnel that referenced this pull request Mar 15, 2021
(be careful with golang's textproto.CanonicalMIMEHeaderKey)
@RPRX RPRX mentioned this pull request Mar 21, 2021
@RPRX
Copy link
Member Author

RPRX commented Mar 21, 2021

Updates in #421

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