mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2024-11-16 03:32:33 +08:00
feat: Add v2ray httpupgrade fast open support
This commit is contained in:
parent
9a1943c568
commit
ee9b7ce693
|
@ -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 {
|
||||||
|
|
|
@ -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 != "" {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
65
transport/vmess/httpupgrade.go
Normal file
65
transport/vmess/httpupgrade.go
Normal 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
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user