diff --git a/.github/update.log b/.github/update.log index 158372f5c8..6aaec17849 100644 --- a/.github/update.log +++ b/.github/update.log @@ -1214,3 +1214,4 @@ Update On Sat Dec 13 19:37:32 CET 2025 Update On Sun Dec 14 19:39:26 CET 2025 Update On Mon Dec 15 19:43:13 CET 2025 Update On Tue Dec 16 19:42:39 CET 2025 +Update On Wed Dec 17 19:43:54 CET 2025 diff --git a/clash-meta/adapter/adapter.go b/clash-meta/adapter/adapter.go index 815064568b..6f20c08abb 100644 --- a/clash-meta/adapter/adapter.go +++ b/clash-meta/adapter/adapter.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "net" - "net/http" "net/url" "strings" "time" @@ -17,6 +16,8 @@ import ( "github.com/metacubex/mihomo/component/ca" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" + + "github.com/metacubex/http" ) var UnifiedDelay = atomic.NewBool(false) diff --git a/clash-meta/adapter/inbound/https.go b/clash-meta/adapter/inbound/https.go index 24b30804b7..4ea468555f 100644 --- a/clash-meta/adapter/inbound/https.go +++ b/clash-meta/adapter/inbound/https.go @@ -2,9 +2,10 @@ package inbound import ( "net" - "net/http" C "github.com/metacubex/mihomo/constant" + + "github.com/metacubex/http" ) // NewHTTPS receive CONNECT request and return ConnContext diff --git a/clash-meta/adapter/inbound/util.go b/clash-meta/adapter/inbound/util.go index 17e6479cbb..0cc1e761a7 100644 --- a/clash-meta/adapter/inbound/util.go +++ b/clash-meta/adapter/inbound/util.go @@ -2,12 +2,13 @@ package inbound import ( "net" - "net/http" "net/netip" "strings" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/socks5" + + "github.com/metacubex/http" ) func parseSocksAddr(target socks5.Addr) *C.Metadata { diff --git a/clash-meta/adapter/outbound/http.go b/clash-meta/adapter/outbound/http.go index 9707bf13c2..7f282a21c9 100644 --- a/clash-meta/adapter/outbound/http.go +++ b/clash-meta/adapter/outbound/http.go @@ -3,17 +3,18 @@ package outbound import ( "bufio" "context" - "crypto/tls" "encoding/base64" "errors" "fmt" "net" - "net/http" "strconv" N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/ca" C "github.com/metacubex/mihomo/constant" + + "github.com/metacubex/http" + "github.com/metacubex/tls" ) type Http struct { diff --git a/clash-meta/adapter/outbound/hysteria.go b/clash-meta/adapter/outbound/hysteria.go index 67c9af0b6e..94c0c2e02d 100644 --- a/clash-meta/adapter/outbound/hysteria.go +++ b/clash-meta/adapter/outbound/hysteria.go @@ -2,7 +2,6 @@ package outbound import ( "context" - "crypto/tls" "encoding/base64" "fmt" "net" @@ -13,7 +12,6 @@ import ( "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/ech" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" hyCongestion "github.com/metacubex/mihomo/transport/hysteria/congestion" @@ -23,6 +21,8 @@ import ( "github.com/metacubex/mihomo/transport/hysteria/transport" "github.com/metacubex/mihomo/transport/hysteria/utils" + "github.com/metacubex/tls" + "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/congestion" M "github.com/metacubex/sing/common/metadata" @@ -45,7 +45,7 @@ type Hysteria struct { option *HysteriaOption client *core.Client - tlsConfig *tlsC.Config + tlsConfig *tls.Config echConfig *ech.Config } @@ -175,7 +175,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { if err != nil { return nil, err } - tlsClientConfig := tlsC.UConfig(tlsConfig) + tlsClientConfig := tlsConfig quicConfig := &quic.Config{ InitialStreamReceiveWindow: uint64(option.ReceiveWindowConn), diff --git a/clash-meta/adapter/outbound/hysteria2.go b/clash-meta/adapter/outbound/hysteria2.go index 2982276938..0e797a9845 100644 --- a/clash-meta/adapter/outbound/hysteria2.go +++ b/clash-meta/adapter/outbound/hysteria2.go @@ -2,7 +2,6 @@ package outbound import ( "context" - "crypto/tls" "errors" "fmt" "net" @@ -13,7 +12,6 @@ import ( "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/proxydialer" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" tuicCommon "github.com/metacubex/mihomo/transport/tuic/common" @@ -21,6 +19,7 @@ import ( "github.com/metacubex/quic-go" "github.com/metacubex/sing-quic/hysteria2" M "github.com/metacubex/sing/common/metadata" + "github.com/metacubex/tls" ) func init() { @@ -157,7 +156,7 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { tlsConfig.NextProtos = option.ALPN } - tlsClientConfig := tlsC.UConfig(tlsConfig) + tlsClientConfig := tlsConfig echConfig, err := option.ECHOpts.Parse() if err != nil { return nil, err diff --git a/clash-meta/adapter/outbound/socks5.go b/clash-meta/adapter/outbound/socks5.go index c00480844a..f27f6de8bf 100644 --- a/clash-meta/adapter/outbound/socks5.go +++ b/clash-meta/adapter/outbound/socks5.go @@ -2,7 +2,6 @@ package outbound import ( "context" - "crypto/tls" "errors" "fmt" "io" @@ -14,6 +13,8 @@ import ( "github.com/metacubex/mihomo/component/ca" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/socks5" + + "github.com/metacubex/tls" ) type Socks5 struct { diff --git a/clash-meta/adapter/outbound/trojan.go b/clash-meta/adapter/outbound/trojan.go index cf86f7b452..3390c57709 100644 --- a/clash-meta/adapter/outbound/trojan.go +++ b/clash-meta/adapter/outbound/trojan.go @@ -2,11 +2,9 @@ package outbound import ( "context" - "crypto/tls" "errors" "fmt" "net" - "net/http" "strconv" N "github.com/metacubex/mihomo/common/net" @@ -18,6 +16,9 @@ import ( "github.com/metacubex/mihomo/transport/shadowsocks/core" "github.com/metacubex/mihomo/transport/trojan" "github.com/metacubex/mihomo/transport/vmess" + + "github.com/metacubex/http" + "github.com/metacubex/tls" ) type Trojan struct { diff --git a/clash-meta/adapter/outbound/tuic.go b/clash-meta/adapter/outbound/tuic.go index e29ad3ccbc..fd1268ad1e 100644 --- a/clash-meta/adapter/outbound/tuic.go +++ b/clash-meta/adapter/outbound/tuic.go @@ -2,7 +2,6 @@ package outbound import ( "context" - "crypto/tls" "fmt" "math" "net" @@ -11,7 +10,6 @@ import ( "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ech" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/tuic" @@ -19,6 +17,7 @@ import ( "github.com/metacubex/quic-go" M "github.com/metacubex/sing/common/metadata" "github.com/metacubex/sing/common/uot" + "github.com/metacubex/tls" ) type Tuic struct { @@ -26,7 +25,7 @@ type Tuic struct { option *TuicOption client *tuic.PoolClient - tlsConfig *tlsC.Config + tlsConfig *tls.Config echConfig *ech.Config } @@ -233,7 +232,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { tlsConfig.InsecureSkipVerify = true // tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config } - tlsClientConfig := tlsC.UConfig(tlsConfig) + tlsClientConfig := tlsConfig echConfig, err := option.ECHOpts.Parse() if err != nil { return nil, err diff --git a/clash-meta/adapter/outbound/vless.go b/clash-meta/adapter/outbound/vless.go index c5c759ac90..3ca1bb9f79 100644 --- a/clash-meta/adapter/outbound/vless.go +++ b/clash-meta/adapter/outbound/vless.go @@ -2,10 +2,8 @@ package outbound import ( "context" - "crypto/tls" "fmt" "net" - "net/http" "strconv" "github.com/metacubex/mihomo/common/convert" @@ -20,9 +18,11 @@ import ( "github.com/metacubex/mihomo/transport/vless/encryption" "github.com/metacubex/mihomo/transport/vmess" + "github.com/metacubex/http" vmessSing "github.com/metacubex/sing-vmess" "github.com/metacubex/sing-vmess/packetaddr" M "github.com/metacubex/sing/common/metadata" + "github.com/metacubex/tls" ) type Vless struct { diff --git a/clash-meta/adapter/outbound/vmess.go b/clash-meta/adapter/outbound/vmess.go index ea874250ff..5654cd7c48 100644 --- a/clash-meta/adapter/outbound/vmess.go +++ b/clash-meta/adapter/outbound/vmess.go @@ -2,11 +2,9 @@ package outbound import ( "context" - "crypto/tls" "errors" "fmt" "net" - "net/http" "strconv" "strings" "sync" @@ -21,9 +19,11 @@ import ( "github.com/metacubex/mihomo/transport/gun" mihomoVMess "github.com/metacubex/mihomo/transport/vmess" + "github.com/metacubex/http" vmess "github.com/metacubex/sing-vmess" "github.com/metacubex/sing-vmess/packetaddr" M "github.com/metacubex/sing/common/metadata" + "github.com/metacubex/tls" ) var ErrUDPRemoteAddrMismatch = errors.New("udp packet dropped due to mismatched remote address") diff --git a/clash-meta/adapter/provider/provider.go b/clash-meta/adapter/provider/provider.go index 6ee4fbb110..ff21fe89bf 100644 --- a/clash-meta/adapter/provider/provider.go +++ b/clash-meta/adapter/provider/provider.go @@ -4,7 +4,6 @@ import ( "encoding/json" "errors" "fmt" - "net/http" "reflect" "runtime" "strings" @@ -21,6 +20,7 @@ import ( "github.com/metacubex/mihomo/tunnel/statistic" "github.com/dlclark/regexp2" + "github.com/metacubex/http" "gopkg.in/yaml.v3" ) diff --git a/clash-meta/common/convert/util.go b/clash-meta/common/convert/util.go index ab00637493..c944ef18eb 100644 --- a/clash-meta/common/convert/util.go +++ b/clash-meta/common/convert/util.go @@ -2,12 +2,12 @@ package convert import ( "encoding/base64" - "net/http" "strings" "time" "github.com/metacubex/mihomo/common/utils" + "github.com/metacubex/http" "github.com/metacubex/randv2" "github.com/metacubex/sing-shadowsocks/shadowimpl" ) diff --git a/clash-meta/component/tls/auth.go b/clash-meta/component/ca/auth.go similarity index 64% rename from clash-meta/component/tls/auth.go rename to clash-meta/component/ca/auth.go index bfda9f3553..bd25598900 100644 --- a/clash-meta/component/tls/auth.go +++ b/clash-meta/component/ca/auth.go @@ -1,17 +1,17 @@ -package tls +package ca import ( - utls "github.com/metacubex/utls" + "github.com/metacubex/tls" ) -type ClientAuthType = utls.ClientAuthType +type ClientAuthType = tls.ClientAuthType const ( - NoClientCert = utls.NoClientCert - RequestClientCert = utls.RequestClientCert - RequireAnyClientCert = utls.RequireAnyClientCert - VerifyClientCertIfGiven = utls.VerifyClientCertIfGiven - RequireAndVerifyClientCert = utls.RequireAndVerifyClientCert + NoClientCert = tls.NoClientCert + RequestClientCert = tls.RequestClientCert + RequireAnyClientCert = tls.RequireAnyClientCert + VerifyClientCertIfGiven = tls.VerifyClientCertIfGiven + RequireAndVerifyClientCert = tls.RequireAndVerifyClientCert ) func ClientAuthTypeFromString(s string) ClientAuthType { diff --git a/clash-meta/component/ca/config.go b/clash-meta/component/ca/config.go index 8f96f7452e..9cc8839f40 100644 --- a/clash-meta/component/ca/config.go +++ b/clash-meta/component/ca/config.go @@ -1,7 +1,6 @@ package ca import ( - "crypto/tls" "crypto/x509" _ "embed" "errors" @@ -13,6 +12,8 @@ import ( "github.com/metacubex/mihomo/common/once" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/ntp" + + "github.com/metacubex/tls" ) var globalCertPool *x509.CertPool diff --git a/clash-meta/component/ca/keypair.go b/clash-meta/component/ca/keypair.go index cefd0cf667..13b38dc11f 100644 --- a/clash-meta/component/ca/keypair.go +++ b/clash-meta/component/ca/keypair.go @@ -7,13 +7,14 @@ import ( "crypto/elliptic" "crypto/rand" "crypto/rsa" - "crypto/tls" "crypto/x509" "encoding/pem" "fmt" "math/big" "os" "time" + + "github.com/metacubex/tls" ) type Path interface { diff --git a/clash-meta/component/ech/ech.go b/clash-meta/component/ech/ech.go index a4064c8df0..fa685d2d3b 100644 --- a/clash-meta/component/ech/ech.go +++ b/clash-meta/component/ech/ech.go @@ -5,13 +5,33 @@ import ( "fmt" tlsC "github.com/metacubex/mihomo/component/tls" + "github.com/metacubex/tls" ) type Config struct { GetEncryptedClientHelloConfigList func(ctx context.Context, serverName string) ([]byte, error) } -func (cfg *Config) ClientHandle(ctx context.Context, tlsConfig *tlsC.Config) (err error) { +func (cfg *Config) ClientHandle(ctx context.Context, tlsConfig *tls.Config) (err error) { + if cfg == nil { + return nil + } + echConfigList, err := cfg.GetEncryptedClientHelloConfigList(ctx, tlsConfig.ServerName) + if err != nil { + return fmt.Errorf("resolve ECH config error: %w", err) + } + + tlsConfig.EncryptedClientHelloConfigList = echConfigList + if tlsConfig.MinVersion != 0 && tlsConfig.MinVersion < tls.VersionTLS13 { + tlsConfig.MinVersion = tls.VersionTLS13 + } + if tlsConfig.MaxVersion != 0 && tlsConfig.MaxVersion < tls.VersionTLS13 { + tlsConfig.MaxVersion = tls.VersionTLS13 + } + return nil +} + +func (cfg *Config) ClientHandleUTLS(ctx context.Context, tlsConfig *tlsC.Config) (err error) { if cfg == nil { return nil } diff --git a/clash-meta/component/ech/key.go b/clash-meta/component/ech/key.go index afae8098c2..b0d572d628 100644 --- a/clash-meta/component/ech/key.go +++ b/clash-meta/component/ech/key.go @@ -10,8 +10,8 @@ import ( "os" "github.com/metacubex/mihomo/component/ca" - tlsC "github.com/metacubex/mihomo/component/tls" + "github.com/metacubex/tls" "golang.org/x/crypto/cryptobyte" ) @@ -85,11 +85,11 @@ func GenECHConfig(publicName string) (configBase64 string, keyPem string, err er return } -func UnmarshalECHKeys(raw []byte) ([]tlsC.EncryptedClientHelloKey, error) { - var keys []tlsC.EncryptedClientHelloKey +func UnmarshalECHKeys(raw []byte) ([]tls.EncryptedClientHelloKey, error) { + var keys []tls.EncryptedClientHelloKey rawString := cryptobyte.String(raw) for !rawString.Empty() { - var key tlsC.EncryptedClientHelloKey + var key tls.EncryptedClientHelloKey if !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.PrivateKey)) { return nil, errors.New("error parsing private key") } @@ -104,7 +104,7 @@ func UnmarshalECHKeys(raw []byte) ([]tlsC.EncryptedClientHelloKey, error) { return keys, nil } -func LoadECHKey(key string, tlsConfig *tlsC.Config, path ca.Path) error { +func LoadECHKey(key string, tlsConfig *tls.Config, path ca.Path) error { if key == "" { return nil } @@ -129,7 +129,7 @@ func LoadECHKey(key string, tlsConfig *tlsC.Config, path ca.Path) error { return nil } -func loadECHKey(echKey []byte, tlsConfig *tlsC.Config) error { +func loadECHKey(echKey []byte, tlsConfig *tls.Config) error { block, rest := pem.Decode(echKey) if block == nil || block.Type != "ECH KEYS" || len(rest) > 0 { return errors.New("invalid ECH keys pem") diff --git a/clash-meta/component/geodata/init.go b/clash-meta/component/geodata/init.go index 08ec1b948d..7b87844249 100644 --- a/clash-meta/component/geodata/init.go +++ b/clash-meta/component/geodata/init.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "io" - "net/http" "os" "sync" "time" @@ -14,6 +13,8 @@ import ( "github.com/metacubex/mihomo/component/mmdb" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" + + "github.com/metacubex/http" ) var ( diff --git a/clash-meta/component/http/http.go b/clash-meta/component/http/http.go index 1683c4da38..0592254bb5 100644 --- a/clash-meta/component/http/http.go +++ b/clash-meta/component/http/http.go @@ -4,7 +4,6 @@ import ( "context" "io" "net" - "net/http" URL "net/url" "runtime" "strings" @@ -13,6 +12,8 @@ import ( "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/listener/inner" + + "github.com/metacubex/http" ) var ( diff --git a/clash-meta/component/resource/vehicle.go b/clash-meta/component/resource/vehicle.go index a34417aab2..86fb3948fe 100644 --- a/clash-meta/component/resource/vehicle.go +++ b/clash-meta/component/resource/vehicle.go @@ -4,7 +4,6 @@ import ( "context" "errors" "io" - "net/http" "os" "path/filepath" "time" @@ -13,6 +12,8 @@ import ( mihomoHttp "github.com/metacubex/mihomo/component/http" "github.com/metacubex/mihomo/component/profile/cachefile" P "github.com/metacubex/mihomo/constant/provider" + + "github.com/metacubex/http" ) const ( diff --git a/clash-meta/component/tls/httpserver.go b/clash-meta/component/tls/httpserver.go index 3ddac4d8ee..6a9426d9e3 100644 --- a/clash-meta/component/tls/httpserver.go +++ b/clash-meta/component/tls/httpserver.go @@ -3,14 +3,13 @@ package tls import ( "context" "net" - "net/http" "runtime/debug" "time" N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/log" - "golang.org/x/net/http2" + "github.com/metacubex/http" ) func extractTlsHandshakeTimeoutFromServer(s *http.Server) time.Duration { @@ -35,8 +34,8 @@ func extractTlsHandshakeTimeoutFromServer(s *http.Server) time.Duration { // only do tls handshake and check NegotiatedProtocol with std's *tls.Conn // so we do the same logic to let http2 (not h2c) work fine func NewListenerForHttps(l net.Listener, httpServer *http.Server, tlsConfig *Config) net.Listener { - http2Server := &http2.Server{} - _ = http2.ConfigureServer(httpServer, http2Server) + http2Server := &http.Http2Server{} + _ = http.Http2ConfigureServer(httpServer, http2Server) return N.NewHandleContextListener(context.Background(), l, func(ctx context.Context, conn net.Conn) (net.Conn, error) { c := Server(conn, tlsConfig) @@ -58,8 +57,8 @@ func NewListenerForHttps(l net.Listener, httpServer *http.Server, tlsConfig *Con _ = conn.SetWriteDeadline(time.Time{}) } - if c.ConnectionState().NegotiatedProtocol == http2.NextProtoTLS { - http2Server.ServeConn(c, &http2.ServeConnOpts{BaseConfig: httpServer}) + if c.ConnectionState().NegotiatedProtocol == http.Http2NextProtoTLS { + http2Server.ServeConn(c, &http.Http2ServeConnOpts{BaseConfig: httpServer}) return nil, net.ErrClosed } return c, nil diff --git a/clash-meta/component/tls/reality.go b/clash-meta/component/tls/reality.go index fe1135f3c4..41079e838e 100644 --- a/clash-meta/component/tls/reality.go +++ b/clash-meta/component/tls/reality.go @@ -10,22 +10,21 @@ import ( "crypto/hmac" "crypto/sha256" "crypto/sha512" - "crypto/tls" "crypto/x509" "encoding/binary" "errors" "net" - "net/http" "strings" "time" "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/ntp" + "github.com/metacubex/http" "github.com/metacubex/randv2" + "github.com/metacubex/tls" utls "github.com/metacubex/utls" "golang.org/x/crypto/hkdf" - "golang.org/x/net/http2" ) const RealityMaxShortIDLen = 8 @@ -37,13 +36,14 @@ type RealityConfig struct { SupportX25519MLKEM768 bool } -func GetRealityConn(ctx context.Context, conn net.Conn, fingerprint UClientHelloID, tlsConfig *Config, realityConfig *RealityConfig) (net.Conn, error) { +func GetRealityConn(ctx context.Context, conn net.Conn, fingerprint UClientHelloID, serverName string, realityConfig *RealityConfig) (net.Conn, error) { for retry := 0; ; retry++ { verifier := &realityVerifier{ - serverName: tlsConfig.ServerName, + serverName: serverName, } uConfig := &utls.Config{ - ServerName: tlsConfig.ServerName, + Time: ntp.Now, + ServerName: serverName, InsecureSkipVerify: true, SessionTicketsDisabled: true, VerifyPeerCertificate: verifier.VerifyPeerCertificate, @@ -132,7 +132,7 @@ func GetRealityConn(ctx context.Context, conn net.Conn, fingerprint UClientHello func realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.ClientHelloID) { defer uConn.Close() client := http.Client{ - Transport: &http2.Transport{ + Transport: &http.Http2Transport{ DialTLSContext: func(ctx context.Context, network, addr string, config *tls.Config) (net.Conn, error) { return uConn, nil }, diff --git a/clash-meta/component/tls/utls.go b/clash-meta/component/tls/utls.go index f68cb997f1..2b33d323dd 100644 --- a/clash-meta/component/tls/utls.go +++ b/clash-meta/component/tls/utls.go @@ -1,13 +1,13 @@ package tls import ( - "crypto/tls" "net" "github.com/metacubex/mihomo/common/once" "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/log" + "github.com/metacubex/tls" utls "github.com/metacubex/utls" "github.com/mroth/weightedrand/v2" ) diff --git a/clash-meta/component/updater/update_core.go b/clash-meta/component/updater/update_core.go index ffc4fb0451..83a303af5c 100644 --- a/clash-meta/component/updater/update_core.go +++ b/clash-meta/component/updater/update_core.go @@ -6,7 +6,6 @@ import ( "context" "fmt" "io" - "net/http" "os" "os/exec" "path/filepath" @@ -20,6 +19,8 @@ import ( C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/constant/features" "github.com/metacubex/mihomo/log" + + "github.com/metacubex/http" ) const ( diff --git a/clash-meta/component/updater/utils.go b/clash-meta/component/updater/utils.go index a77c86fe86..58a0a545c0 100644 --- a/clash-meta/component/updater/utils.go +++ b/clash-meta/component/updater/utils.go @@ -3,11 +3,12 @@ package updater import ( "context" "io" - "net/http" "os" "time" mihomoHttp "github.com/metacubex/mihomo/component/http" + + "github.com/metacubex/http" ) const defaultHttpTimeout = time.Second * 90 diff --git a/clash-meta/dns/client.go b/clash-meta/dns/client.go index 9cc7df2d71..ade44e0890 100644 --- a/clash-meta/dns/client.go +++ b/clash-meta/dns/client.go @@ -2,7 +2,6 @@ package dns import ( "context" - "crypto/tls" "fmt" "net" "strings" @@ -12,6 +11,7 @@ import ( C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" + "github.com/metacubex/tls" D "github.com/miekg/dns" ) diff --git a/clash-meta/dns/doh.go b/clash-meta/dns/doh.go index 29be78b2d4..df0697ec55 100644 --- a/clash-meta/dns/doh.go +++ b/clash-meta/dns/doh.go @@ -2,13 +2,11 @@ package dns import ( "context" - "crypto/tls" "encoding/base64" "errors" "fmt" "io" "net" - "net/http" "net/url" "runtime" "strconv" @@ -16,15 +14,15 @@ import ( "time" "github.com/metacubex/mihomo/component/ca" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" + "github.com/metacubex/http" "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/http3" + "github.com/metacubex/tls" D "github.com/miekg/dns" "golang.org/x/exp/slices" - "golang.org/x/net/http2" ) // Values to configure HTTP and HTTP/2 transport. @@ -439,8 +437,8 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp // Explicitly configure transport to use HTTP/2. // // See https://github.com/AdguardTeam/dnsproxy/issues/11. - var transportH2 *http2.Transport - transportH2, err = http2.ConfigureTransports(transport) + var transportH2 *http.Http2Transport + transportH2, err = http.Http2ConfigureTransports(transport) if err != nil { return nil, err } @@ -530,20 +528,20 @@ func (doh *dnsOverHTTPS) createTransportH3( // Ignore the address and always connect to the one that we got // from the bootstrapper. _ string, - tlsCfg *tlsC.Config, + tlsCfg *tls.Config, cfg *quic.Config, ) (c *quic.Conn, err error) { return doh.dialQuic(ctx, addr, tlsCfg, cfg) }, DisableCompression: true, - TLSClientConfig: tlsC.UConfig(tlsConfig), + TLSClientConfig: tlsConfig, QUICConfig: doh.getQUICConfig(), } return &http3Transport{baseTransport: rt}, nil } -func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tlsC.Config, cfg *quic.Config) (*quic.Conn, error) { +func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (*quic.Conn, error) { ip, port, err := net.SplitHostPort(addr) if err != nil { return nil, err @@ -612,7 +610,7 @@ func (doh *dnsOverHTTPS) probeH3( // Run probeQUIC and probeTLS in parallel and see which one is faster. chQuic := make(chan error, 1) chTLS := make(chan error, 1) - go doh.probeQUIC(ctx, addr, tlsC.UConfig(probeTLSCfg), chQuic) + go doh.probeQUIC(ctx, addr, probeTLSCfg, chQuic) go doh.probeTLS(ctx, probeTLSCfg, chTLS) select { @@ -637,7 +635,7 @@ func (doh *dnsOverHTTPS) probeH3( // probeQUIC attempts to establish a QUIC connection to the specified address. // We run probeQUIC and probeTLS in parallel and see which one is faster. -func (doh *dnsOverHTTPS) probeQUIC(ctx context.Context, addr string, tlsConfig *tlsC.Config, ch chan error) { +func (doh *dnsOverHTTPS) probeQUIC(ctx context.Context, addr string, tlsConfig *tls.Config, ch chan error) { startTime := time.Now() conn, err := doh.dialQuic(ctx, addr, tlsConfig, doh.getQUICConfig()) if err != nil { diff --git a/clash-meta/dns/doq.go b/clash-meta/dns/doq.go index 6861bd4d11..18459e97db 100644 --- a/clash-meta/dns/doq.go +++ b/clash-meta/dns/doq.go @@ -2,7 +2,6 @@ package dns import ( "context" - "crypto/tls" "encoding/binary" "errors" "fmt" @@ -13,11 +12,11 @@ import ( "time" "github.com/metacubex/mihomo/component/ca" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" "github.com/metacubex/quic-go" + "github.com/metacubex/tls" D "github.com/miekg/dns" ) @@ -348,7 +347,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn *quic.Conn, er transport := quic.Transport{Conn: udp} transport.SetCreatedConn(true) // auto close conn transport.SetSingleUse(true) // auto close transport - conn, err = transport.Dial(ctx, &udpAddr, tlsC.UConfig(tlsConfig), doq.getQUICConfig()) + conn, err = transport.Dial(ctx, &udpAddr, tlsConfig, doq.getQUICConfig()) if err != nil { return nil, fmt.Errorf("opening quic connection to %s: %w", doq.addr, err) } diff --git a/clash-meta/go.mod b/clash-meta/go.mod index c0160c2630..de5eacdccf 100644 --- a/clash-meta/go.mod +++ b/clash-meta/go.mod @@ -7,8 +7,6 @@ require ( github.com/coreos/go-iptables v0.8.0 github.com/dlclark/regexp2 v1.11.5 github.com/enfein/mieru/v3 v3.26.0 - github.com/go-chi/chi/v5 v5.2.3 - github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 github.com/gofrs/uuid/v5 v5.4.0 github.com/golang/snappy v1.0.0 @@ -20,15 +18,19 @@ require ( github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b github.com/metacubex/blake3 v0.1.0 github.com/metacubex/chacha v0.1.5 + github.com/metacubex/chi v0.1.0 + github.com/metacubex/cpu v0.1.0 github.com/metacubex/fswatch v0.1.1 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 + github.com/metacubex/http v0.1.0 github.com/metacubex/kcp-go v0.0.0-20251111012849-7455698490e9 - github.com/metacubex/quic-go v0.55.1-0.20251203073212-6940cac967c2 + github.com/metacubex/mlkem v0.1.0 + github.com/metacubex/quic-go v0.57.1-0.20251217071004-e89f497a2e72 github.com/metacubex/randv2 v0.2.0 github.com/metacubex/restls-client-go v0.1.7 github.com/metacubex/sing v0.5.6 github.com/metacubex/sing-mux v0.3.4 - github.com/metacubex/sing-quic v0.0.0-20251004051927-c45ee18473bb + github.com/metacubex/sing-quic v0.0.0-20251217080445-b15217cb57f3 github.com/metacubex/sing-shadowsocks v0.2.12 github.com/metacubex/sing-shadowsocks2 v0.2.7 github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 @@ -37,6 +39,7 @@ require ( github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f github.com/metacubex/smux v0.0.0-20251111013112-03f8d12dafc1 github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443 + github.com/metacubex/tls v0.1.0 github.com/metacubex/utls v1.8.3 github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f github.com/miekg/dns v1.1.63 // lastest version compatible with golang1.20 @@ -44,7 +47,6 @@ require ( github.com/openacid/low v0.1.21 github.com/oschwald/maxminddb-golang v1.12.0 // lastest version compatible with golang1.20 github.com/saba-futai/sudoku v0.0.2-d - github.com/sagernet/cors v1.2.1 github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a github.com/samber/lo v1.52.0 github.com/sirupsen/logrus v1.9.3 @@ -78,12 +80,11 @@ require ( github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gaukas/godicttls v0.0.4 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect github.com/josharian/native v1.1.0 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/klauspost/reedsolomon v1.12.3 // indirect @@ -92,13 +93,14 @@ require ( github.com/mdlayher/socket v0.4.1 // indirect github.com/metacubex/ascon v0.1.0 // indirect github.com/metacubex/gvisor v0.0.0-20250919004547-6122b699a301 // indirect + github.com/metacubex/hkdf v0.1.0 // indirect + github.com/metacubex/hpke v0.1.0 // indirect github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect + github.com/metacubex/qpack v0.6.0 // indirect github.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49 // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect - github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/pierrec/lz4/v4 v4.1.14 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/quic-go/qpack v0.4.0 // indirect github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect @@ -108,6 +110,6 @@ require ( gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect golang.org/x/mod v0.20.0 // indirect golang.org/x/text v0.22.0 // indirect - golang.org/x/time v0.7.0 // indirect + golang.org/x/time v0.10.0 // indirect golang.org/x/tools v0.24.0 // indirect ) diff --git a/clash-meta/go.sum b/clash-meta/go.sum index c6fba8e505..a07e350f93 100644 --- a/clash-meta/go.sum +++ b/clash-meta/go.sum @@ -14,9 +14,6 @@ github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xW github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc= github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -40,15 +37,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= -github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= -github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= -github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= -github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= @@ -58,17 +48,15 @@ github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakr github.com/gofrs/uuid/v5 v5.4.0 h1:EfbpCTjqMuGyq5ZJwxqzn3Cbr2d0rUZU7v5ycAk/e/0= github.com/gofrs/uuid/v5 v5.4.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= +github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4= github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -102,18 +90,32 @@ github.com/metacubex/blake3 v0.1.0 h1:KGnjh/56REO7U+cgZA8dnBhxdP7jByrG7hTP+bu6cq github.com/metacubex/blake3 v0.1.0/go.mod h1:CCkLdzFrqf7xmxCdhQFvJsRRV2mwOLDoSPg6vUTB9Uk= github.com/metacubex/chacha v0.1.5 h1:fKWMb/5c7ZrY8Uoqi79PPFxl+qwR7X/q0OrsAubyX2M= github.com/metacubex/chacha v0.1.5/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8= +github.com/metacubex/chi v0.1.0 h1:rjNDyDj50nRpicG43CNkIw4ssiCbmDL8d7wJXKlUCsg= +github.com/metacubex/chi v0.1.0/go.mod h1:zM5u5oMQt8b2DjvDHvzadKrP6B2ztmasL1YHRMbVV+g= +github.com/metacubex/cpu v0.1.0 h1:8PeTdV9j6UKbN1K5Jvtbi/Jock7dknvzyYuLb8Conmk= +github.com/metacubex/cpu v0.1.0/go.mod h1:09VEt4dSRLR+bOA8l4w4NDuzGZ8n5dkMv7e8axgEeTU= github.com/metacubex/fswatch v0.1.1 h1:jqU7C/v+g0qc2RUFgmAOPoVvfl2BXXUXEumn6oQuxhU= github.com/metacubex/fswatch v0.1.1/go.mod h1:czrTT7Zlbz7vWft8RQu9Qqh+JoX+Nnb+UabuyN1YsgI= github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI= github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= github.com/metacubex/gvisor v0.0.0-20250919004547-6122b699a301 h1:N5GExQJqYAH3gOCshpp2u/J3CtNYzMctmlb0xK9wtbQ= github.com/metacubex/gvisor v0.0.0-20250919004547-6122b699a301/go.mod h1:8LpS0IJW1VmWzUm3ylb0e2SK5QDm5lO/2qwWLZgRpBU= +github.com/metacubex/hkdf v0.1.0 h1:fPA6VzXK8cU1foc/TOmGCDmSa7pZbxlnqhl3RNsthaA= +github.com/metacubex/hkdf v0.1.0/go.mod h1:3seEfds3smgTAXqUGn+tgEJH3uXdsUjOiduG/2EtvZ4= +github.com/metacubex/hpke v0.1.0 h1:gu2jUNhraehWi0P/z5HX2md3d7L1FhPQE6/Q0E9r9xQ= +github.com/metacubex/hpke v0.1.0/go.mod h1:vfDm6gfgrwlXUxKDkWbcE44hXtmc1uxLDm2BcR11b3U= +github.com/metacubex/http v0.1.0 h1:Jcy0I9zKjYijSUaksZU34XEe2xNdoFkgUTB7z7K5q0o= +github.com/metacubex/http v0.1.0/go.mod h1:Nxx0zZAo2AhRfanyL+fmmK6ACMtVsfpwIl1aFAik2Eg= github.com/metacubex/kcp-go v0.0.0-20251111012849-7455698490e9 h1:7m3tRPrLpKOLOvZ/Lp4XCxz0t7rg9t9K35x6TahjR8o= github.com/metacubex/kcp-go v0.0.0-20251111012849-7455698490e9/go.mod h1:HIJZW4QMhbBqXuqC1ly6Hn0TEYT2SzRw58ns1yGhXTs= +github.com/metacubex/mlkem v0.1.0 h1:wFClitonSFcmipzzQvax75beLQU+D7JuC+VK1RzSL8I= +github.com/metacubex/mlkem v0.1.0/go.mod h1:amhaXZVeYNShuy9BILcR7P0gbeo/QLZsnqCdL8U2PDQ= github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 h1:1Qpuy+sU3DmyX9HwI+CrBT/oLNJngvBorR2RbajJcqo= github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793/go.mod h1:RjRNb4G52yAgfR+Oe/kp9G4PJJ97Fnj89eY1BFO3YyA= -github.com/metacubex/quic-go v0.55.1-0.20251203073212-6940cac967c2 h1:21KrRBqF5en0yXwwb5Vpptbeiiu3p7gD0G+RqNYvsvw= -github.com/metacubex/quic-go v0.55.1-0.20251203073212-6940cac967c2/go.mod h1:1lktQFtCD17FZliVypbrDHwbsFSsmz2xz2TRXydvB5c= +github.com/metacubex/qpack v0.6.0 h1:YqClGIMOpiRYLjV1qOs483Od08MdPgRnHjt90FuaAKw= +github.com/metacubex/qpack v0.6.0/go.mod h1:lKGSi7Xk94IMvHGOmxS9eIei3bvIqpOAImEBsaOwTkA= +github.com/metacubex/quic-go v0.57.1-0.20251217071004-e89f497a2e72 h1:kNlYHZ75itJwkerDiySpixX+dKsv/K0TYQsKvuxogNM= +github.com/metacubex/quic-go v0.57.1-0.20251217071004-e89f497a2e72/go.mod h1:N071X2oW2+kIhLlHW3mfcD2QP+zWu2bEs1EEAm66bvI= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/restls-client-go v0.1.7 h1:eCwiXCTQb5WJu9IlgYvDBA1OgrINv58dEe7hcN5H15k= @@ -123,8 +125,8 @@ github.com/metacubex/sing v0.5.6 h1:mEPDCadsCj3DB8gn+t/EtposlYuALEkExa/LUguw6/c= github.com/metacubex/sing v0.5.6/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w= github.com/metacubex/sing-mux v0.3.4 h1:tf4r27CIkzaxq9kBlAXQkgMXq2HPp5Mta60Kb4RCZF0= github.com/metacubex/sing-mux v0.3.4/go.mod h1:SEJfAuykNj/ozbPqngEYqyggwSr81+L7Nu09NRD5mh4= -github.com/metacubex/sing-quic v0.0.0-20251004051927-c45ee18473bb h1:gxrJmnxuEAel+kh3V7ntqkHjURif0xKDu76nzr/BF5Y= -github.com/metacubex/sing-quic v0.0.0-20251004051927-c45ee18473bb/go.mod h1:JK4+PYUKps6pnlicKjsSUAjAcvIUjhorIjdNZGg930M= +github.com/metacubex/sing-quic v0.0.0-20251217080445-b15217cb57f3 h1:3LlkguIRAzyBWLxP5xrETi1AMIt3McZcDlXNgiyXMsE= +github.com/metacubex/sing-quic v0.0.0-20251217080445-b15217cb57f3/go.mod h1:fAyoc/8IFK1yJp8meJvPNyGk7ZnKG1vmNaTwYx6NHA4= github.com/metacubex/sing-shadowsocks v0.2.12 h1:Wqzo8bYXrK5aWqxu/TjlTnYZzAKtKsaFQBdr6IHFaBE= github.com/metacubex/sing-shadowsocks v0.2.12/go.mod h1:2e5EIaw0rxKrm1YTRmiMnDulwbGxH9hAFlrwQLQMQkU= github.com/metacubex/sing-shadowsocks2 v0.2.7 h1:hSuuc0YpsfiqYqt1o+fP4m34BQz4e6wVj3PPBVhor3A= @@ -141,6 +143,8 @@ github.com/metacubex/smux v0.0.0-20251111013112-03f8d12dafc1 h1:a6DF0ze9miXes+rd github.com/metacubex/smux v0.0.0-20251111013112-03f8d12dafc1/go.mod h1:4bPD8HWx9jPJ9aE4uadgyN7D1/Wz3KmPy+vale8sKLE= github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443 h1:H6TnfM12tOoTizYE/qBHH3nEuibIelmHI+BVSxVJr8o= github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw= +github.com/metacubex/tls v0.1.0 h1:1kjR/1q2uU1cZIwiHYEnWzS4L+0Cu1/X3yfIQ76BzNY= +github.com/metacubex/tls v0.1.0/go.mod h1:0XeVdL0cBw+8i5Hqy3lVeP9IyD/LFTq02ExvHM6rzEM= github.com/metacubex/utls v1.8.3 h1:0m/yCxm3SK6kWve2lKiFb1pue1wHitJ8sQQD4Ikqde4= github.com/metacubex/utls v1.8.3/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko= github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f h1:FGBPRb1zUabhPhDrlKEjQ9lgIwQ6cHL4x8M9lrERhbk= @@ -153,9 +157,6 @@ github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd github.com/mroth/weightedrand/v2 v2.1.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU= github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4= github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:UqoUn6cHESlliMhOnKLWr+CBH+e3bazUPvFj1XZwAjs= -github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= -github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= -github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0= github.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo= github.com/openacid/low v0.1.21/go.mod h1:q+MsKI6Pz2xsCkzV4BLj7NR5M4EX0sGz5AqotpZDVh0= @@ -169,12 +170,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= -github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/saba-futai/sudoku v0.0.2-d h1:HW/gIyNUFcDchpMN+ZhluM86U/HGkWkkRV+9Km6WZM8= github.com/saba-futai/sudoku v0.0.2-d/go.mod h1:Rvggsoprp7HQM7bMIZUd1M27bPj8THRsZdY1dGbIAvo= -github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ= -github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw= @@ -244,7 +241,6 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -258,8 +254,8 @@ golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= -golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= +golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= diff --git a/clash-meta/hub/route/cache.go b/clash-meta/hub/route/cache.go index 78bda737be..f2eda53a87 100644 --- a/clash-meta/hub/route/cache.go +++ b/clash-meta/hub/route/cache.go @@ -1,12 +1,11 @@ package route import ( - "net/http" - "github.com/metacubex/mihomo/component/resolver" - "github.com/go-chi/chi/v5" - "github.com/go-chi/render" + "github.com/metacubex/chi" + "github.com/metacubex/chi/render" + "github.com/metacubex/http" ) func cacheRouter() http.Handler { diff --git a/clash-meta/hub/route/common.go b/clash-meta/hub/route/common.go index 6dd0b40c85..a0b9261da8 100644 --- a/clash-meta/hub/route/common.go +++ b/clash-meta/hub/route/common.go @@ -6,14 +6,15 @@ import ( "errors" "io" "net" - "net/http" "net/url" "strconv" "strings" "time" - "github.com/go-chi/chi/v5" N "github.com/metacubex/mihomo/common/net" + + "github.com/metacubex/chi" + "github.com/metacubex/http" ) // When name is composed of a partial escape string, Golang does not unescape it diff --git a/clash-meta/hub/route/configs.go b/clash-meta/hub/route/configs.go index 141a6c1559..af33074330 100644 --- a/clash-meta/hub/route/configs.go +++ b/clash-meta/hub/route/configs.go @@ -1,7 +1,6 @@ package route import ( - "net/http" "net/netip" "path/filepath" @@ -18,8 +17,9 @@ import ( "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/tunnel" - "github.com/go-chi/chi/v5" - "github.com/go-chi/render" + "github.com/metacubex/chi" + "github.com/metacubex/chi/render" + "github.com/metacubex/http" ) func configRouter() http.Handler { diff --git a/clash-meta/hub/route/connections.go b/clash-meta/hub/route/connections.go index 2ae1e885be..ddfa6c58f8 100644 --- a/clash-meta/hub/route/connections.go +++ b/clash-meta/hub/route/connections.go @@ -3,14 +3,14 @@ package route import ( "bytes" "encoding/json" - "net/http" "strconv" "time" "github.com/metacubex/mihomo/tunnel/statistic" - "github.com/go-chi/chi/v5" - "github.com/go-chi/render" + "github.com/metacubex/chi" + "github.com/metacubex/chi/render" + "github.com/metacubex/http" ) func connectionRouter() http.Handler { diff --git a/clash-meta/hub/route/dns.go b/clash-meta/hub/route/dns.go index 1762c94719..5a805a180a 100644 --- a/clash-meta/hub/route/dns.go +++ b/clash-meta/hub/route/dns.go @@ -3,12 +3,12 @@ package route import ( "context" "math" - "net/http" "github.com/metacubex/mihomo/component/resolver" - "github.com/go-chi/chi/v5" - "github.com/go-chi/render" + "github.com/metacubex/chi" + "github.com/metacubex/chi/render" + "github.com/metacubex/http" "github.com/miekg/dns" "github.com/samber/lo" ) diff --git a/clash-meta/hub/route/doh.go b/clash-meta/hub/route/doh.go index bb5416c436..3e0c30cff3 100644 --- a/clash-meta/hub/route/doh.go +++ b/clash-meta/hub/route/doh.go @@ -4,11 +4,11 @@ import ( "context" "encoding/base64" "io" - "net/http" "github.com/metacubex/mihomo/component/resolver" - "github.com/go-chi/render" + "github.com/metacubex/chi/render" + "github.com/metacubex/http" ) func dohRouter() http.Handler { diff --git a/clash-meta/hub/route/external.go b/clash-meta/hub/route/external.go index d2f0635822..2b1ce6a06c 100644 --- a/clash-meta/hub/route/external.go +++ b/clash-meta/hub/route/external.go @@ -1,6 +1,6 @@ package route -import "github.com/go-chi/chi/v5" +import "github.com/metacubex/chi" type externalRouter func(r chi.Router) diff --git a/clash-meta/hub/route/groups.go b/clash-meta/hub/route/groups.go index 05e8fe493e..5d5e5b817f 100644 --- a/clash-meta/hub/route/groups.go +++ b/clash-meta/hub/route/groups.go @@ -2,18 +2,18 @@ package route import ( "context" - "net/http" "strconv" "time" - "github.com/go-chi/chi/v5" - "github.com/go-chi/render" - "github.com/metacubex/mihomo/adapter/outboundgroup" "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/profile/cachefile" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/tunnel" + + "github.com/metacubex/chi" + "github.com/metacubex/chi/render" + "github.com/metacubex/http" ) func groupRouter() http.Handler { diff --git a/clash-meta/hub/route/provider.go b/clash-meta/hub/route/provider.go index 24b1989ed1..c060cc895d 100644 --- a/clash-meta/hub/route/provider.go +++ b/clash-meta/hub/route/provider.go @@ -2,14 +2,14 @@ package route import ( "context" - "net/http" C "github.com/metacubex/mihomo/constant" P "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/tunnel" - "github.com/go-chi/chi/v5" - "github.com/go-chi/render" + "github.com/metacubex/chi" + "github.com/metacubex/chi/render" + "github.com/metacubex/http" "github.com/samber/lo" ) diff --git a/clash-meta/hub/route/proxies.go b/clash-meta/hub/route/proxies.go index ba4e03f902..f100adbb7d 100644 --- a/clash-meta/hub/route/proxies.go +++ b/clash-meta/hub/route/proxies.go @@ -3,7 +3,6 @@ package route import ( "context" "fmt" - "net/http" "strconv" "time" @@ -13,8 +12,9 @@ import ( C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/tunnel" - "github.com/go-chi/chi/v5" - "github.com/go-chi/render" + "github.com/metacubex/chi" + "github.com/metacubex/chi/render" + "github.com/metacubex/http" ) var ( diff --git a/clash-meta/hub/route/restart.go b/clash-meta/hub/route/restart.go index 49d7e51703..89a95be940 100644 --- a/clash-meta/hub/route/restart.go +++ b/clash-meta/hub/route/restart.go @@ -2,7 +2,6 @@ package route import ( "fmt" - "net/http" "os" "os/exec" "runtime" @@ -11,8 +10,9 @@ import ( "github.com/metacubex/mihomo/hub/executor" "github.com/metacubex/mihomo/log" - "github.com/go-chi/chi/v5" - "github.com/go-chi/render" + "github.com/metacubex/chi" + "github.com/metacubex/chi/render" + "github.com/metacubex/http" ) func restartRouter() http.Handler { diff --git a/clash-meta/hub/route/rules.go b/clash-meta/hub/route/rules.go index 43d33299a2..e2d4d82ab4 100644 --- a/clash-meta/hub/route/rules.go +++ b/clash-meta/hub/route/rules.go @@ -2,12 +2,11 @@ package route import ( "github.com/metacubex/mihomo/constant" - "net/http" - "github.com/metacubex/mihomo/tunnel" - "github.com/go-chi/chi/v5" - "github.com/go-chi/render" + "github.com/metacubex/chi" + "github.com/metacubex/chi/render" + "github.com/metacubex/http" ) func ruleRouter() http.Handler { diff --git a/clash-meta/hub/route/server.go b/clash-meta/hub/route/server.go index 27c13f75b1..6c47d67294 100644 --- a/clash-meta/hub/route/server.go +++ b/clash-meta/hub/route/server.go @@ -5,7 +5,6 @@ import ( "crypto/subtle" "encoding/json" "net" - "net/http" "os" "path/filepath" "runtime/debug" @@ -17,16 +16,17 @@ import ( "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ech" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/ntp" "github.com/metacubex/mihomo/tunnel/statistic" - "github.com/go-chi/chi/v5" - "github.com/go-chi/chi/v5/middleware" - "github.com/go-chi/render" - "github.com/sagernet/cors" + "github.com/metacubex/chi" + "github.com/metacubex/chi/cors" + "github.com/metacubex/chi/middleware" + "github.com/metacubex/chi/render" + "github.com/metacubex/http" + "github.com/metacubex/tls" ) var ( @@ -204,16 +204,16 @@ func startTLS(cfg *Config) { } log.Infoln("RESTful API tls listening at: %s", l.Addr().String()) - tlsConfig := &tlsC.Config{Time: ntp.Now} + tlsConfig := &tls.Config{Time: ntp.Now} tlsConfig.NextProtos = []string{"h2", "http/1.1"} - tlsConfig.Certificates = []tlsC.Certificate{tlsC.UCertificate(cert)} - tlsConfig.ClientAuth = tlsC.ClientAuthTypeFromString(cfg.ClientAuthType) + tlsConfig.Certificates = []tls.Certificate{cert} + tlsConfig.ClientAuth = ca.ClientAuthTypeFromString(cfg.ClientAuthType) if len(cfg.ClientAuthCert) > 0 { - if tlsConfig.ClientAuth == tlsC.NoClientCert { - tlsConfig.ClientAuth = tlsC.RequireAndVerifyClientCert + if tlsConfig.ClientAuth == tls.NoClientCert { + tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert } } - if tlsConfig.ClientAuth == tlsC.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tlsC.RequireAndVerifyClientCert { + if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert { pool, err := ca.LoadCertificates(cfg.ClientAuthCert, C.Path) if err != nil { log.Errorln("External controller tls listen error: %s", err) @@ -233,7 +233,7 @@ func startTLS(cfg *Config) { Handler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer, cfg.Cors), } tlsServer = server - if err = server.Serve(tlsC.NewListenerForHttps(l, server, tlsConfig)); err != nil { + if err = server.Serve(tls.NewListener(l, tlsConfig)); err != nil { log.Errorln("External controller tls serve error: %s", err) } } diff --git a/clash-meta/hub/route/upgrade.go b/clash-meta/hub/route/upgrade.go index b2bcecd20a..b3593669a0 100644 --- a/clash-meta/hub/route/upgrade.go +++ b/clash-meta/hub/route/upgrade.go @@ -2,14 +2,14 @@ package route import ( "fmt" - "net/http" "os" "github.com/metacubex/mihomo/component/updater" "github.com/metacubex/mihomo/log" - "github.com/go-chi/chi/v5" - "github.com/go-chi/render" + "github.com/metacubex/chi" + "github.com/metacubex/chi/render" + "github.com/metacubex/http" ) func upgradeRouter() http.Handler { diff --git a/clash-meta/listener/anytls/server.go b/clash-meta/listener/anytls/server.go index 99f1d7bb56..731f13947e 100644 --- a/clash-meta/listener/anytls/server.go +++ b/clash-meta/listener/anytls/server.go @@ -13,7 +13,6 @@ import ( "github.com/metacubex/mihomo/common/buf" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ech" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/sing" @@ -24,13 +23,14 @@ import ( "github.com/metacubex/sing/common/auth" "github.com/metacubex/sing/common/bufio" M "github.com/metacubex/sing/common/metadata" + "github.com/metacubex/tls" ) type Listener struct { closed bool config LC.AnyTLSServer listeners []net.Listener - tlsConfig *tlsC.Config + tlsConfig *tls.Config userMap map[[32]byte]string padding atomic.Pointer[padding.PaddingFactory] } @@ -43,13 +43,13 @@ func New(config LC.AnyTLSServer, tunnel C.Tunnel, additions ...inbound.Addition) } } - tlsConfig := &tlsC.Config{Time: ntp.Now} + tlsConfig := &tls.Config{Time: ntp.Now} if config.Certificate != "" && config.PrivateKey != "" { cert, err := ca.LoadTLSKeyPair(config.Certificate, config.PrivateKey, C.Path) if err != nil { return nil, err } - tlsConfig.Certificates = []tlsC.Certificate{tlsC.UCertificate(cert)} + tlsConfig.Certificates = []tls.Certificate{cert} if config.EchKey != "" { err = ech.LoadECHKey(config.EchKey, tlsConfig, C.Path) @@ -58,13 +58,13 @@ func New(config LC.AnyTLSServer, tunnel C.Tunnel, additions ...inbound.Addition) } } } - tlsConfig.ClientAuth = tlsC.ClientAuthTypeFromString(config.ClientAuthType) + tlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType) if len(config.ClientAuthCert) > 0 { - if tlsConfig.ClientAuth == tlsC.NoClientCert { - tlsConfig.ClientAuth = tlsC.RequireAndVerifyClientCert + if tlsConfig.ClientAuth == tls.NoClientCert { + tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert } } - if tlsConfig.ClientAuth == tlsC.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tlsC.RequireAndVerifyClientCert { + if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert { pool, err := ca.LoadCertificates(config.ClientAuthCert, C.Path) if err != nil { return nil, err @@ -109,7 +109,7 @@ func New(config LC.AnyTLSServer, tunnel C.Tunnel, additions ...inbound.Addition) return nil, err } if len(tlsConfig.Certificates) > 0 { - l = tlsC.NewListener(l, tlsConfig) + l = tls.NewListener(l, tlsConfig) } else { return nil, errors.New("disallow using AnyTLS without certificates config") } diff --git a/clash-meta/listener/http/client.go b/clash-meta/listener/http/client.go index 0f084fca7b..9f5c37f8bd 100644 --- a/clash-meta/listener/http/client.go +++ b/clash-meta/listener/http/client.go @@ -4,13 +4,14 @@ import ( "context" "errors" "net" - "net/http" "time" "github.com/metacubex/mihomo/adapter/inbound" N "github.com/metacubex/mihomo/common/net" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/socks5" + + "github.com/metacubex/http" ) func newClient(srcConn net.Conn, tunnel C.Tunnel, additions []inbound.Addition) *http.Client { // additions using slice let caller can change its value (without size) after newClient return diff --git a/clash-meta/listener/http/hack.go b/clash-meta/listener/http/hack.go index c33eb6f1bc..ea54241ea6 100644 --- a/clash-meta/listener/http/hack.go +++ b/clash-meta/listener/http/hack.go @@ -2,9 +2,10 @@ package http import ( "bufio" - "net/http" _ "unsafe" + + "github.com/metacubex/http" ) -//go:linkname ReadRequest net/http.readRequest +//go:linkname ReadRequest github.com/metacubex/http.readRequest func ReadRequest(b *bufio.Reader) (req *http.Request, err error) diff --git a/clash-meta/listener/http/proxy.go b/clash-meta/listener/http/proxy.go index 5c08cd458a..588d0f6df5 100644 --- a/clash-meta/listener/http/proxy.go +++ b/clash-meta/listener/http/proxy.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "net" - "net/http" "strings" "sync" @@ -14,6 +13,8 @@ import ( "github.com/metacubex/mihomo/component/auth" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" + + "github.com/metacubex/http" ) type bodyWrapper struct { diff --git a/clash-meta/listener/http/server.go b/clash-meta/listener/http/server.go index 8b86ac6213..2aba6fda4d 100644 --- a/clash-meta/listener/http/server.go +++ b/clash-meta/listener/http/server.go @@ -7,12 +7,13 @@ import ( "github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ech" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" authStore "github.com/metacubex/mihomo/listener/auth" LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/reality" "github.com/metacubex/mihomo/ntp" + + "github.com/metacubex/tls" ) type Listener struct { @@ -66,7 +67,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A return nil, err } - tlsConfig := &tlsC.Config{Time: ntp.Now} + tlsConfig := &tls.Config{Time: ntp.Now} var realityBuilder *reality.Builder if config.Certificate != "" && config.PrivateKey != "" { @@ -74,7 +75,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A if err != nil { return nil, err } - tlsConfig.Certificates = []tlsC.Certificate{tlsC.UCertificate(cert)} + tlsConfig.Certificates = []tls.Certificate{cert} if config.EchKey != "" { err = ech.LoadECHKey(config.EchKey, tlsConfig, C.Path) @@ -83,13 +84,13 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A } } } - tlsConfig.ClientAuth = tlsC.ClientAuthTypeFromString(config.ClientAuthType) + tlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType) if len(config.ClientAuthCert) > 0 { - if tlsConfig.ClientAuth == tlsC.NoClientCert { - tlsConfig.ClientAuth = tlsC.RequireAndVerifyClientCert + if tlsConfig.ClientAuth == tls.NoClientCert { + tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert } } - if tlsConfig.ClientAuth == tlsC.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tlsC.RequireAndVerifyClientCert { + if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert { pool, err := ca.LoadCertificates(config.ClientAuthCert, C.Path) if err != nil { return nil, err @@ -100,7 +101,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A if tlsConfig.Certificates != nil { return nil, errors.New("certificate is unavailable in reality") } - if tlsConfig.ClientAuth != tlsC.NoClientCert { + if tlsConfig.ClientAuth != tls.NoClientCert { return nil, errors.New("client-auth is unavailable in reality") } realityBuilder, err = config.RealityConfig.Build(tunnel) @@ -112,7 +113,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A if realityBuilder != nil { l = realityBuilder.NewListener(l) } else if len(tlsConfig.Certificates) > 0 { - l = tlsC.NewListener(l, tlsConfig) + l = tls.NewListener(l, tlsConfig) } hl := &Listener{ diff --git a/clash-meta/listener/http/upgrade.go b/clash-meta/listener/http/upgrade.go index ac67ef6889..b658917925 100644 --- a/clash-meta/listener/http/upgrade.go +++ b/clash-meta/listener/http/upgrade.go @@ -2,15 +2,16 @@ package http import ( "context" - "crypto/tls" "net" - "net/http" "strings" "github.com/metacubex/mihomo/adapter/inbound" N "github.com/metacubex/mihomo/common/net" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/socks5" + + "github.com/metacubex/http" + "github.com/metacubex/tls" ) func isUpgradeRequest(req *http.Request) bool { diff --git a/clash-meta/listener/http/utils.go b/clash-meta/listener/http/utils.go index eb19283da2..4651ce5cda 100644 --- a/clash-meta/listener/http/utils.go +++ b/clash-meta/listener/http/utils.go @@ -4,9 +4,10 @@ import ( "encoding/base64" "errors" "net" - "net/http" "net/netip" "strings" + + "github.com/metacubex/http" ) // removeHopByHopHeaders remove Proxy-* headers diff --git a/clash-meta/listener/inbound/common_test.go b/clash-meta/listener/inbound/common_test.go index 4178035b27..b1be5e4d57 100644 --- a/clash-meta/listener/inbound/common_test.go +++ b/clash-meta/listener/inbound/common_test.go @@ -4,12 +4,10 @@ import ( "bytes" "context" "crypto/rand" - "crypto/tls" "encoding/base64" "fmt" "io" "net" - "net/http" "net/netip" "strconv" "sync" @@ -23,13 +21,13 @@ import ( "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/ech" "github.com/metacubex/mihomo/component/generator" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" - "github.com/go-chi/chi/v5" - "github.com/go-chi/render" + "github.com/metacubex/chi" + "github.com/metacubex/chi/render" + "github.com/metacubex/http" + "github.com/metacubex/tls" "github.com/stretchr/testify/assert" - "golang.org/x/net/http2" ) var httpPath = "/inbound_test" @@ -157,9 +155,9 @@ func NewHttpTestTunnel() *TestTunnel { io.Copy(io.Discard, r.Body) render.Data(w, r, httpData[:size]) }) - h2Server := &http2.Server{} + //h2Server := &http.Http2Server{} server := http.Server{Handler: r} - _ = http2.ConfigureServer(&server, h2Server) + //_ = http.Http2ConfigureServer(&server, h2Server) go server.Serve(ln) testFn := func(t *testing.T, proxy C.ProxyAdapter, proto string, size int) { req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s://%s%s?size=%d", proto, remoteAddr, httpPath, size), bytes.NewReader(httpData[:size])) @@ -216,6 +214,9 @@ func NewHttpTestTunnel() *TestTunnel { defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) + if proto == "https" { // ensure server using http2 + assert.Equal(t, 2, resp.ProtoMajor) + } data, err := io.ReadAll(resp.Body) if !assert.NoError(t, err) { @@ -268,7 +269,7 @@ func NewHttpTestTunnel() *TestTunnel { ch: make(chan struct{}), } if metadata.DstPort == 443 { - tlsConn := tlsC.Server(c, tlsC.UConfig(tlsConfig)) + tlsConn := tls.Server(c, tlsConfig) if metadata.Host == realityDest { // ignore the tls handshake error for realityDest if realityRealDial { rconn, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress()) @@ -284,11 +285,12 @@ func NewHttpTestTunnel() *TestTunnel { if err := tlsConn.HandshakeContext(ctx); err != nil { return } - if tlsConn.ConnectionState().NegotiatedProtocol == http2.NextProtoTLS { - h2Server.ServeConn(tlsConn, &http2.ServeConnOpts{BaseConfig: &server}) - } else { - ln.ch <- tlsConn - } + //if tlsConn.ConnectionState().NegotiatedProtocol == http.Http2NextProtoTLS { + // h2Server.ServeConn(tlsConn, &http.Http2ServeConnOpts{BaseConfig: &server}) + //} else { + // ln.ch <- tlsConn + //} + ln.ch <- tlsConn } else { ln.ch <- c } diff --git a/clash-meta/listener/mixed/mixed.go b/clash-meta/listener/mixed/mixed.go index 3df49d2c5a..995822b0cf 100644 --- a/clash-meta/listener/mixed/mixed.go +++ b/clash-meta/listener/mixed/mixed.go @@ -9,7 +9,6 @@ import ( "github.com/metacubex/mihomo/component/auth" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ech" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" authStore "github.com/metacubex/mihomo/listener/auth" LC "github.com/metacubex/mihomo/listener/config" @@ -19,6 +18,8 @@ import ( "github.com/metacubex/mihomo/ntp" "github.com/metacubex/mihomo/transport/socks4" "github.com/metacubex/mihomo/transport/socks5" + + "github.com/metacubex/tls" ) type Listener struct { @@ -62,7 +63,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A return nil, err } - tlsConfig := &tlsC.Config{Time: ntp.Now} + tlsConfig := &tls.Config{Time: ntp.Now} var realityBuilder *reality.Builder if config.Certificate != "" && config.PrivateKey != "" { @@ -70,7 +71,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A if err != nil { return nil, err } - tlsConfig.Certificates = []tlsC.Certificate{tlsC.UCertificate(cert)} + tlsConfig.Certificates = []tls.Certificate{cert} if config.EchKey != "" { err = ech.LoadECHKey(config.EchKey, tlsConfig, C.Path) @@ -79,13 +80,13 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A } } } - tlsConfig.ClientAuth = tlsC.ClientAuthTypeFromString(config.ClientAuthType) + tlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType) if len(config.ClientAuthCert) > 0 { - if tlsConfig.ClientAuth == tlsC.NoClientCert { - tlsConfig.ClientAuth = tlsC.RequireAndVerifyClientCert + if tlsConfig.ClientAuth == tls.NoClientCert { + tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert } } - if tlsConfig.ClientAuth == tlsC.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tlsC.RequireAndVerifyClientCert { + if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert { pool, err := ca.LoadCertificates(config.ClientAuthCert, C.Path) if err != nil { return nil, err @@ -96,7 +97,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A if tlsConfig.Certificates != nil { return nil, errors.New("certificate is unavailable in reality") } - if tlsConfig.ClientAuth != tlsC.NoClientCert { + if tlsConfig.ClientAuth != tls.NoClientCert { return nil, errors.New("client-auth is unavailable in reality") } realityBuilder, err = config.RealityConfig.Build(tunnel) @@ -108,7 +109,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A if realityBuilder != nil { l = realityBuilder.NewListener(l) } else if len(tlsConfig.Certificates) > 0 { - l = tlsC.NewListener(l, tlsConfig) + l = tls.NewListener(l, tlsConfig) } ml := &Listener{ diff --git a/clash-meta/listener/sing_hysteria2/server.go b/clash-meta/listener/sing_hysteria2/server.go index 2d172aab4f..becb06b115 100644 --- a/clash-meta/listener/sing_hysteria2/server.go +++ b/clash-meta/listener/sing_hysteria2/server.go @@ -5,8 +5,6 @@ import ( "errors" "fmt" "net" - "net/http" - "net/http/httputil" "net/url" "strings" @@ -15,17 +13,18 @@ import ( "github.com/metacubex/mihomo/common/sockopt" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ech" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/sing" "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/ntp" - "github.com/metacubex/sing-quic/hysteria2" - + "github.com/metacubex/http" + "github.com/metacubex/http/httputil" "github.com/metacubex/quic-go" + "github.com/metacubex/sing-quic/hysteria2" E "github.com/metacubex/sing/common/exceptions" + "github.com/metacubex/tls" ) type Listener struct { @@ -61,18 +60,18 @@ func New(config LC.Hysteria2Server, tunnel C.Tunnel, additions ...inbound.Additi if err != nil { return nil, err } - tlsConfig := &tlsC.Config{ + tlsConfig := &tls.Config{ Time: ntp.Now, - MinVersion: tlsC.VersionTLS13, + MinVersion: tls.VersionTLS13, } - tlsConfig.Certificates = []tlsC.Certificate{tlsC.UCertificate(cert)} - tlsConfig.ClientAuth = tlsC.ClientAuthTypeFromString(config.ClientAuthType) + tlsConfig.Certificates = []tls.Certificate{cert} + tlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType) if len(config.ClientAuthCert) > 0 { - if tlsConfig.ClientAuth == tlsC.NoClientCert { - tlsConfig.ClientAuth = tlsC.RequireAndVerifyClientCert + if tlsConfig.ClientAuth == tls.NoClientCert { + tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert } } - if tlsConfig.ClientAuth == tlsC.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tlsC.RequireAndVerifyClientCert { + if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert { pool, err := ca.LoadCertificates(config.ClientAuthCert, C.Path) if err != nil { return nil, err diff --git a/clash-meta/listener/sing_vless/server.go b/clash-meta/listener/sing_vless/server.go index a210d70a2d..049f5eb1d9 100644 --- a/clash-meta/listener/sing_vless/server.go +++ b/clash-meta/listener/sing_vless/server.go @@ -4,13 +4,11 @@ import ( "context" "errors" "net" - "net/http" "strings" "github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ech" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/reality" @@ -20,8 +18,10 @@ import ( "github.com/metacubex/mihomo/transport/vless/encryption" mihomoVMess "github.com/metacubex/mihomo/transport/vmess" + "github.com/metacubex/http" "github.com/metacubex/sing/common" "github.com/metacubex/sing/common/metadata" + "github.com/metacubex/tls" ) type Listener struct { @@ -76,7 +76,7 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition) }() } - tlsConfig := &tlsC.Config{Time: ntp.Now} + tlsConfig := &tls.Config{Time: ntp.Now} var realityBuilder *reality.Builder var httpServer http.Server @@ -85,7 +85,7 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition) if err != nil { return nil, err } - tlsConfig.Certificates = []tlsC.Certificate{tlsC.UCertificate(cert)} + tlsConfig.Certificates = []tls.Certificate{cert} if config.EchKey != "" { err = ech.LoadECHKey(config.EchKey, tlsConfig, C.Path) @@ -94,13 +94,13 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition) } } } - tlsConfig.ClientAuth = tlsC.ClientAuthTypeFromString(config.ClientAuthType) + tlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType) if len(config.ClientAuthCert) > 0 { - if tlsConfig.ClientAuth == tlsC.NoClientCert { - tlsConfig.ClientAuth = tlsC.RequireAndVerifyClientCert + if tlsConfig.ClientAuth == tls.NoClientCert { + tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert } } - if tlsConfig.ClientAuth == tlsC.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tlsC.RequireAndVerifyClientCert { + if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert { pool, err := ca.LoadCertificates(config.ClientAuthCert, C.Path) if err != nil { return nil, err @@ -111,7 +111,7 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition) if tlsConfig.Certificates != nil { return nil, errors.New("certificate is unavailable in reality") } - if tlsConfig.ClientAuth != tlsC.NoClientCert { + if tlsConfig.ClientAuth != tls.NoClientCert { return nil, errors.New("client-auth is unavailable in reality") } realityBuilder, err = config.RealityConfig.Build(tunnel) @@ -154,11 +154,7 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition) if realityBuilder != nil { l = realityBuilder.NewListener(l) } else if len(tlsConfig.Certificates) > 0 { - if httpServer.Handler != nil { - l = tlsC.NewListenerForHttps(l, &httpServer, tlsConfig) - } else { - l = tlsC.NewListener(l, tlsConfig) - } + l = tls.NewListener(l, tlsConfig) } else if sl.decryption == nil { return nil, errors.New("disallow using Vless without any certificates/reality/decryption config") } diff --git a/clash-meta/listener/sing_vmess/server.go b/clash-meta/listener/sing_vmess/server.go index 24c323baab..956aa70871 100644 --- a/clash-meta/listener/sing_vmess/server.go +++ b/clash-meta/listener/sing_vmess/server.go @@ -4,14 +4,12 @@ import ( "context" "errors" "net" - "net/http" "net/url" "strings" "github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ech" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/reality" @@ -20,9 +18,11 @@ import ( "github.com/metacubex/mihomo/transport/gun" mihomoVMess "github.com/metacubex/mihomo/transport/vmess" + "github.com/metacubex/http" vmess "github.com/metacubex/sing-vmess" "github.com/metacubex/sing/common" "github.com/metacubex/sing/common/metadata" + "github.com/metacubex/tls" ) type Listener struct { @@ -76,7 +76,7 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition) sl = &Listener{false, config, nil, service} - tlsConfig := &tlsC.Config{Time: ntp.Now} + tlsConfig := &tls.Config{Time: ntp.Now} var realityBuilder *reality.Builder var httpServer http.Server @@ -85,7 +85,7 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition) if err != nil { return nil, err } - tlsConfig.Certificates = []tlsC.Certificate{tlsC.UCertificate(cert)} + tlsConfig.Certificates = []tls.Certificate{cert} if config.EchKey != "" { err = ech.LoadECHKey(config.EchKey, tlsConfig, C.Path) @@ -94,13 +94,13 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition) } } } - tlsConfig.ClientAuth = tlsC.ClientAuthTypeFromString(config.ClientAuthType) + tlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType) if len(config.ClientAuthCert) > 0 { - if tlsConfig.ClientAuth == tlsC.NoClientCert { - tlsConfig.ClientAuth = tlsC.RequireAndVerifyClientCert + if tlsConfig.ClientAuth == tls.NoClientCert { + tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert } } - if tlsConfig.ClientAuth == tlsC.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tlsC.RequireAndVerifyClientCert { + if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert { pool, err := ca.LoadCertificates(config.ClientAuthCert, C.Path) if err != nil { return nil, err @@ -111,7 +111,7 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition) if tlsConfig.Certificates != nil { return nil, errors.New("certificate is unavailable in reality") } - if tlsConfig.ClientAuth != tlsC.NoClientCert { + if tlsConfig.ClientAuth != tls.NoClientCert { return nil, errors.New("client-auth is unavailable in reality") } realityBuilder, err = config.RealityConfig.Build(tunnel) @@ -154,11 +154,7 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition) if realityBuilder != nil { l = realityBuilder.NewListener(l) } else if len(tlsConfig.Certificates) > 0 { - if httpServer.Handler != nil { - l = tlsC.NewListenerForHttps(l, &httpServer, tlsConfig) - } else { - l = tlsC.NewListener(l, tlsConfig) - } + l = tls.NewListener(l, tlsConfig) } sl.listeners = append(sl.listeners, l) diff --git a/clash-meta/listener/socks/tcp.go b/clash-meta/listener/socks/tcp.go index 60eaa7411c..55e9e59439 100644 --- a/clash-meta/listener/socks/tcp.go +++ b/clash-meta/listener/socks/tcp.go @@ -10,7 +10,6 @@ import ( "github.com/metacubex/mihomo/component/auth" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ech" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" authStore "github.com/metacubex/mihomo/listener/auth" LC "github.com/metacubex/mihomo/listener/config" @@ -18,6 +17,8 @@ import ( "github.com/metacubex/mihomo/ntp" "github.com/metacubex/mihomo/transport/socks4" "github.com/metacubex/mihomo/transport/socks5" + + "github.com/metacubex/tls" ) type Listener struct { @@ -61,7 +62,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A return nil, err } - tlsConfig := &tlsC.Config{Time: ntp.Now} + tlsConfig := &tls.Config{Time: ntp.Now} var realityBuilder *reality.Builder if config.Certificate != "" && config.PrivateKey != "" { @@ -69,7 +70,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A if err != nil { return nil, err } - tlsConfig.Certificates = []tlsC.Certificate{tlsC.UCertificate(cert)} + tlsConfig.Certificates = []tls.Certificate{cert} if config.EchKey != "" { err = ech.LoadECHKey(config.EchKey, tlsConfig, C.Path) @@ -78,13 +79,13 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A } } } - tlsConfig.ClientAuth = tlsC.ClientAuthTypeFromString(config.ClientAuthType) + tlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType) if len(config.ClientAuthCert) > 0 { - if tlsConfig.ClientAuth == tlsC.NoClientCert { - tlsConfig.ClientAuth = tlsC.RequireAndVerifyClientCert + if tlsConfig.ClientAuth == tls.NoClientCert { + tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert } } - if tlsConfig.ClientAuth == tlsC.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tlsC.RequireAndVerifyClientCert { + if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert { pool, err := ca.LoadCertificates(config.ClientAuthCert, C.Path) if err != nil { return nil, err @@ -95,7 +96,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A if tlsConfig.Certificates != nil { return nil, errors.New("certificate is unavailable in reality") } - if tlsConfig.ClientAuth != tlsC.NoClientCert { + if tlsConfig.ClientAuth != tls.NoClientCert { return nil, errors.New("client-auth is unavailable in reality") } realityBuilder, err = config.RealityConfig.Build(tunnel) @@ -107,7 +108,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A if realityBuilder != nil { l = realityBuilder.NewListener(l) } else if len(tlsConfig.Certificates) > 0 { - l = tlsC.NewListener(l, tlsConfig) + l = tls.NewListener(l, tlsConfig) } sl := &Listener{ diff --git a/clash-meta/listener/trojan/server.go b/clash-meta/listener/trojan/server.go index 03fb02cfcb..6155d20927 100644 --- a/clash-meta/listener/trojan/server.go +++ b/clash-meta/listener/trojan/server.go @@ -4,13 +4,11 @@ import ( "errors" "io" "net" - "net/http" "strings" "github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ech" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/reality" @@ -22,7 +20,9 @@ import ( "github.com/metacubex/mihomo/transport/trojan" mihomoVMess "github.com/metacubex/mihomo/transport/vmess" + "github.com/metacubex/http" "github.com/metacubex/smux" + "github.com/metacubex/tls" ) type Listener struct { @@ -71,7 +71,7 @@ func New(config LC.TrojanServer, tunnel C.Tunnel, additions ...inbound.Addition) } sl = &Listener{false, config, nil, keys, pickCipher, h} - tlsConfig := &tlsC.Config{Time: ntp.Now} + tlsConfig := &tls.Config{Time: ntp.Now} var realityBuilder *reality.Builder var httpServer http.Server @@ -80,7 +80,7 @@ func New(config LC.TrojanServer, tunnel C.Tunnel, additions ...inbound.Addition) if err != nil { return nil, err } - tlsConfig.Certificates = []tlsC.Certificate{tlsC.UCertificate(cert)} + tlsConfig.Certificates = []tls.Certificate{cert} if config.EchKey != "" { err = ech.LoadECHKey(config.EchKey, tlsConfig, C.Path) @@ -89,13 +89,13 @@ func New(config LC.TrojanServer, tunnel C.Tunnel, additions ...inbound.Addition) } } } - tlsConfig.ClientAuth = tlsC.ClientAuthTypeFromString(config.ClientAuthType) + tlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType) if len(config.ClientAuthCert) > 0 { - if tlsConfig.ClientAuth == tlsC.NoClientCert { - tlsConfig.ClientAuth = tlsC.RequireAndVerifyClientCert + if tlsConfig.ClientAuth == tls.NoClientCert { + tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert } } - if tlsConfig.ClientAuth == tlsC.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tlsC.RequireAndVerifyClientCert { + if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert { pool, err := ca.LoadCertificates(config.ClientAuthCert, C.Path) if err != nil { return nil, err @@ -106,7 +106,7 @@ func New(config LC.TrojanServer, tunnel C.Tunnel, additions ...inbound.Addition) if tlsConfig.Certificates != nil { return nil, errors.New("certificate is unavailable in reality") } - if tlsConfig.ClientAuth != tlsC.NoClientCert { + if tlsConfig.ClientAuth != tls.NoClientCert { return nil, errors.New("client-auth is unavailable in reality") } realityBuilder, err = config.RealityConfig.Build(tunnel) @@ -149,11 +149,7 @@ func New(config LC.TrojanServer, tunnel C.Tunnel, additions ...inbound.Addition) if realityBuilder != nil { l = realityBuilder.NewListener(l) } else if len(tlsConfig.Certificates) > 0 { - if httpServer.Handler != nil { - l = tlsC.NewListenerForHttps(l, &httpServer, tlsConfig) - } else { - l = tlsC.NewListener(l, tlsConfig) - } + l = tls.NewListener(l, tlsConfig) } else if !config.TrojanSSOption.Enabled { return nil, errors.New("disallow using Trojan without both certificates/reality/ss config") } diff --git a/clash-meta/listener/tuic/server.go b/clash-meta/listener/tuic/server.go index f105c51ac3..30845515e7 100644 --- a/clash-meta/listener/tuic/server.go +++ b/clash-meta/listener/tuic/server.go @@ -9,7 +9,6 @@ import ( "github.com/metacubex/mihomo/common/sockopt" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ech" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/sing" @@ -20,6 +19,7 @@ import ( "github.com/gofrs/uuid/v5" "github.com/metacubex/quic-go" + "github.com/metacubex/tls" "golang.org/x/exp/slices" ) @@ -53,18 +53,18 @@ func New(config LC.TuicServer, tunnel C.Tunnel, additions ...inbound.Addition) ( if err != nil { return nil, err } - tlsConfig := &tlsC.Config{ + tlsConfig := &tls.Config{ Time: ntp.Now, - MinVersion: tlsC.VersionTLS13, + MinVersion: tls.VersionTLS13, } - tlsConfig.Certificates = []tlsC.Certificate{tlsC.UCertificate(cert)} - tlsConfig.ClientAuth = tlsC.ClientAuthTypeFromString(config.ClientAuthType) + tlsConfig.Certificates = []tls.Certificate{cert} + tlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType) if len(config.ClientAuthCert) > 0 { - if tlsConfig.ClientAuth == tlsC.NoClientCert { - tlsConfig.ClientAuth = tlsC.RequireAndVerifyClientCert + if tlsConfig.ClientAuth == tls.NoClientCert { + tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert } } - if tlsConfig.ClientAuth == tlsC.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tlsC.RequireAndVerifyClientCert { + if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert { pool, err := ca.LoadCertificates(config.ClientAuthCert, C.Path) if err != nil { return nil, err diff --git a/clash-meta/transport/gost-plugin/websocket.go b/clash-meta/transport/gost-plugin/websocket.go index fbe1ec32d9..c668c189b0 100644 --- a/clash-meta/transport/gost-plugin/websocket.go +++ b/clash-meta/transport/gost-plugin/websocket.go @@ -2,14 +2,15 @@ package gost import ( "context" - "crypto/tls" "net" - "net/http" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ech" "github.com/metacubex/mihomo/transport/vmess" - smux "github.com/metacubex/smux" + + "github.com/metacubex/http" + "github.com/metacubex/smux" + "github.com/metacubex/tls" ) // Option is options of gost websocket diff --git a/clash-meta/transport/gun/gun.go b/clash-meta/transport/gun/gun.go index 08598b2552..35639c12be 100644 --- a/clash-meta/transport/gun/gun.go +++ b/clash-meta/transport/gun/gun.go @@ -6,14 +6,11 @@ package gun import ( "bufio" "context" - "crypto/tls" "encoding/binary" "errors" "fmt" "io" "net" - "net/http" - "net/http/httptrace" "net/url" "sync" "time" @@ -24,7 +21,9 @@ import ( tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" - "golang.org/x/net/http2" + "github.com/metacubex/http" + "github.com/metacubex/http/httptrace" + "github.com/metacubex/tls" ) var ( @@ -260,35 +259,34 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, clientFingerprint stri } if clientFingerprint, ok := tlsC.GetFingerprint(clientFingerprint); ok { - tlsConfig := tlsC.UConfig(cfg) - err := echConfig.ClientHandle(ctx, tlsConfig) - if err != nil { - pconn.Close() - return nil, err - } - if realityConfig == nil { + tlsConfig := tlsC.UConfig(cfg) + err := echConfig.ClientHandleUTLS(ctx, tlsConfig) + if err != nil { + pconn.Close() + return nil, err + } tlsConn := tlsC.UClient(pconn, tlsConfig, clientFingerprint) if err := tlsConn.HandshakeContext(ctx); err != nil { pconn.Close() return nil, err } state := tlsConn.ConnectionState() - if p := state.NegotiatedProtocol; p != http2.NextProtoTLS { + if p := state.NegotiatedProtocol; p != http.Http2NextProtoTLS { tlsConn.Close() - return nil, fmt.Errorf("http2: unexpected ALPN protocol %s, want %s", p, http2.NextProtoTLS) + return nil, fmt.Errorf("http2: unexpected ALPN protocol %s, want %s", p, http.Http2NextProtoTLS) } return tlsConn, nil } else { - realityConn, err := tlsC.GetRealityConn(ctx, pconn, clientFingerprint, tlsConfig, realityConfig) + realityConn, err := tlsC.GetRealityConn(ctx, pconn, clientFingerprint, cfg.ServerName, realityConfig) if err != nil { pconn.Close() return nil, err } //state := realityConn.(*utls.UConn).ConnectionState() - //if p := state.NegotiatedProtocol; p != http2.NextProtoTLS { + //if p := state.NegotiatedProtocol; p != http.Http2NextProtoTLS { // realityConn.Close() - // return nil, fmt.Errorf("http2: unexpected ALPN protocol %s, want %s", p, http2.NextProtoTLS) + // return nil, fmt.Errorf("http2: unexpected ALPN protocol %s, want %s", p, http.Http2NextProtoTLS) //} return realityConn, nil } @@ -297,25 +295,10 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, clientFingerprint stri return nil, errors.New("REALITY is based on uTLS, please set a client-fingerprint") } - if echConfig != nil { - tlsConfig := tlsC.UConfig(cfg) - err := echConfig.ClientHandle(ctx, tlsConfig) - if err != nil { - pconn.Close() - return nil, err - } - - conn := tlsC.Client(pconn, tlsConfig) - if err := conn.HandshakeContext(ctx); err != nil { - pconn.Close() - return nil, err - } - state := conn.ConnectionState() - if p := state.NegotiatedProtocol; p != http2.NextProtoTLS { - conn.Close() - return nil, fmt.Errorf("http2: unexpected ALPN protocol %s, want %s", p, http2.NextProtoTLS) - } - return conn, nil + err = echConfig.ClientHandle(ctx, cfg) + if err != nil { + pconn.Close() + return nil, err } conn := tls.Client(pconn, cfg) @@ -324,14 +307,14 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, clientFingerprint stri return nil, err } state := conn.ConnectionState() - if p := state.NegotiatedProtocol; p != http2.NextProtoTLS { + if p := state.NegotiatedProtocol; p != http.Http2NextProtoTLS { conn.Close() - return nil, fmt.Errorf("http2: unexpected ALPN protocol %s, want %s", p, http2.NextProtoTLS) + return nil, fmt.Errorf("http2: unexpected ALPN protocol %s, want %s", p, http.Http2NextProtoTLS) } return conn, nil } - transport := &http2.Transport{ + transport := &http.Http2Transport{ DialTLSContext: dialFunc, TLSClientConfig: tlsConfig, AllowHTTP: false, @@ -341,9 +324,9 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, clientFingerprint stri ctx, cancel := context.WithCancel(context.Background()) wrap := &TransportWrap{ - Transport: transport, - ctx: ctx, - cancel: cancel, + Http2Transport: transport, + ctx: ctx, + cancel: cancel, } return wrap } diff --git a/clash-meta/transport/gun/server.go b/clash-meta/transport/gun/server.go index 953c487816..16605b4342 100644 --- a/clash-meta/transport/gun/server.go +++ b/clash-meta/transport/gun/server.go @@ -3,7 +3,6 @@ package gun import ( "io" "net" - "net/http" "strings" "sync" "time" @@ -12,8 +11,8 @@ import ( N "github.com/metacubex/mihomo/common/net" C "github.com/metacubex/mihomo/constant" - "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" + "github.com/metacubex/http" + "github.com/metacubex/http/h2c" ) const idleTimeout = 30 * time.Second @@ -72,7 +71,7 @@ func NewServerHandler(options ServerOption) http.Handler { } httpHandler.ServeHTTP(writer, request) - }), &http2.Server{ + }), &http.Http2Server{ IdleTimeout: idleTimeout, }) } diff --git a/clash-meta/transport/gun/transport.go b/clash-meta/transport/gun/transport.go index 9c5c437560..5925b35239 100644 --- a/clash-meta/transport/gun/transport.go +++ b/clash-meta/transport/gun/transport.go @@ -5,11 +5,11 @@ import ( "net" "sync" - "golang.org/x/net/http2" + "github.com/metacubex/http" ) type TransportWrap struct { - *http2.Transport + *http.Http2Transport ctx context.Context cancel context.CancelFunc closeOnce sync.Once @@ -18,7 +18,7 @@ type TransportWrap struct { func (tw *TransportWrap) Close() error { tw.closeOnce.Do(func() { tw.cancel() - closeTransport(tw.Transport) + closeTransport(tw.Http2Transport) }) return nil } diff --git a/clash-meta/transport/gun/transport_close.go b/clash-meta/transport/gun/transport_close.go index 2b30ed7cdd..b9c7613446 100644 --- a/clash-meta/transport/gun/transport_close.go +++ b/clash-meta/transport/gun/transport_close.go @@ -2,21 +2,20 @@ package gun import ( "net" - "net/http" "sync" "time" "unsafe" - "golang.org/x/net/http2" + "github.com/metacubex/http" ) type clientConnPool struct { - t *http2.Transport + t *http.Http2Transport mu sync.Mutex - conns map[string][]*http2.ClientConn // key is host:port - dialing map[string]unsafe.Pointer // currently in-flight dials - keys map[*http2.ClientConn][]string + conns map[string][]*http.Http2ClientConn // key is host:port + dialing map[string]unsafe.Pointer // currently in-flight dials + keys map[*http.Http2ClientConn][]string addConnCalls map[string]unsafe.Pointer // in-flight addConnIfNeeded calls } @@ -35,7 +34,7 @@ type tlsConn interface { NetConn() net.Conn } -func closeClientConn(cc *http2.ClientConn) { // like forceCloseConn() in http2.ClientConn but also apply for tls-like conn +func closeClientConn(cc *http.Http2ClientConn) { // like forceCloseConn() in http.Http2ClientConn but also apply for tls-like conn if conn, ok := (*clientConn)(unsafe.Pointer(cc)).tconn.(tlsConn); ok { t := time.AfterFunc(time.Second, func() { _ = conn.NetConn().Close() @@ -45,7 +44,7 @@ func closeClientConn(cc *http2.ClientConn) { // like forceCloseConn() in http2.C _ = cc.Close() } -func closeTransport(tr *http2.Transport) { +func closeTransport(tr *http.Http2Transport) { connPool := transportConnPool(tr) p := (*clientConnPool)((*efaceWords)(unsafe.Pointer(&connPool)).data) p.mu.Lock() @@ -56,9 +55,9 @@ func closeTransport(tr *http2.Transport) { } } // cleanup - p.conns = make(map[string][]*http2.ClientConn) - p.keys = make(map[*http2.ClientConn][]string) + p.conns = make(map[string][]*http.Http2ClientConn) + p.keys = make(map[*http.Http2ClientConn][]string) } -//go:linkname transportConnPool golang.org/x/net/http2.(*Transport).connPool -func transportConnPool(t *http2.Transport) http2.ClientConnPool +//go:linkname transportConnPool github.com/metacubex/http.(*http2Transport).connPool +func transportConnPool(t *http.Http2Transport) http.Http2ClientConnPool diff --git a/clash-meta/transport/hysteria/conns/faketcp/tcp_test.go b/clash-meta/transport/hysteria/conns/faketcp/tcp_test.go deleted file mode 100644 index 03f73afb8c..0000000000 --- a/clash-meta/transport/hysteria/conns/faketcp/tcp_test.go +++ /dev/null @@ -1,192 +0,0 @@ -//go:build linux -// +build linux - -package faketcp - -import ( - _ "net/http/pprof" -) - -//const testPortStream = "127.0.0.1:3456" -//const testPortPacket = "127.0.0.1:3457" - -const testPortStream = "127.0.0.1:3456" -const portServerPacket = "[::]:3457" -const portRemotePacket = "127.0.0.1:3457" - -//func init() { -// startTCPServer() -// startTCPRawServer() -// go func() { -// log.Println(http.ListenAndServe("0.0.0.0:6060", nil)) -// }() -//} -// -//func startTCPServer() net.Listener { -// l, err := net.Listen("tcp", testPortStream) -// if err != nil { -// log.Panicln(err) -// } -// -// go func() { -// defer l.Close() -// for { -// conn, err := l.Accept() -// if err != nil { -// log.Println(err) -// return -// } -// -// go handleRequest(conn) -// } -// }() -// return l -//} -// -//func startTCPRawServer() *TCPConn { -// conn, err := Listen("tcp", portServerPacket) -// if err != nil { -// log.Panicln(err) -// } -// err = conn.SetReadBuffer(1024 * 1024) -// if err != nil { -// log.Println(err) -// } -// err = conn.SetWriteBuffer(1024 * 1024) -// if err != nil { -// log.Println(err) -// } -// -// go func() { -// defer conn.Close() -// buf := make([]byte, 1024) -// for { -// n, addr, err := conn.ReadFrom(buf) -// if err != nil { -// log.Println("server readfrom:", err) -// return -// } -// //echo -// n, err = conn.WriteTo(buf[:n], addr) -// if err != nil { -// log.Println("server writeTo:", err) -// return -// } -// } -// }() -// return conn -//} -// -//func handleRequest(conn net.Conn) { -// defer conn.Close() -// -// for { -// buf := make([]byte, 1024) -// size, err := conn.Read(buf) -// if err != nil { -// log.Println("handleRequest:", err) -// return -// } -// data := buf[:size] -// conn.Write(data) -// } -//} -// -//func TestDialTCPStream(t *testing.T) { -// conn, err := Dial("tcp", testPortStream) -// if err != nil { -// t.Fatal(err) -// } -// defer conn.Close() -// -// addr, err := net.ResolveTCPAddr("tcp", testPortStream) -// if err != nil { -// t.Fatal(err) -// } -// -// n, err := conn.WriteTo([]byte("abc"), addr) -// if err != nil { -// t.Fatal(n, err) -// } -// -// buf := make([]byte, 1024) -// if n, addr, err := conn.ReadFrom(buf); err != nil { -// t.Fatal(n, addr, err) -// } else { -// log.Println(string(buf[:n]), "from:", addr) -// } -//} -// -//func TestDialToTCPPacket(t *testing.T) { -// conn, err := Dial("tcp", portRemotePacket) -// if err != nil { -// t.Fatal(err) -// } -// defer conn.Close() -// -// addr, err := net.ResolveTCPAddr("tcp", portRemotePacket) -// if err != nil { -// t.Fatal(err) -// } -// -// n, err := conn.WriteTo([]byte("abc"), addr) -// if err != nil { -// t.Fatal(n, err) -// } -// log.Println("written") -// -// buf := make([]byte, 1024) -// log.Println("readfrom buf") -// if n, addr, err := conn.ReadFrom(buf); err != nil { -// log.Println(err) -// t.Fatal(n, addr, err) -// } else { -// log.Println(string(buf[:n]), "from:", addr) -// } -// -// log.Println("complete") -//} -// -//func TestSettings(t *testing.T) { -// conn, err := Dial("tcp", portRemotePacket) -// if err != nil { -// t.Fatal(err) -// } -// defer conn.Close() -// if err := conn.SetDSCP(46); err != nil { -// log.Fatal("SetDSCP:", err) -// } -// if err := conn.SetReadBuffer(4096); err != nil { -// log.Fatal("SetReaderBuffer:", err) -// } -// if err := conn.SetWriteBuffer(4096); err != nil { -// log.Fatal("SetWriteBuffer:", err) -// } -//} -// -//func BenchmarkEcho(b *testing.B) { -// conn, err := Dial("tcp", portRemotePacket) -// if err != nil { -// b.Fatal(err) -// } -// defer conn.Close() -// -// addr, err := net.ResolveTCPAddr("tcp", portRemotePacket) -// if err != nil { -// b.Fatal(err) -// } -// -// buf := make([]byte, 1024) -// b.ReportAllocs() -// b.SetBytes(int64(len(buf))) -// for i := 0; i < b.N; i++ { -// n, err := conn.WriteTo(buf, addr) -// if err != nil { -// b.Fatal(n, err) -// } -// -// if n, addr, err := conn.ReadFrom(buf); err != nil { -// b.Fatal(n, addr, err) -// } -// } -//} diff --git a/clash-meta/transport/hysteria/core/client.go b/clash-meta/transport/hysteria/core/client.go index 13c8db7502..3e72db64d7 100644 --- a/clash-meta/transport/hysteria/core/client.go +++ b/clash-meta/transport/hysteria/core/client.go @@ -9,7 +9,6 @@ import ( "sync" "time" - tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/transport/hysteria/obfs" "github.com/metacubex/mihomo/transport/hysteria/pmtud_fix" "github.com/metacubex/mihomo/transport/hysteria/transport" @@ -18,6 +17,7 @@ import ( "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/congestion" "github.com/metacubex/randv2" + "github.com/metacubex/tls" ) var ( @@ -36,7 +36,7 @@ type Client struct { congestionFactory CongestionFactory obfuscator obfs.Obfuscator - tlsConfig *tlsC.Config + tlsConfig *tls.Config quicConfig *quic.Config quicSession *quic.Conn @@ -50,7 +50,7 @@ type Client struct { fastOpen bool } -func NewClient(serverAddr string, serverPorts string, protocol string, auth []byte, tlsConfig *tlsC.Config, quicConfig *quic.Config, +func NewClient(serverAddr string, serverPorts string, protocol string, auth []byte, tlsConfig *tls.Config, quicConfig *quic.Config, transport *transport.ClientTransport, sendBPS uint64, recvBPS uint64, congestionFactory CongestionFactory, obfuscator obfs.Obfuscator, hopInterval time.Duration, fastOpen bool) (*Client, error) { quicConfig.DisablePathMTUDiscovery = quicConfig.DisablePathMTUDiscovery || pmtud_fix.DisablePathMTUDiscovery diff --git a/clash-meta/transport/hysteria/transport/client.go b/clash-meta/transport/hysteria/transport/client.go index 67f826a320..f30ee58f6e 100644 --- a/clash-meta/transport/hysteria/transport/client.go +++ b/clash-meta/transport/hysteria/transport/client.go @@ -5,7 +5,6 @@ import ( "net" "time" - tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/transport/hysteria/conns/faketcp" "github.com/metacubex/mihomo/transport/hysteria/conns/udp" "github.com/metacubex/mihomo/transport/hysteria/conns/wechat" @@ -13,6 +12,7 @@ import ( "github.com/metacubex/mihomo/transport/hysteria/utils" "github.com/metacubex/quic-go" + "github.com/metacubex/tls" ) type ClientTransport struct{} @@ -62,7 +62,7 @@ func (ct *ClientTransport) quicPacketConn(proto string, rAddr net.Addr, serverPo } } -func (ct *ClientTransport) QUICDial(proto string, server string, serverPorts string, tlsConfig *tlsC.Config, quicConfig *quic.Config, obfs obfsPkg.Obfuscator, hopInterval time.Duration, dialer utils.PacketDialer) (*quic.Conn, error) { +func (ct *ClientTransport) QUICDial(proto string, server string, serverPorts string, tlsConfig *tls.Config, quicConfig *quic.Config, obfs obfsPkg.Obfuscator, hopInterval time.Duration, dialer utils.PacketDialer) (*quic.Conn, error) { serverUDPAddr, err := dialer.RemoteAddr(server) if err != nil { return nil, err diff --git a/clash-meta/transport/shadowtls/shadowtls.go b/clash-meta/transport/shadowtls/shadowtls.go index a0a3d7fb7e..adbe915ba7 100644 --- a/clash-meta/transport/shadowtls/shadowtls.go +++ b/clash-meta/transport/shadowtls/shadowtls.go @@ -4,7 +4,6 @@ import ( "context" "crypto/hmac" "crypto/sha1" - "crypto/tls" "encoding/binary" "fmt" "hash" @@ -13,6 +12,8 @@ import ( "github.com/metacubex/mihomo/common/pool" C "github.com/metacubex/mihomo/constant" + + "github.com/metacubex/tls" ) const ( diff --git a/clash-meta/transport/simple-obfs/http.go b/clash-meta/transport/simple-obfs/http.go index 9c3f8e0029..b5b31dccf6 100644 --- a/clash-meta/transport/simple-obfs/http.go +++ b/clash-meta/transport/simple-obfs/http.go @@ -7,10 +7,10 @@ import ( "fmt" "io" "net" - "net/http" "github.com/metacubex/mihomo/common/pool" + "github.com/metacubex/http" "github.com/metacubex/randv2" ) diff --git a/clash-meta/transport/sing-shadowtls/shadowtls.go b/clash-meta/transport/sing-shadowtls/shadowtls.go index 501e080f41..adf827e2be 100644 --- a/clash-meta/transport/sing-shadowtls/shadowtls.go +++ b/clash-meta/transport/sing-shadowtls/shadowtls.go @@ -2,7 +2,6 @@ package sing_shadowtls import ( "context" - "crypto/tls" "net" "github.com/metacubex/mihomo/component/ca" @@ -10,6 +9,7 @@ import ( "github.com/metacubex/mihomo/log" "github.com/metacubex/sing-shadowtls" + "github.com/metacubex/tls" "golang.org/x/exp/slices" ) diff --git a/clash-meta/transport/tuic/server.go b/clash-meta/transport/tuic/server.go index d9d6439ec1..152c9d1b1c 100644 --- a/clash-meta/transport/tuic/server.go +++ b/clash-meta/transport/tuic/server.go @@ -9,7 +9,6 @@ import ( "github.com/metacubex/mihomo/adapter/inbound" N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/utils" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/socks5" "github.com/metacubex/mihomo/transport/tuic/common" @@ -18,13 +17,14 @@ import ( "github.com/gofrs/uuid/v5" "github.com/metacubex/quic-go" + "github.com/metacubex/tls" ) type ServerOption struct { HandleTcpFn func(conn net.Conn, addr socks5.Addr, additions ...inbound.Addition) error HandleUdpFn func(addr socks5.Addr, packet C.UDPPacket, additions ...inbound.Addition) error - TlsConfig *tlsC.Config + TlsConfig *tls.Config QuicConfig *quic.Config Tokens [][32]byte // V4 special Users map[[16]byte]string // V5 special diff --git a/clash-meta/transport/tuic/v4/client.go b/clash-meta/transport/tuic/v4/client.go index 5184318825..b2affe9641 100644 --- a/clash-meta/transport/tuic/v4/client.go +++ b/clash-meta/transport/tuic/v4/client.go @@ -15,17 +15,17 @@ import ( N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/pool" "github.com/metacubex/mihomo/common/xsync" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/transport/tuic/common" "github.com/metacubex/quic-go" "github.com/metacubex/randv2" + "github.com/metacubex/tls" ) type ClientOption struct { - TlsConfig *tlsC.Config + TlsConfig *tls.Config QuicConfig *quic.Config Token [32]byte UdpRelayMode common.UdpRelayMode diff --git a/clash-meta/transport/tuic/v5/client.go b/clash-meta/transport/tuic/v5/client.go index 7cb7c22663..77e4f36948 100644 --- a/clash-meta/transport/tuic/v5/client.go +++ b/clash-meta/transport/tuic/v5/client.go @@ -15,17 +15,17 @@ import ( N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/pool" "github.com/metacubex/mihomo/common/xsync" - tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/transport/tuic/common" "github.com/metacubex/quic-go" "github.com/metacubex/randv2" + "github.com/metacubex/tls" ) type ClientOption struct { - TlsConfig *tlsC.Config + TlsConfig *tls.Config QuicConfig *quic.Config Uuid [16]byte Password string diff --git a/clash-meta/transport/v2ray-plugin/websocket.go b/clash-meta/transport/v2ray-plugin/websocket.go index b2e37926e9..67b7c3ea20 100644 --- a/clash-meta/transport/v2ray-plugin/websocket.go +++ b/clash-meta/transport/v2ray-plugin/websocket.go @@ -2,13 +2,14 @@ package obfs import ( "context" - "crypto/tls" "net" - "net/http" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ech" "github.com/metacubex/mihomo/transport/vmess" + + "github.com/metacubex/http" + "github.com/metacubex/tls" ) // Option is options of websocket obfs diff --git a/clash-meta/transport/vless/encryption/client.go b/clash-meta/transport/vless/encryption/client.go index bcfce08ed8..03769f9dfe 100644 --- a/clash-meta/transport/vless/encryption/client.go +++ b/clash-meta/transport/vless/encryption/client.go @@ -7,12 +7,23 @@ import ( "errors" "io" "net" + "runtime" "sync" "time" "github.com/metacubex/blake3" - utls "github.com/metacubex/utls" - "github.com/metacubex/utls/mlkem" + "github.com/metacubex/cpu" + "github.com/metacubex/mlkem" +) + +var ( + // Keep in sync with crypto/internal/fips140/aes/gcm.supportsAESGCM. + hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ && cpu.X86.HasSSE41 && cpu.X86.HasSSSE3 + hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL + hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCTR && cpu.S390X.HasGHASH + hasGCMAsmPPC64 = runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" + + HasAESGCMHardwareSupport = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X || hasGCMAsmPPC64 ) type ClientInstance struct { @@ -66,7 +77,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) { if i.NfsPKeys == nil { return nil, errors.New("uninitialized") } - c := NewCommonConn(conn, utls.HasAESGCMHardwareSupport()) + c := NewCommonConn(conn, HasAESGCMHardwareSupport) ivAndRealysLength := 16 + i.RelaysLength pfsKeyExchangeLength := 18 + 1184 + 32 + 16 diff --git a/clash-meta/transport/vless/encryption/client_test.go b/clash-meta/transport/vless/encryption/client_test.go index 793d01919e..c61069e0e9 100644 --- a/clash-meta/transport/vless/encryption/client_test.go +++ b/clash-meta/transport/vless/encryption/client_test.go @@ -4,17 +4,15 @@ import ( "fmt" "runtime" "testing" - - utls "github.com/metacubex/utls" ) func TestHasAESGCMHardwareSupport(t *testing.T) { - fmt.Println("HasAESGCMHardwareSupport:", utls.HasAESGCMHardwareSupport()) + fmt.Println("HasAESGCMHardwareSupport:", HasAESGCMHardwareSupport) if runtime.GOARCH == "arm64" && runtime.GOOS == "darwin" { // It should be supported starting from Apple Silicon M1 // https://github.com/golang/go/blob/go1.25.0/src/internal/cpu/cpu_arm64_darwin.go#L26-L30 - if !utls.HasAESGCMHardwareSupport() { + if !HasAESGCMHardwareSupport { t.Errorf("For ARM64 Darwin platforms (excluding iOS), AES GCM hardware acceleration should always be available.") } } diff --git a/clash-meta/transport/vless/encryption/key.go b/clash-meta/transport/vless/encryption/key.go index af9ac03379..98dd241c21 100644 --- a/clash-meta/transport/vless/encryption/key.go +++ b/clash-meta/transport/vless/encryption/key.go @@ -7,7 +7,7 @@ import ( "fmt" "github.com/metacubex/blake3" - "github.com/metacubex/utls/mlkem" + "github.com/metacubex/mlkem" ) const MLKEM768SeedLength = mlkem.SeedSize diff --git a/clash-meta/transport/vless/encryption/server.go b/clash-meta/transport/vless/encryption/server.go index 7118569888..7d0e86e9ef 100644 --- a/clash-meta/transport/vless/encryption/server.go +++ b/clash-meta/transport/vless/encryption/server.go @@ -13,7 +13,7 @@ import ( "time" "github.com/metacubex/blake3" - "github.com/metacubex/utls/mlkem" + "github.com/metacubex/mlkem" ) type ServerSession struct { diff --git a/clash-meta/transport/vless/vision/vision.go b/clash-meta/transport/vless/vision/vision.go index e9786981eb..a77fba105d 100644 --- a/clash-meta/transport/vless/vision/vision.go +++ b/clash-meta/transport/vless/vision/vision.go @@ -18,6 +18,7 @@ import ( "github.com/metacubex/mihomo/transport/vless/encryption" "github.com/gofrs/uuid/v5" + "github.com/metacubex/tls" ) var ErrNotHandshakeComplete = errors.New("tls connection not handshake complete") @@ -47,6 +48,13 @@ func NewConn(conn net.Conn, tlsConn net.Conn, userUUID uuid.UUID) (*Conn, error) t = reflect.TypeOf(underlying).Elem() p = unsafe.Pointer(underlying) break + case *tls.Conn: + //log.Debugln("type tls") + tlsConn = underlying + c.netConn = underlying.NetConn() + t = reflect.TypeOf(underlying).Elem() + p = unsafe.Pointer(underlying) + break case *tlsC.Conn: //log.Debugln("type *tlsC.Conn") tlsConn = underlying @@ -114,6 +122,14 @@ func checkTLSVersion(tlsConn net.Conn) error { if state.Version != gotls.VersionTLS13 { return ErrNotTLS13 } + case *tls.Conn: + state := underlying.ConnectionState() + if !state.HandshakeComplete { + return ErrNotHandshakeComplete + } + if state.Version != tls.VersionTLS13 { + return ErrNotTLS13 + } case *tlsC.Conn: state := underlying.ConnectionState() if !state.HandshakeComplete { diff --git a/clash-meta/transport/vmess/h2.go b/clash-meta/transport/vmess/h2.go index 5ad24f3d27..a7244b0991 100644 --- a/clash-meta/transport/vmess/h2.go +++ b/clash-meta/transport/vmess/h2.go @@ -4,18 +4,17 @@ import ( "context" "io" "net" - "net/http" "net/url" N "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/http" "github.com/metacubex/randv2" - "golang.org/x/net/http2" ) type h2Conn struct { net.Conn - *http2.ClientConn + *http.Http2ClientConn pwriter *io.PipeWriter res *http.Response cfg *H2Config @@ -50,7 +49,7 @@ func (hc *h2Conn) establishConn() error { } // it will be close at : `func (hc *h2Conn) Close() error` - res, err := hc.ClientConn.RoundTrip(&req) + res, err := hc.Http2ClientConn.RoundTrip(&req) if err != nil { return err } @@ -96,7 +95,7 @@ func (hc *h2Conn) Close() error { if hc.res != nil { ctx = hc.res.Request.Context() } - if err := hc.ClientConn.Shutdown(ctx); err != nil { + if err := hc.Http2ClientConn.Shutdown(ctx); err != nil { return err } return hc.Conn.Close() @@ -108,7 +107,7 @@ func StreamH2Conn(ctx context.Context, conn net.Conn, cfg *H2Config) (_ net.Conn defer done(&err) } - transport := &http2.Transport{} + transport := &http.Http2Transport{} cconn, err := transport.NewClientConn(conn) if err != nil { @@ -116,8 +115,8 @@ func StreamH2Conn(ctx context.Context, conn net.Conn, cfg *H2Config) (_ net.Conn } return &h2Conn{ - Conn: conn, - ClientConn: cconn, - cfg: cfg, + Conn: conn, + Http2ClientConn: cconn, + cfg: cfg, }, nil } diff --git a/clash-meta/transport/vmess/http.go b/clash-meta/transport/vmess/http.go index 94f1fbd351..77ac2ebebb 100644 --- a/clash-meta/transport/vmess/http.go +++ b/clash-meta/transport/vmess/http.go @@ -5,11 +5,11 @@ import ( "bytes" "fmt" "net" - "net/http" "net/textproto" "github.com/metacubex/mihomo/common/utils" + "github.com/metacubex/http" "github.com/metacubex/randv2" ) diff --git a/clash-meta/transport/vmess/tls.go b/clash-meta/transport/vmess/tls.go index 7239ebd238..4c4c50ec08 100644 --- a/clash-meta/transport/vmess/tls.go +++ b/clash-meta/transport/vmess/tls.go @@ -2,13 +2,14 @@ package vmess import ( "context" - "crypto/tls" "errors" "net" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ech" tlsC "github.com/metacubex/mihomo/component/tls" + + "github.com/metacubex/tls" ) type TLSConfig struct { @@ -43,13 +44,12 @@ func StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn } if clientFingerprint, ok := tlsC.GetFingerprint(cfg.ClientFingerprint); ok { - tlsConfig := tlsC.UConfig(tlsConfig) - err = cfg.ECH.ClientHandle(ctx, tlsConfig) - if err != nil { - return nil, err - } - if cfg.Reality == nil { + tlsConfig := tlsC.UConfig(tlsConfig) + err = cfg.ECH.ClientHandleUTLS(ctx, tlsConfig) + if err != nil { + return nil, err + } tlsConn := tlsC.UClient(conn, tlsConfig, clientFingerprint) err = tlsConn.HandshakeContext(ctx) if err != nil { @@ -57,24 +57,16 @@ func StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn } return tlsConn, nil } else { - return tlsC.GetRealityConn(ctx, conn, clientFingerprint, tlsConfig, cfg.Reality) + return tlsC.GetRealityConn(ctx, conn, clientFingerprint, tlsConfig.ServerName, cfg.Reality) } } if cfg.Reality != nil { return nil, errors.New("REALITY is based on uTLS, please set a client-fingerprint") } - if cfg.ECH != nil { - tlsConfig := tlsC.UConfig(tlsConfig) - err = cfg.ECH.ClientHandle(ctx, tlsConfig) - if err != nil { - return nil, err - } - - tlsConn := tlsC.Client(conn, tlsConfig) - - err = tlsConn.HandshakeContext(ctx) - return tlsConn, err + err = cfg.ECH.ClientHandle(ctx, tlsConfig) + if err != nil { + return nil, err } tlsConn := tls.Client(conn, tlsConfig) diff --git a/clash-meta/transport/vmess/websocket.go b/clash-meta/transport/vmess/websocket.go index 73b743dab7..0ee1b3ab2a 100644 --- a/clash-meta/transport/vmess/websocket.go +++ b/clash-meta/transport/vmess/websocket.go @@ -5,14 +5,12 @@ import ( "bytes" "context" "crypto/rand" - "crypto/tls" "encoding/base64" "encoding/binary" "errors" "fmt" "io" "net" - "net/http" "net/url" "strconv" "strings" @@ -26,7 +24,9 @@ import ( "github.com/gobwas/ws" "github.com/gobwas/ws/wsutil" + "github.com/metacubex/http" "github.com/metacubex/randv2" + "github.com/metacubex/tls" ) type websocketConn struct { @@ -357,7 +357,7 @@ func streamWebsocketConn(ctx context.Context, conn net.Conn, c *WebsocketConfig, if clientFingerprint, ok := tlsC.GetFingerprint(c.ClientFingerprint); ok { tlsConfig := tlsC.UConfig(config) - err = c.ECHConfig.ClientHandle(ctx, tlsConfig) + err = c.ECHConfig.ClientHandleUTLS(ctx, tlsConfig) if err != nil { return nil, err } @@ -370,17 +370,11 @@ func streamWebsocketConn(ctx context.Context, conn net.Conn, c *WebsocketConfig, return nil, err } conn = tlsConn - } else if c.ECHConfig != nil { - tlsConfig := tlsC.UConfig(config) - err = c.ECHConfig.ClientHandle(ctx, tlsConfig) + } else { + err = c.ECHConfig.ClientHandle(ctx, config) if err != nil { return nil, err } - tlsConn := tlsC.Client(conn, tlsConfig) - - err = tlsConn.HandshakeContext(ctx) - conn = tlsConn - } else { tlsConn := tls.Client(conn, config) err = tlsConn.HandshakeContext(ctx) if err != nil { diff --git a/clash-nyanpasu/eslint.config.js b/clash-nyanpasu/eslint.config.js index 784c33456a..d4e11b48cd 100644 --- a/clash-nyanpasu/eslint.config.js +++ b/clash-nyanpasu/eslint.config.js @@ -40,12 +40,8 @@ export default tseslint.config( }, { files: ['**/*.{jsx,mjsx,tsx,mtsx}'], - extends: [ - // @ts-expect-error fucking plugin why export flat config with nullable types? - react.configs.flat.recommended, - ], + extends: [react.configs.flat.recommended], plugins: { - // @ts-expect-error react hooks not compatible with eslint types 'react-hooks': pluginReactHooks, 'react-compiler': pluginReactCompiler, }, @@ -92,7 +88,6 @@ export default tseslint.config( languageOptions: { parserOptions: { project: true, - projectService: true, tsconfigRootDir: import.meta.dirname, }, }, diff --git a/clash-nyanpasu/frontend/interface/src/ipc/use-clash-proxies.ts b/clash-nyanpasu/frontend/interface/src/ipc/use-clash-proxies.ts index 95720e8a92..7ade615471 100644 --- a/clash-nyanpasu/frontend/interface/src/ipc/use-clash-proxies.ts +++ b/clash-nyanpasu/frontend/interface/src/ipc/use-clash-proxies.ts @@ -15,14 +15,12 @@ export type ClashProxiesQueryHelperFn = { } export interface ClashProxiesQueryProxyItem - extends ProxyItem, - ClashProxiesQueryHelperFn { + extends ProxyItem, ClashProxiesQueryHelperFn { mutateSelect: () => Promise } export interface ClashProxiesQueryGroupItem - extends ProxyGroupItem, - ClashProxiesQueryHelperFn { + extends ProxyGroupItem, ClashProxiesQueryHelperFn { all: ClashProxiesQueryProxyItem[] } diff --git a/clash-nyanpasu/frontend/nyanpasu/package.json b/clash-nyanpasu/frontend/nyanpasu/package.json index 3155f2611d..233e4ea670 100644 --- a/clash-nyanpasu/frontend/nyanpasu/package.json +++ b/clash-nyanpasu/frontend/nyanpasu/package.json @@ -22,12 +22,16 @@ "@mui/x-date-pickers": "8.17.0", "@nyanpasu/interface": "workspace:^", "@nyanpasu/ui": "workspace:^", + "@radix-ui/react-scroll-area": "1.2.10", + "@radix-ui/react-slot": "1.2.4", "@tailwindcss/postcss": "4.1.17", "@tanstack/router-zod-adapter": "1.81.5", "@tauri-apps/api": "2.8.0", "@types/json-schema": "7.0.15", + "@uidotdev/usehooks": "2.4.1", "ahooks": "3.9.6", "allotment": "1.20.4", + "class-variance-authority": "0.7.1", "country-code-emoji": "2.3.0", "country-emoji": "1.5.6", "dayjs": "1.11.19", @@ -56,7 +60,7 @@ "@csstools/normalize.css": "12.1.1", "@emotion/babel-plugin": "11.13.5", "@emotion/react": "11.14.0", - "@iconify/json": "2.2.411", + "@iconify/json": "2.2.419", "@monaco-editor/react": "4.7.0", "@tanstack/react-query": "5.90.11", "@tanstack/react-router": "1.134.15", diff --git a/clash-nyanpasu/frontend/nyanpasu/src/assets/styles/index.scss b/clash-nyanpasu/frontend/nyanpasu/src/assets/styles/index.scss index ba8f1d2e97..b66a402cc0 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/assets/styles/index.scss +++ b/clash-nyanpasu/frontend/nyanpasu/src/assets/styles/index.scss @@ -1,4 +1,5 @@ @use './fonts.scss'; +@use './theme.scss'; body { margin: 0; @@ -58,3 +59,7 @@ body { .user-none { user-select: none; } + +.bg-inherit-allow-fallback { + background-color: var(--fallback-bg, inherit); +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/assets/styles/tailwind.css b/clash-nyanpasu/frontend/nyanpasu/src/assets/styles/tailwind.css index e856fb9efd..71333853fa 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/assets/styles/tailwind.css +++ b/clash-nyanpasu/frontend/nyanpasu/src/assets/styles/tailwind.css @@ -24,9 +24,116 @@ --custom-text-shadow-xl: 1px 1px 3px rgb(0 0 0 / 29%), 2px 4px 7px rgb(73 64 125 / 35%); --custom-text-shadow-none: none; + + /* Material Design 3 Color System */ + --color-primary: var(--color-md-primary); + --color-on-primary: var(--color-md-on-primary); + --color-primary-container: var(--color-md-primary-container); + --color-on-primary-container: var(--color-md-on-primary-container); + --color-secondary: var(--color-md-secondary); + --color-on-secondary: var(--color-md-on-secondary); + --color-secondary-container: var(--color-md-secondary-container); + --color-on-secondary-container: var(--color-md-on-secondary-container); + --color-tertiary: var(--color-md-tertiary); + --color-on-tertiary: var(--color-md-on-tertiary); + --color-tertiary-container: var(--color-md-tertiary-container); + --color-on-tertiary-container: var(--color-md-on-tertiary-container); + --color-error: var(--color-md-error); + --color-on-error: var(--color-md-on-error); + --color-error-container: var(--color-md-error-container); + --color-on-error-container: var(--color-md-on-error-container); + --color-background: var(--color-md-background); + --color-on-background: var(--color-md-on-background); + --color-surface: var(--color-md-surface); + --color-on-surface: var(--color-md-on-surface); + --color-surface-variant: var(--color-md-surface-variant); + --color-on-surface-variant: var(--color-md-on-surface-variant); + --color-outline: var(--color-md-outline); + --color-outline-variant: var(--color-md-outline-variant); + --color-shadow: var(--color-md-shadow); + --color-scrim: var(--color-md-scrim); + --color-inverse-surface: var(--color-md-inverse-surface); + --color-inverse-on-surface: var(--color-md-inverse-on-surface); + --color-inverse-primary: var(--color-md-inverse-primary); + + /* Progress Spin Animation */ + --animate-progress-spin: progress-spin 5332ms cubic-bezier(0.4, 0, 0.2, 1) + infinite both; + --animate-progress-spin-left: progress-spin-left 1333ms + cubic-bezier(0.4, 0, 0.2, 1) infinite both; + --animate-progress-spin-right: progress-spin-right 1333ms + cubic-bezier(0.4, 0, 0.2, 1) infinite both; } @utility text-shadow-* { /* prettier-ignore */ text-shadow: --value(--custom-text-shadow-*); } + +@utility bg-transparent-fallback-* { + background-color: transparent; + + --fallback-bg: --value(--color-*); +} + +@keyframes progress-spin { + 12.5% { + transform: rotate(135deg); + } + + 25% { + transform: rotate(270deg); + } + + 37.5% { + transform: rotate(405deg); + } + + 50% { + transform: rotate(540deg); + } + + 62.5% { + transform: rotate(675deg); + } + + 75% { + transform: rotate(810deg); + } + + 87.5% { + transform: rotate(945deg); + } + + 100% { + transform: rotate(1080deg); + } +} + +@keyframes progress-spin-left { + 0% { + transform: rotate(265deg); + } + + 50% { + transform: rotate(130deg); + } + + 100% { + transform: rotate(265deg); + } +} + +@keyframes progress-spin-right { + 0% { + transform: rotate(-265deg); + } + + 50% { + transform: rotate(-130deg); + } + + 100% { + transform: rotate(-265deg); + } +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/assets/styles/theme.scss b/clash-nyanpasu/frontend/nyanpasu/src/assets/styles/theme.scss new file mode 100644 index 0000000000..335f0af9c2 --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/assets/styles/theme.scss @@ -0,0 +1,67 @@ +// default theme, generated by material-color-utilities +// frontend/nyanpasu/src/components/providers/theme-provider.tsx + +// this is fallback theme, if custom theme not set +:root { + --color-md-primary: #005db5; + --color-md-on-primary: #fff; + --color-md-primary-container: #d6e3ff; + --color-md-on-primary-container: #001b3d; + --color-md-secondary: #555f71; + --color-md-on-secondary: #fff; + --color-md-secondary-container: #d9e3f8; + --color-md-on-secondary-container: #121c2b; + --color-md-tertiary: #6f5675; + --color-md-on-tertiary: #fff; + --color-md-tertiary-container: #f9d8fe; + --color-md-on-tertiary-container: #28132f; + --color-md-error: #ba1a1a; + --color-md-on-error: #fff; + --color-md-error-container: #ffdad6; + --color-md-on-error-container: #410002; + --color-md-background: #fdfbff; + --color-md-on-background: #1a1b1e; + --color-md-surface: #fdfbff; + --color-md-on-surface: #1a1b1e; + --color-md-surface-variant: #e0e2ec; + --color-md-on-surface-variant: #43474e; + --color-md-outline: #74777f; + --color-md-outline-variant: #c4c6cf; + --color-md-shadow: #000; + --color-md-scrim: #000; + --color-md-inverse-surface: #2f3033; + --color-md-inverse-on-surface: #f1f0f4; + --color-md-inverse-primary: #a8c8ff; +} + +:root.dark { + --color-md-primary: #a8c8ff; + --color-md-on-primary: #003062; + --color-md-primary-container: #00468b; + --color-md-on-primary-container: #d6e3ff; + --color-md-secondary: #bdc7dc; + --color-md-on-secondary: #273141; + --color-md-secondary-container: #3e4758; + --color-md-on-secondary-container: #d9e3f8; + --color-md-tertiary: #dcbce1; + --color-md-on-tertiary: #3e2845; + --color-md-tertiary-container: #563e5c; + --color-md-on-tertiary-container: #f9d8fe; + --color-md-error: #ffb4ab; + --color-md-on-error: #690005; + --color-md-error-container: #93000a; + --color-md-on-error-container: #ffb4ab; + --color-md-background: #1a1b1e; + --color-md-on-background: #e3e2e6; + --color-md-surface: #1a1b1e; + --color-md-on-surface: #e3e2e6; + --color-md-surface-variant: #43474e; + --color-md-on-surface-variant: #c4c6cf; + --color-md-outline: #8e9099; + --color-md-outline-variant: #43474e; + --color-md-shadow: #000; + --color-md-scrim: #000; + --color-md-inverse-surface: #e3e2e6; + --color-md-inverse-on-surface: #2f3033; + --color-md-inverse-primary: #005db5; +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/components/providers/theme-provider.tsx b/clash-nyanpasu/frontend/nyanpasu/src/components/providers/theme-provider.tsx new file mode 100644 index 0000000000..aa08f80436 --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/components/providers/theme-provider.tsx @@ -0,0 +1,136 @@ +import { isEqual, kebabCase } from 'lodash-es' +import { + createContext, + PropsWithChildren, + useCallback, + useContext, + useEffect, +} from 'react' +import { insertStyle } from '@/utils/styled' +import { + argbFromHex, + hexFromArgb, + Theme, + themeFromSourceColor, +} from '@material/material-color-utilities' +import { useSetting } from '@nyanpasu/interface' +import { useLocalStorage } from '@uidotdev/usehooks' + +export const DEFAULT_COLOR = '#1867C0' + +const CUSTOM_THEME_KEY = 'custom-theme' as const + +const THEME_PALETTE_KEY = 'theme-palette-v1' as const +const THEME_CSS_VARS_KEY = 'theme-css-vars-v1' as const + +const generateThemeCssVars = ({ schemes }: Theme) => { + let lightCssVars = ':root{' + let darkCssVars = ':root.dark{' + + Object.entries(schemes).forEach(([mode, scheme]) => { + let inputScheme + + // Safely convert scheme to JSON if possible, otherwise use as-is + if (typeof scheme.toJSON === 'function') { + inputScheme = scheme.toJSON() + } else { + inputScheme = scheme + } + + Object.entries(inputScheme).forEach(([key, value]) => { + if (mode === 'light') { + lightCssVars += `--color-md-${kebabCase(key)}: ${hexFromArgb(value)};` + } else { + darkCssVars += `--color-md-${kebabCase(key)}: ${hexFromArgb(value)};` + } + }) + }) + + lightCssVars += '}' + darkCssVars += '}' + + return lightCssVars + darkCssVars +} + +const ThemeContext = createContext<{ + themePalette: Theme + themeCssVars: string + themeColor: string + setTheme: (color: string) => void +} | null>(null) + +export function useExperimentalThemeContext() { + const context = useContext(ThemeContext) + + if (!context) { + throw new Error( + 'useExperimentalThemeContext must be used within a ExperimentalThemeProvider', + ) + } + + return context +} + +export function ExperimentalThemeProvider({ children }: PropsWithChildren) { + const { value: themeColor } = useSetting('theme_color') + + const [cachedThemePalette, setCachedThemePalette] = useLocalStorage( + THEME_PALETTE_KEY, + themeFromSourceColor( + // use default color if theme color is not set + argbFromHex(themeColor || DEFAULT_COLOR), + ), + ) + + const [cachedThemeCssVars, setCachedThemeCssVars] = useLocalStorage( + THEME_CSS_VARS_KEY, + // initialize theme css vars from cached theme palette + generateThemeCssVars(cachedThemePalette), + ) + + // automatically insert custom theme css vars into document head + useEffect(() => { + insertStyle(CUSTOM_THEME_KEY, cachedThemeCssVars) + }, [cachedThemeCssVars]) + + const setTheme = useCallback( + (color: string) => { + if (color === themeColor) { + return + } + + const materialColor = themeFromSourceColor( + // use default color if theme color is not set + argbFromHex(color || DEFAULT_COLOR), + ) + + if (isEqual(materialColor, cachedThemePalette)) { + return + } else { + setCachedThemePalette(materialColor) + } + + const themeCssVars = generateThemeCssVars(materialColor) + setCachedThemeCssVars(themeCssVars) + }, + [ + themeColor, + cachedThemePalette, + setCachedThemeCssVars, + setCachedThemePalette, + ], + ) + + return ( + + {children} + + ) +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/components/ui/button.tsx b/clash-nyanpasu/frontend/nyanpasu/src/components/ui/button.tsx new file mode 100644 index 0000000000..7945946126 --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/components/ui/button.tsx @@ -0,0 +1,136 @@ +import { cva, type VariantProps } from 'class-variance-authority' +import { AnimatePresence, motion } from 'framer-motion' +import { lazy, Suspense, useCallback } from 'react' +import { chains } from '@/utils/chain' +import { cn } from '@nyanpasu/ui' +import { Slot } from '@radix-ui/react-slot' +import { CircularProgress } from './progress' +import { useRipple } from './ripple' + +export const buttonVariants = cva( + [ + 'cursor-pointer select-none', + 'focus:outline-hidden', + 'relative overflow-hidden', + 'h-10 text-sm font-medium', + 'rounded-full', + 'transition-[background-color,color,shadow]', + ], + { + variants: { + variant: { + basic: [ + 'px-4', + 'text-primary dark:text-primary', + 'bg-transparent-fallback-surface dark:bg-transparent-fallback-on-surface', + 'hover:bg-primary-container dark:hover:bg-surface-variant', + ], + }, + disabled: { + true: 'cursor-not-allowed shadow-none hover:shadow-none focus:shadow-none', + false: '', + }, + icon: { + true: 'p-0 grid place-content-center', + false: 'min-w-16', + }, + }, + compoundVariants: [ + { + icon: true, + className: 'w-10', + }, + ], + defaultVariants: { + variant: 'basic', + disabled: false, + icon: false, + }, + }, +) + +export type ButtonVariantsProps = VariantProps + +const LazyRipple = lazy(() => + import('./ripple').then((mod) => ({ default: mod.Ripple })), +) + +export interface ButtonProps + extends + Omit, 'disabled'>, + ButtonVariantsProps { + asChild?: boolean + loading?: boolean +} + +export const Button = ({ + loading, + asChild, + variant, + disabled, + icon, + className, + children, + onClick, + ...props +}: ButtonProps) => { + const Comp = asChild ? Slot : 'button' + + const ripple = useRipple() + + const handleClick = disabled ? undefined : chains(onClick, ripple.onClick) + + const handleClear = useCallback( + (key: React.Key) => { + ripple.onClear(key) + }, + [ripple], + ) + + return ( + + {asChild ? ( + children + ) : ( + <> + {children} + + + {loading && ( + + + + )} + + + + {ripple && !loading && !disabled && ( + + )} + + + )} + + ) +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/components/ui/circle.tsx b/clash-nyanpasu/frontend/nyanpasu/src/components/ui/circle.tsx new file mode 100644 index 0000000000..da2381e4a5 --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/components/ui/circle.tsx @@ -0,0 +1,55 @@ +import { ComponentProps } from 'react' +import { cn } from '@nyanpasu/ui' + +const BASE_STROKE_WIDTH = 10 +const BASE_SIZE = 100 + +const getCircleRefence = (value: number) => { + const radius = (BASE_SIZE - BASE_STROKE_WIDTH) / 2 + const strokeDasharray = 2 * Math.PI * radius + const strokeDashoffset = (strokeDasharray * (100 - value)) / 100 + + return { + radius, + strokeDasharray, + strokeDashoffset, + } +} + +export function Circle({ + value, + className, + style, + ...props +}: ComponentProps<'circle'> & { + value: number +}) { + const { strokeDasharray, strokeDashoffset } = getCircleRefence(value) + + return ( + + ) +} + +export function CircleSVG({ className, ...props }: ComponentProps<'svg'>) { + return ( + + ) +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/components/ui/progress.tsx b/clash-nyanpasu/frontend/nyanpasu/src/components/ui/progress.tsx new file mode 100644 index 0000000000..7e57a5761d --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/components/ui/progress.tsx @@ -0,0 +1,93 @@ +import { ComponentProps } from 'react' +import { cn } from '@nyanpasu/ui' +import { Circle, CircleSVG } from './circle' + +const HalfCircle = (props: Omit, 'value'>) => { + return +} + +const HalfCircleSVG = ({ + className, + ...props +}: ComponentProps) => { + return +} + +const HalfCircleContainer = ({ + className, + ...props +}: ComponentProps<'div'>) => { + return ( +
+ ) +} + +export function CircularProgress({ + value, + indeterminate, + className, + children, + ...props +}: ComponentProps<'div'> & { + indeterminate?: boolean + value?: number +}) { + return ( +
+ {indeterminate ? ( +
+
+ {/* left */} + + + + + + + {/* right */} + + + + + +
+
+ ) : ( +
+ + + +
+ )} + + {children && ( +
+ {children} +
+ )} +
+ ) +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/components/ui/ripple.tsx b/clash-nyanpasu/frontend/nyanpasu/src/components/ui/ripple.tsx new file mode 100644 index 0000000000..9cf268ffd7 --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/components/ui/ripple.tsx @@ -0,0 +1,83 @@ +import { + AnimatePresence, + clamp, + domAnimation, + LazyMotion, + motion, +} from 'framer-motion' +import { Key, MouseEvent, useCallback, useState } from 'react' + +export type RippleConfig = { + key: Key + x: number + y: number + size: number +} + +export interface RippleProps { + ripples: RippleConfig[] + color?: string + onClear: (key: Key) => void +} + +export const Ripple = ({ ripples, color, onClear }: RippleProps) => { + return ripples.map((ripple) => { + const duration = clamp( + ripple.size > 100 ? 0.6 : 0.4, + 0.01 * ripple.size, + 0.3, + ) + + return ( + + + { + onClear(ripple.key) + }} + /> + + + ) + }) +} + +export const useRipple = () => { + const [ripples, setRipples] = useState([]) + + const onClick = useCallback((e: MouseEvent) => { + const target = e.currentTarget + + const size = Math.max(target.clientWidth, target.clientHeight) + const rect = target.getBoundingClientRect() + + setRipples((prev) => [ + ...prev, + { + key: new Date().getTime(), + size, + x: e.clientX - rect.left - size / 2, + y: e.clientY - rect.top - size / 2, + }, + ]) + }, []) + + const onClear = useCallback((key: Key) => { + setRipples((prev) => prev.filter((ripple) => ripple.key !== key)) + }, []) + + return { ripples, onClick, onClear } +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/components/ui/scroll-area.tsx b/clash-nyanpasu/frontend/nyanpasu/src/components/ui/scroll-area.tsx new file mode 100644 index 0000000000..5ccee5d78e --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/components/ui/scroll-area.tsx @@ -0,0 +1,71 @@ +import * as React from 'react' +import { cn } from '@nyanpasu/ui' +import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area' + +export function Viewport({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + {children} + + ) +} + +export const Corner = ScrollAreaPrimitive.Corner + +export const Root = ScrollAreaPrimitive.Root + +export function ScrollArea({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + {children} + + + + ) +} + +export function ScrollBar({ + className, + orientation = 'vertical', + ...props +}: React.ComponentProps) { + return ( + + + + ) +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/consts.ts b/clash-nyanpasu/frontend/nyanpasu/src/consts.ts index 70fc80d456..87a68b9962 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/consts.ts +++ b/clash-nyanpasu/frontend/nyanpasu/src/consts.ts @@ -5,4 +5,10 @@ import { getSystem } from '@nyanpasu/ui' export const OS = getSystem() +export const isWindows = OS === 'windows' + +export const isMacOS = OS === 'macos' + +export const isLinux = OS === 'linux' + export const IS_NIGHTLY = window.__IS_NIGHTLY__ === true diff --git a/clash-nyanpasu/frontend/nyanpasu/src/pages/(experimental)/_modules/header-menu.tsx b/clash-nyanpasu/frontend/nyanpasu/src/pages/(experimental)/_modules/header-menu.tsx new file mode 100644 index 0000000000..cb6a7d56f2 --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/pages/(experimental)/_modules/header-menu.tsx @@ -0,0 +1,34 @@ +import { ComponentProps } from 'react' +import { Button, ButtonProps } from '@/components/ui/button' +import { cn } from '@nyanpasu/ui' + +const MenuButton = ({ className, ...props }: ButtonProps) => { + return ( +