feat: support fast_open for hysteria, and unified parameter naming
This commit is contained in:
parent
5e6b33327b
commit
7fde98c9df
2 changed files with 79 additions and 44 deletions
|
@ -10,6 +10,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
@ -97,17 +98,23 @@ type HysteriaOption struct {
|
||||||
Down string `proxy:"down"`
|
Down string `proxy:"down"`
|
||||||
DownSpeed int `proxy:"down-speed,omitempty"` // compatible with Stash
|
DownSpeed int `proxy:"down-speed,omitempty"` // compatible with Stash
|
||||||
Auth string `proxy:"auth,omitempty"`
|
Auth string `proxy:"auth,omitempty"`
|
||||||
AuthString string `proxy:"auth_str,omitempty"`
|
OldAuthString string `proxy:"auth_str,omitempty"`
|
||||||
|
AuthString string `proxy:"auth-str,omitempty"`
|
||||||
Obfs string `proxy:"obfs,omitempty"`
|
Obfs string `proxy:"obfs,omitempty"`
|
||||||
SNI string `proxy:"sni,omitempty"`
|
SNI string `proxy:"sni,omitempty"`
|
||||||
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
|
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
|
||||||
Fingerprint string `proxy:"fingerprint,omitempty"`
|
Fingerprint string `proxy:"fingerprint,omitempty"`
|
||||||
ALPN []string `proxy:"alpn,omitempty"`
|
ALPN []string `proxy:"alpn,omitempty"`
|
||||||
CustomCA string `proxy:"ca,omitempty"`
|
CustomCA string `proxy:"ca,omitempty"`
|
||||||
CustomCAString string `proxy:"ca_str,omitempty"`
|
OldCustomCAString string `proxy:"ca_str,omitempty"`
|
||||||
ReceiveWindowConn int `proxy:"recv_window_conn,omitempty"`
|
CustomCAString string `proxy:"ca-str,omitempty"`
|
||||||
ReceiveWindow int `proxy:"recv_window,omitempty"`
|
OldReceiveWindowConn int `proxy:"recv_window_conn,omitempty"`
|
||||||
DisableMTUDiscovery bool `proxy:"disable_mtu_discovery,omitempty"`
|
ReceiveWindowConn int `proxy:"recv-window-conn,omitempty"`
|
||||||
|
OldReceiveWindow int `proxy:"recv_window,omitempty"`
|
||||||
|
ReceiveWindow int `proxy:"recv-window,omitempty"`
|
||||||
|
OldDisableMTUDiscovery bool `proxy:"disable_mtu_discovery,omitempty"`
|
||||||
|
DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"`
|
||||||
|
FastOpen bool `proxy:"fast-open,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *HysteriaOption) Speed() (uint64, uint64, error) {
|
func (c *HysteriaOption) Speed() (uint64, uint64, error) {
|
||||||
|
@ -151,8 +158,8 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("hysteria %s load ca error: %w", addr, err)
|
return nil, fmt.Errorf("hysteria %s load ca error: %w", addr, err)
|
||||||
}
|
}
|
||||||
} else if option.CustomCAString != "" {
|
} else if compatibilityValue(option.CustomCAString, option.OldCustomCAString) != "" {
|
||||||
bs = []byte(option.CustomCAString)
|
bs = []byte(compatibilityValue(option.CustomCAString, option.OldCustomCAString))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(bs) > 0 {
|
if len(bs) > 0 {
|
||||||
|
@ -182,14 +189,13 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
|
||||||
} else {
|
} else {
|
||||||
tlsConfig.NextProtos = []string{DefaultALPN}
|
tlsConfig.NextProtos = []string{DefaultALPN}
|
||||||
}
|
}
|
||||||
|
|
||||||
quicConfig := &quic.Config{
|
quicConfig := &quic.Config{
|
||||||
InitialStreamReceiveWindow: uint64(option.ReceiveWindowConn),
|
InitialStreamReceiveWindow: uint64(compatibilityValue(option.ReceiveWindowConn, option.OldReceiveWindow)),
|
||||||
MaxStreamReceiveWindow: uint64(option.ReceiveWindowConn),
|
MaxStreamReceiveWindow: uint64(compatibilityValue(option.ReceiveWindowConn, option.OldReceiveWindow)),
|
||||||
InitialConnectionReceiveWindow: uint64(option.ReceiveWindow),
|
InitialConnectionReceiveWindow: uint64(compatibilityValue(option.ReceiveWindow, option.OldReceiveWindow)),
|
||||||
MaxConnectionReceiveWindow: uint64(option.ReceiveWindow),
|
MaxConnectionReceiveWindow: uint64(compatibilityValue(option.ReceiveWindow, option.OldReceiveWindow)),
|
||||||
KeepAlivePeriod: 10 * time.Second,
|
KeepAlivePeriod: 10 * time.Second,
|
||||||
DisablePathMTUDiscovery: option.DisableMTUDiscovery,
|
DisablePathMTUDiscovery: compatibilityValue(option.DisableMTUDiscovery, option.OldDisableMTUDiscovery),
|
||||||
EnableDatagrams: true,
|
EnableDatagrams: true,
|
||||||
}
|
}
|
||||||
if option.ObfsProtocol != "" {
|
if option.ObfsProtocol != "" {
|
||||||
|
@ -198,11 +204,11 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
|
||||||
if option.Protocol == "" {
|
if option.Protocol == "" {
|
||||||
option.Protocol = DefaultProtocol
|
option.Protocol = DefaultProtocol
|
||||||
}
|
}
|
||||||
if option.ReceiveWindowConn == 0 {
|
if compatibilityValue( option.ReceiveWindowConn, option.OldReceiveWindowConn)== 0 {
|
||||||
quicConfig.InitialStreamReceiveWindow = DefaultStreamReceiveWindow / 10
|
quicConfig.InitialStreamReceiveWindow = DefaultStreamReceiveWindow / 10
|
||||||
quicConfig.MaxStreamReceiveWindow = DefaultStreamReceiveWindow
|
quicConfig.MaxStreamReceiveWindow = DefaultStreamReceiveWindow
|
||||||
}
|
}
|
||||||
if option.ReceiveWindow == 0 {
|
if compatibilityValue( option.ReceiveWindow,option.OldReceiveWindow) == 0 {
|
||||||
quicConfig.InitialConnectionReceiveWindow = DefaultConnectionReceiveWindow / 10
|
quicConfig.InitialConnectionReceiveWindow = DefaultConnectionReceiveWindow / 10
|
||||||
quicConfig.MaxConnectionReceiveWindow = DefaultConnectionReceiveWindow
|
quicConfig.MaxConnectionReceiveWindow = DefaultConnectionReceiveWindow
|
||||||
}
|
}
|
||||||
|
@ -210,7 +216,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
|
||||||
log.Infoln("hysteria: Path MTU Discovery is not yet supported on this platform")
|
log.Infoln("hysteria: Path MTU Discovery is not yet supported on this platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
var auth = []byte(option.AuthString)
|
var auth = []byte(compatibilityValue(option.AuthString, option.OldAuthString))
|
||||||
if option.Auth != "" {
|
if option.Auth != "" {
|
||||||
auth, err = base64.StdEncoding.DecodeString(option.Auth)
|
auth, err = base64.StdEncoding.DecodeString(option.Auth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -235,7 +241,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
|
||||||
client, err := core.NewClient(
|
client, err := core.NewClient(
|
||||||
addr, option.Protocol, auth, tlsConfig, quicConfig, clientTransport, up, down, func(refBPS uint64) congestion.CongestionControl {
|
addr, option.Protocol, auth, tlsConfig, quicConfig, clientTransport, up, down, func(refBPS uint64) congestion.CongestionControl {
|
||||||
return hyCongestion.NewBrutalSender(congestion.ByteCount(refBPS))
|
return hyCongestion.NewBrutalSender(congestion.ByteCount(refBPS))
|
||||||
}, obfuscator,
|
}, obfuscator, option.FastOpen,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("hysteria %s create error: %w", addr, err)
|
return nil, fmt.Errorf("hysteria %s create error: %w", addr, err)
|
||||||
|
@ -254,6 +260,13 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func compatibilityValue[T any](prefer T, fallback T) T {
|
||||||
|
if reflect.ValueOf(prefer).IsZero() {
|
||||||
|
return fallback
|
||||||
|
} else {
|
||||||
|
return prefer
|
||||||
|
}
|
||||||
|
}
|
||||||
func stringToBps(s string) uint64 {
|
func stringToBps(s string) uint64 {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -45,11 +45,12 @@ type Client struct {
|
||||||
udpSessionMutex sync.RWMutex
|
udpSessionMutex sync.RWMutex
|
||||||
udpSessionMap map[uint32]chan *udpMessage
|
udpSessionMap map[uint32]chan *udpMessage
|
||||||
udpDefragger defragger
|
udpDefragger defragger
|
||||||
|
fastOpen bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(serverAddr string, protocol string, auth []byte, tlsConfig *tls.Config, quicConfig *quic.Config,
|
func NewClient(serverAddr string, protocol string, auth []byte, tlsConfig *tls.Config, quicConfig *quic.Config,
|
||||||
transport *transport.ClientTransport, sendBPS uint64, recvBPS uint64, congestionFactory CongestionFactory,
|
transport *transport.ClientTransport, sendBPS uint64, recvBPS uint64, congestionFactory CongestionFactory,
|
||||||
obfuscator obfs.Obfuscator) (*Client, error) {
|
obfuscator obfs.Obfuscator,fastOpen bool) (*Client, error) {
|
||||||
quicConfig.DisablePathMTUDiscovery = quicConfig.DisablePathMTUDiscovery || pmtud_fix.DisablePathMTUDiscovery
|
quicConfig.DisablePathMTUDiscovery = quicConfig.DisablePathMTUDiscovery || pmtud_fix.DisablePathMTUDiscovery
|
||||||
c := &Client{
|
c := &Client{
|
||||||
transport: transport,
|
transport: transport,
|
||||||
|
@ -62,6 +63,7 @@ func NewClient(serverAddr string, protocol string, auth []byte, tlsConfig *tls.C
|
||||||
obfuscator: obfuscator,
|
obfuscator: obfuscator,
|
||||||
tlsConfig: tlsConfig,
|
tlsConfig: tlsConfig,
|
||||||
quicConfig: quicConfig,
|
quicConfig: quicConfig,
|
||||||
|
fastOpen: fastOpen,
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
@ -205,6 +207,9 @@ func (c *Client) DialTCP(addr string, dialer transport.PacketDialer) (net.Conn,
|
||||||
_ = stream.Close()
|
_ = stream.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// If fast open is enabled, we return the stream immediately
|
||||||
|
// and defer the response handling to the first Read() call
|
||||||
|
if !c.fastOpen {
|
||||||
// Read response
|
// Read response
|
||||||
var sr serverResponse
|
var sr serverResponse
|
||||||
err = struc.Unpack(stream, &sr)
|
err = struc.Unpack(stream, &sr)
|
||||||
|
@ -216,10 +221,13 @@ func (c *Client) DialTCP(addr string, dialer transport.PacketDialer) (net.Conn,
|
||||||
_ = stream.Close()
|
_ = stream.Close()
|
||||||
return nil, fmt.Errorf("connection rejected: %s", sr.Message)
|
return nil, fmt.Errorf("connection rejected: %s", sr.Message)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &quicConn{
|
return &quicConn{
|
||||||
Orig: stream,
|
Orig: stream,
|
||||||
PseudoLocalAddr: session.LocalAddr(),
|
PseudoLocalAddr: session.LocalAddr(),
|
||||||
PseudoRemoteAddr: session.RemoteAddr(),
|
PseudoRemoteAddr: session.RemoteAddr(),
|
||||||
|
Established: !c.fastOpen,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,9 +296,23 @@ type quicConn struct {
|
||||||
Orig quic.Stream
|
Orig quic.Stream
|
||||||
PseudoLocalAddr net.Addr
|
PseudoLocalAddr net.Addr
|
||||||
PseudoRemoteAddr net.Addr
|
PseudoRemoteAddr net.Addr
|
||||||
|
Established bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *quicConn) Read(b []byte) (n int, err error) {
|
func (w *quicConn) Read(b []byte) (n int, err error) {
|
||||||
|
if !w.Established {
|
||||||
|
var sr serverResponse
|
||||||
|
err := struc.Unpack(w.Orig, &sr)
|
||||||
|
if err != nil {
|
||||||
|
_ = w.Close()
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if !sr.OK {
|
||||||
|
_ = w.Close()
|
||||||
|
return 0, fmt.Errorf("connection rejected: %s", sr.Message)
|
||||||
|
}
|
||||||
|
w.Established = true
|
||||||
|
}
|
||||||
return w.Orig.Read(b)
|
return w.Orig.Read(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue