Files
v2ray_simple/ws/server.go

125 lines
4.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package ws
import (
"encoding/base64"
"net"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil"
"github.com/hahahrfool/v2ray_simple/netLayer"
"github.com/hahahrfool/v2ray_simple/utils"
)
// 2048 /3 = 682.6666 ,
// 683 * 4 = 2732
const MaxEarlyDataLen_Base64 = 2732
type Server struct {
upgrader *ws.Upgrader
UseEarlyData bool
}
// 这里默认: 传入的path必须 以 "/" 为前缀. 本函数 不对此进行任何检查.
func NewServer(path string) *Server {
//因为我们vs的架构先统一监听tcp然后再调用Handshake函数
// 所以我们不能直接用http.Handle, 这也彰显了 用 gobwas/ws 包的好处
// 给Upgrader提供的 OnRequest 专门用于过滤 path, 也不需要我们的 httpLayer 去过滤
// 我们的 httpLayer 的 过滤方法仍然是最安全的,可以杜绝 所有非法数据;
// 而 ws.Upgrader.Upgrade 使用了 readLine 函数。如果客户提供一个非法的超长的一行的话,它就会陷入泥淖
// 这个以后 可以先用 httpLayer的过滤方法过滤掉后再用 MultiReader组装回来提供给 upgrader.Upgrade
// ReadBufferSize默认是 4096已经够大
upgrader := &ws.Upgrader{
OnRequest: func(uri []byte) error {
struri := string(uri)
if struri != path {
min := len(path)
if len(struri) < min {
min = len(struri)
}
return utils.NewDataErr("ws path not match", nil, struri[:min])
}
return nil
},
}
return &Server{
upgrader: upgrader,
}
}
// Handshake 用于 websocket的 Server 监听端,建立握手. 用到了 gobwas/ws.Upgrader.
//
// 返回可直接用于读写 websocket 二进制数据的 net.Conn
func (s *Server) Handshake(underlay net.Conn) (net.Conn, error) {
var theUpgrader *ws.Upgrader = s.upgrader
var thePotentialEarlyData []byte
if s.UseEarlyData {
newupgrader := &ws.Upgrader{
OnRequest: s.upgrader.OnRequest,
//xray和v2ray中使用了
// Sec-WebSocket-Protocol 字段 来传输 earlydata来实现 0-rtt
// websocket标准是没有定义 0-rtt的方法的但是ws的握手包头部是可以自定义header的
// gobwas 的upgrader 用 ProtocolCustom 这个函数来检查 protocol的内容
// 它会遍历客户端给出的所有 protocol然后选择一个来返回
//我们若提供了此函数则必须返回true否则 gobwas会返回 ErrMalformedRequest 错误
ProtocolCustom: func(b []byte) (string, bool) {
//如果不提供custom方法的话gobwas会使用 httphead.ScanTokens 来扫描所有的token
// 其实就是扫描逗号分隔 的 字符串
//但是因为我们是 earlydata所以没有逗号全部都是 base64 编码的内容, 所以直接读然后解码即可
//还有要注意的是,因为这个是回调函数,所以需要是闭包 才能向我们实际连接储存数据所以是无法直接放到通用的upgrader里的
if len(b) > MaxEarlyDataLen_Base64 {
return "", true
}
bs, err := base64.RawURLEncoding.DecodeString(string(b))
if err != nil {
// 传来的并不是base64数据可能是其它访问我们网站websocket的情况但是一般我们path复杂都会过滤掉所以直接认为这是非法的
return "", false
}
//if len(bs) != 0 && utils.CanLogDebug() {
// log.Println("Got New ws earlydata", len(bs), bs)
//}
thePotentialEarlyData = bs
return "", true
},
}
theUpgrader = newupgrader
}
_, err := theUpgrader.Upgrade(underlay)
if err != nil {
return nil, err
}
//log.Println("thePotentialEarlyData", len(thePotentialEarlyData))
theConn := &Conn{
Conn: underlay,
underlayIsBasic: netLayer.IsBasicConn(underlay),
state: ws.StateServerSide,
//w: wsutil.NewWriter(underlay, ws.StateServerSide, ws.OpBinary),
r: wsutil.NewServerSideReader(underlay),
}
//不想客户端;服务端是不怕客户端在握手阶段传来任何多余数据的
// 因为我们还没实现 0-rtt
theConn.r.OnIntermediate = wsutil.ControlFrameHandler(underlay, ws.StateServerSide)
if len(thePotentialEarlyData) > 0 {
theConn.serverEndGotEarlyData = thePotentialEarlyData
}
return theConn, nil
}