feat: Add v2ray httpupgrade fast open support

This commit is contained in:
gVisor bot 2023-11-24 13:02:00 +08:00
parent 9a1943c568
commit ee9b7ce693
8 changed files with 160 additions and 77 deletions

View File

@ -58,15 +58,16 @@ type simpleObfsOption struct {
} }
type v2rayObfsOption struct { type v2rayObfsOption struct {
Mode string `obfs:"mode"` Mode string `obfs:"mode"`
Host string `obfs:"host,omitempty"` Host string `obfs:"host,omitempty"`
Path string `obfs:"path,omitempty"` Path string `obfs:"path,omitempty"`
TLS bool `obfs:"tls,omitempty"` TLS bool `obfs:"tls,omitempty"`
Fingerprint string `obfs:"fingerprint,omitempty"` Fingerprint string `obfs:"fingerprint,omitempty"`
Headers map[string]string `obfs:"headers,omitempty"` Headers map[string]string `obfs:"headers,omitempty"`
SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"`
Mux bool `obfs:"mux,omitempty"` Mux bool `obfs:"mux,omitempty"`
V2rayHttpUpgrade bool `obfs:"v2ray-http-upgrade,omitempty"` V2rayHttpUpgrade bool `obfs:"v2ray-http-upgrade,omitempty"`
V2rayHttpUpgradeFastOpen bool `obfs:"v2ray-http-upgrade-fast-open,omitempty"`
} }
type shadowTLSOption struct { type shadowTLSOption struct {
@ -260,11 +261,12 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
} }
obfsMode = opts.Mode obfsMode = opts.Mode
v2rayOption = &v2rayObfs.Option{ v2rayOption = &v2rayObfs.Option{
Host: opts.Host, Host: opts.Host,
Path: opts.Path, Path: opts.Path,
Headers: opts.Headers, Headers: opts.Headers,
Mux: opts.Mux, Mux: opts.Mux,
V2rayHttpUpgrade: opts.V2rayHttpUpgrade, V2rayHttpUpgrade: opts.V2rayHttpUpgrade,
V2rayHttpUpgradeFastOpen: opts.V2rayHttpUpgradeFastOpen,
} }
if opts.TLS { if opts.TLS {

View File

@ -53,11 +53,12 @@ func (t *Trojan) plainStream(ctx context.Context, c net.Conn) (net.Conn, error)
if t.option.Network == "ws" { if t.option.Network == "ws" {
host, port, _ := net.SplitHostPort(t.addr) host, port, _ := net.SplitHostPort(t.addr)
wsOpts := &trojan.WebsocketOption{ wsOpts := &trojan.WebsocketOption{
Host: host, Host: host,
Port: port, Port: port,
Path: t.option.WSOpts.Path, Path: t.option.WSOpts.Path,
V2rayHttpUpgrade: t.option.WSOpts.V2rayHttpUpgrade, V2rayHttpUpgrade: t.option.WSOpts.V2rayHttpUpgrade,
Headers: http.Header{}, V2rayHttpUpgradeFastOpen: t.option.WSOpts.V2rayHttpUpgradeFastOpen,
Headers: http.Header{},
} }
if t.option.SNI != "" { if t.option.SNI != "" {

View File

@ -88,14 +88,15 @@ func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
case "ws": case "ws":
host, port, _ := net.SplitHostPort(v.addr) host, port, _ := net.SplitHostPort(v.addr)
wsOpts := &vmess.WebsocketConfig{ wsOpts := &vmess.WebsocketConfig{
Host: host, Host: host,
Port: port, Port: port,
Path: v.option.WSOpts.Path, Path: v.option.WSOpts.Path,
MaxEarlyData: v.option.WSOpts.MaxEarlyData, MaxEarlyData: v.option.WSOpts.MaxEarlyData,
EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName, EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName,
V2rayHttpUpgrade: v.option.WSOpts.V2rayHttpUpgrade, V2rayHttpUpgrade: v.option.WSOpts.V2rayHttpUpgrade,
ClientFingerprint: v.option.ClientFingerprint, V2rayHttpUpgradeFastOpen: v.option.WSOpts.V2rayHttpUpgradeFastOpen,
Headers: http.Header{}, ClientFingerprint: v.option.ClientFingerprint,
Headers: http.Header{},
} }
if len(v.option.WSOpts.Headers) != 0 { if len(v.option.WSOpts.Headers) != 0 {

View File

@ -87,11 +87,12 @@ type GrpcOptions struct {
} }
type WSOptions struct { type WSOptions struct {
Path string `proxy:"path,omitempty"` Path string `proxy:"path,omitempty"`
Headers map[string]string `proxy:"headers,omitempty"` Headers map[string]string `proxy:"headers,omitempty"`
MaxEarlyData int `proxy:"max-early-data,omitempty"` MaxEarlyData int `proxy:"max-early-data,omitempty"`
EarlyDataHeaderName string `proxy:"early-data-header-name,omitempty"` EarlyDataHeaderName string `proxy:"early-data-header-name,omitempty"`
V2rayHttpUpgrade bool `proxy:"v2ray-http-upgrade,omitempty"` V2rayHttpUpgrade bool `proxy:"v2ray-http-upgrade,omitempty"`
V2rayHttpUpgradeFastOpen bool `proxy:"v2ray-http-upgrade-fast-open,omitempty"`
} }
// StreamConnContext implements C.ProxyAdapter // StreamConnContext implements C.ProxyAdapter
@ -106,14 +107,15 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
case "ws": case "ws":
host, port, _ := net.SplitHostPort(v.addr) host, port, _ := net.SplitHostPort(v.addr)
wsOpts := &mihomoVMess.WebsocketConfig{ wsOpts := &mihomoVMess.WebsocketConfig{
Host: host, Host: host,
Port: port, Port: port,
Path: v.option.WSOpts.Path, Path: v.option.WSOpts.Path,
MaxEarlyData: v.option.WSOpts.MaxEarlyData, MaxEarlyData: v.option.WSOpts.MaxEarlyData,
EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName, EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName,
V2rayHttpUpgrade: v.option.WSOpts.V2rayHttpUpgrade, V2rayHttpUpgrade: v.option.WSOpts.V2rayHttpUpgrade,
ClientFingerprint: v.option.ClientFingerprint, V2rayHttpUpgradeFastOpen: v.option.WSOpts.V2rayHttpUpgradeFastOpen,
Headers: http.Header{}, ClientFingerprint: v.option.ClientFingerprint,
Headers: http.Header{},
} }
if len(v.option.WSOpts.Headers) != 0 { if len(v.option.WSOpts.Headers) != 0 {

View File

@ -55,11 +55,12 @@ type Option struct {
} }
type WebsocketOption struct { type WebsocketOption struct {
Host string Host string
Port string Port string
Path string Path string
Headers http.Header Headers http.Header
V2rayHttpUpgrade bool V2rayHttpUpgrade bool
V2rayHttpUpgradeFastOpen bool
} }
type Trojan struct { type Trojan struct {
@ -129,14 +130,15 @@ func (t *Trojan) StreamWebsocketConn(ctx context.Context, conn net.Conn, wsOptio
} }
return vmess.StreamWebsocketConn(ctx, conn, &vmess.WebsocketConfig{ return vmess.StreamWebsocketConn(ctx, conn, &vmess.WebsocketConfig{
Host: wsOptions.Host, Host: wsOptions.Host,
Port: wsOptions.Port, Port: wsOptions.Port,
Path: wsOptions.Path, Path: wsOptions.Path,
Headers: wsOptions.Headers, Headers: wsOptions.Headers,
V2rayHttpUpgrade: wsOptions.V2rayHttpUpgrade, V2rayHttpUpgrade: wsOptions.V2rayHttpUpgrade,
TLS: true, V2rayHttpUpgradeFastOpen: wsOptions.V2rayHttpUpgradeFastOpen,
TLSConfig: tlsConfig, TLS: true,
ClientFingerprint: t.option.ClientFingerprint, TLSConfig: tlsConfig,
ClientFingerprint: t.option.ClientFingerprint,
}) })
} }

View File

@ -12,15 +12,16 @@ import (
// Option is options of websocket obfs // Option is options of websocket obfs
type Option struct { type Option struct {
Host string Host string
Port string Port string
Path string Path string
Headers map[string]string Headers map[string]string
TLS bool TLS bool
SkipCertVerify bool SkipCertVerify bool
Fingerprint string Fingerprint string
Mux bool Mux bool
V2rayHttpUpgrade bool V2rayHttpUpgrade bool
V2rayHttpUpgradeFastOpen bool
} }
// NewV2rayObfs return a HTTPObfs // NewV2rayObfs return a HTTPObfs
@ -31,11 +32,12 @@ func NewV2rayObfs(ctx context.Context, conn net.Conn, option *Option) (net.Conn,
} }
config := &vmess.WebsocketConfig{ config := &vmess.WebsocketConfig{
Host: option.Host, Host: option.Host,
Port: option.Port, Port: option.Port,
Path: option.Path, Path: option.Path,
V2rayHttpUpgrade: option.V2rayHttpUpgrade, V2rayHttpUpgrade: option.V2rayHttpUpgrade,
Headers: header, V2rayHttpUpgradeFastOpen: option.V2rayHttpUpgradeFastOpen,
Headers: header,
} }
if option.TLS { if option.TLS {

View File

@ -0,0 +1,65 @@
package vmess
import (
"fmt"
"net/http"
"strings"
"sync"
"github.com/metacubex/mihomo/common/buf"
"github.com/metacubex/mihomo/common/net"
)
type httpUpgradeEarlyConn struct {
*net.BufferedConn
create sync.Once
done bool
err error
}
func (c *httpUpgradeEarlyConn) readResponse() {
var request http.Request
response, err := http.ReadResponse(c.Reader(), &request)
c.done = true
if err != nil {
c.err = err
return
}
if response.StatusCode != http.StatusSwitchingProtocols ||
!strings.EqualFold(response.Header.Get("Connection"), "upgrade") ||
!strings.EqualFold(response.Header.Get("Upgrade"), "websocket") {
c.err = fmt.Errorf("unexpected status: %s", response.Status)
return
}
}
func (c *httpUpgradeEarlyConn) Read(p []byte) (int, error) {
c.create.Do(c.readResponse)
if c.err != nil {
return 0, c.err
}
return c.BufferedConn.Read(p)
}
func (c *httpUpgradeEarlyConn) ReadBuffer(buffer *buf.Buffer) error {
c.create.Do(c.readResponse)
if c.err != nil {
return c.err
}
return c.BufferedConn.ReadBuffer(buffer)
}
func (c *httpUpgradeEarlyConn) ReaderReplaceable() bool {
return c.done
}
func (c *httpUpgradeEarlyConn) ReaderPossiblyReplaceable() bool {
return !c.done
}
func (c *httpUpgradeEarlyConn) ReadCached() *buf.Buffer {
if c.done {
return c.BufferedConn.ReadCached()
}
return nil
}

View File

@ -49,16 +49,17 @@ type websocketWithEarlyDataConn struct {
} }
type WebsocketConfig struct { type WebsocketConfig struct {
Host string Host string
Port string Port string
Path string Path string
Headers http.Header Headers http.Header
TLS bool TLS bool
TLSConfig *tls.Config TLSConfig *tls.Config
MaxEarlyData int MaxEarlyData int
EarlyDataHeaderName string EarlyDataHeaderName string
ClientFingerprint string ClientFingerprint string
V2rayHttpUpgrade bool V2rayHttpUpgrade bool
V2rayHttpUpgradeFastOpen bool
} }
// Read implements net.Conn.Read() // Read implements net.Conn.Read()
@ -415,6 +416,13 @@ func streamWebsocketConn(ctx context.Context, conn net.Conn, c *WebsocketConfig,
return nil, err return nil, err
} }
bufferedConn := N.NewBufferedConn(conn) bufferedConn := N.NewBufferedConn(conn)
if c.V2rayHttpUpgrade && c.V2rayHttpUpgradeFastOpen {
return &httpUpgradeEarlyConn{
BufferedConn: bufferedConn,
}, nil
}
response, err := http.ReadResponse(bufferedConn.Reader(), request) response, err := http.ReadResponse(bufferedConn.Reader(), request)
if err != nil { if err != nil {
return nil, err return nil, err